From 777ad8977b5f79f27c865ae788318a88a14ec296 Mon Sep 17 00:00:00 2001
From: Bryan White <bryanchriswhite@gmail.com>
Date: Thu, 16 Nov 2023 11:31:11 +0100
Subject: [PATCH] [E2E relay test] Update to #178 (#184)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor: `MapFn`s receive context arg

* chore: add `ForEach` map shorthand operator

* chore: add `/pkg/observable/filter`

* chore: add `/pkg/observable/logging`

* chore: add `/pkg/relayer/protocol`

* chore: add `Miner` interface

* feat: add `Miner` implementation

* test: `Miner` implementation

* chore: fix comment

* chore: add godoc comments

* feat: Add Relayer struct

* chore: Rename to RelayMiner

* chore: Rename relay miner file

* chore: Remove unused
RelayerOption parameter

* [Test] First step for automated E2E Relay test (#167)

- Fixed helpers for localnet regenesis
- Added an application & supplier to the genesis file
- Initializing appMap & supplierMap in E2E tests
- Add support for the app's codec (for unmarshaling responses) in E2E tests
- Adding a placeholder for `e2e/tests/relay.feature`

---

Co-authored-by: harry <53987565+h5law@users.noreply.github.com>

* [Relayer] refactor: simplify `RelayerSessionsManager`  (#169)

* refactor: `MapFn`s receive context arg

* feat: add `MapExpand` observable operator

* refactor: `RelayerSessionsManager` to be more reactive

* chore: add godoc comment

* chore: review feedback improvements

* trigger CI

* chore: review feedback improvements

Co-authored-by: Daniel Olshansky <olshansky.daniel@gmail.com>

* chore: review feedback improvements

* chore: update start mining comment

* fix: Update Miner interface

* fix: import cycle & goimports

* chore: review feedback improvements

* chore: cleanup TODO_THIS_COMMIT comments

* chore: improve var & func names for clarity and consistency

* refactor: move claim/proof lifecycle concerns to `relayerSessionsManager`.

* chore: review feedback improvements

* chore: review feedback improvements

* refactor: `miner#hash()` method

* chore: tidy up

* chore: simplify

* wip: relayer CLI

* chore: finish first pass

* chore: review feedback improvements

Co-authored-by: Daniel Olshansky <olshansky.daniel@gmail.com>

* chore: review feedback improvements

Co-authored-by: Daniel Olshansky <olshansky.daniel@gmail.com>

* chore: review feedback improvements

Co-authored-by: Daniel Olshansky <olshansky.daniel@gmail.com>

* chore: review feedback improvements

* chore: review feedback improvements

* chore: tidy up cmd creation

* fix: incomplete refactor

* chore: simplify

* chore: add log lines

* wip: react to miner, refactor, construct miner, refactor

* chore: cleanup

* chore: Reflect responsibility changes of session manager

* feat: Use relay miner to start

* [WIP] Updating relay.feature to run curl command

* chore: Improve comment about startig relayer proxy

* wip: debugging

* Continued implementation but still failing

* Getting an invalid request right now but figuring it out...

* wip: debugging

* Added service and switched to AppGate

* wip: debugging

* chore: Rename falg variables

* wip: debugging

* revertme: disable tilt relayer service

* chore: use arg not flag

* chore: rename command

* Debugging checkpoint

* wip: debugging - improvments

* wip: debugging

* wip: debugging

* wip: debugging

* revert-or-fixme: add error log lines

* revert-or-fixme: add debug log lines

* fix: set relay server handle function

* revert-or-fixme: add debug log lines

* chore: rename some chan vars

* feat: fix all bugs, e2e relay works

* chore: add some todo comments

* wip: debugging

* fix: use remote helm charts again

* fix: put adequate proxied services endpoitns, prevent session republishing

* chore: Refactor JSONRPCServer and server builder

* Upate a couple small comments in the maketfile

* revert: comment relayers out of tiltfile

* chore: fix subcmd name `relayerminer` -> `relayminer`

* chore: improve logging

* chore: cleanup error messaging & logging in appgate server

* refactor: rename misnamed `jsonRPCServer` receiver var

* chore: remove appgate server debug log

* chore: unexport `relayMiner` struct

* refactor: interrupt signal handling

* chore: improve comments

* chore: improve comments

* revert: tiltfile hot-reload dirs

* refactor: re-consolidate client contexts

* fix: typo

* chore: remove todo

* chore: add todo comment

* revert: comment change

* fix: error format strings

* chore: remove comment

* fix: error format strings

* chore: add `-features-path` flag to cucumber tests

* fix: set the relayminer URL in the curl cmd

* chore: remove redundant `-X` curl arg (says curl)

* squash: fix relayminer url: reword: s/relayminer/appgateserver/

* chore: improve error messaging

* fix: curl invocation

* test: implement step definition to assert agains relay response

* chore: improve error name & messaging

* Self review

* fixup: merge upstream

* chore: review feedback improvements

* chore: update anvil service port in make targets

* chore: review feedback improvements

Co-authored-by: Daniel Olshansky <olshansky.daniel@gmail.com>

* refactor: relayminer depinject helpers & godoc comments on all constructors

* refactor: separate tx and query client contexts 🙄

* fix: sessiontree store path check

* fix: sessiontree store path check

* chore: review feedback improvements

Co-authored-by: Daniel Olshansky <olshansky.daniel@gmail.com>

* chore: review feedback improvements

* chore: add long command description

* fix: supplier client test

* chore: cleanup flags and dependencies for appgateserver cmd

* chore: move shared dependency setup logic to shared pkg

* chore: update comment

* Update .gitignore

* Update OpenAPI spec

* Updated comments for post 177+179 work for okdas

* Update pkg/relayer/cmd/cmd.go

* Update the names and references to queryNode/sequencerNode/fullNode etc

* Update some comments and TODOs

* Added a couple more comments

* More tiny comment updates

* chore: review feedback improvements

Co-authored-by: Daniel Olshansky <olshansky.daniel@gmail.com>

---------

Co-authored-by: Redouane Lakrache <r3d0ne@gmail.com>
Co-authored-by: Daniel Olshansky <olshansky.daniel@gmail.com>
Co-authored-by: harry <53987565+h5law@users.noreply.github.com>
---
 e2e/tests/init_test.go              | 40 ++++++++++++++++++++++++-----
 e2e/tests/node.go                   |  4 ++-
 pkg/appgateserver/errors.go         |  5 ++--
 pkg/appgateserver/relay_verifier.go | 11 +++++++-
 pkg/relayer/proxy/relay_verifier.go |  2 +-
 5 files changed, 51 insertions(+), 11 deletions(-)

diff --git a/e2e/tests/init_test.go b/e2e/tests/init_test.go
index 454238264..c7492903b 100644
--- a/e2e/tests/init_test.go
+++ b/e2e/tests/init_test.go
@@ -3,7 +3,9 @@
 package e2e
 
 import (
+	"flag"
 	"fmt"
+	"log"
 	"regexp"
 	"strconv"
 	"strings"
@@ -22,6 +24,12 @@ import (
 	suppliertypes "github.com/pokt-network/poktroll/x/supplier/types"
 )
 
+// TODO_TECHDEBT(@okdas): Once relayminer and appgateserver are running in tilt,
+// use their respective in-tilt hostnames and run E2E tests in tilt. This
+// should match the on-chain advertised endpoint for the service with the
+// given serviceId.
+const localnetAppGateServerUrl = "http://localhost:42069"
+
 var (
 	addrRe   *regexp.Regexp
 	amountRe *regexp.Regexp
@@ -31,12 +39,21 @@ var (
 	accNameToAppMap      = make(map[string]apptypes.Application)
 	accNameToSupplierMap = make(map[string]sharedtypes.Supplier)
 
-	keyRingFlag = "--keyring-backend=test"
+	flagFeaturesPath string
+	keyRingFlag      = "--keyring-backend=test"
 )
 
 func init() {
 	addrRe = regexp.MustCompile(`address:\s+(\S+)\s+name:\s+(\S+)`)
 	amountRe = regexp.MustCompile(`amount:\s+"(.+?)"\s+denom:\s+upokt`)
+
+	flag.StringVar(&flagFeaturesPath, "features-path", "*.feature", "Specifies glob paths for the runner to look up .feature files")
+}
+
+func TestMain(m *testing.M) {
+	flag.Parse()
+	log.Printf("features path: %s", flagFeaturesPath)
+	m.Run()
 }
 
 type suite struct {
@@ -58,7 +75,7 @@ func (s *suite) Before() {
 // TestFeatures runs the e2e tests specified in any .features files in this directory
 // * This test suite assumes that a LocalNet is running
 func TestFeatures(t *testing.T) {
-	gocuke.NewRunner(t, &suite{}).Path("*.feature").Run()
+	gocuke.NewRunner(t, &suite{}).Path(flagFeaturesPath).Run()
 }
 
 func (s *suite) TheUserHasThePocketdBinaryInstalled() {
@@ -243,16 +260,21 @@ func (s *suite) TheSessionForApplicationAndServiceContainsTheSupplier(appName st
 }
 
 func (s *suite) TheApplicationSendsTheSupplierARequestForServiceWithData(appName, supplierName, serviceId, requestData string) {
-	res, err := s.pocketd.RunCurl("", serviceId, requestData)
+	res, err := s.pocketd.RunCurl(localnetAppGateServerUrl, serviceId, requestData)
 	if err != nil {
 		s.Fatalf("error sending relay request from app %s to supplier %s for service %s: %v", appName, supplierName, serviceId, err)
 	}
-	// TODO(#184): store & use the result of res
-	fmt.Println(res)
+
+	relayKey := relayReferenceKey(appName, supplierName)
+	s.scenarioState[relayKey] = res.Stdout
 }
 
 func (s *suite) TheApplicationReceivesASuccessfulRelayResponseSignedBy(appName string, supplierName string) {
-	// TODO(#126, @Olshansk): Implement this step
+	relayKey := relayReferenceKey(appName, supplierName)
+	stdout, ok := s.scenarioState[relayKey]
+
+	require.Truef(s, ok, "no relay response found for %s", relayKey)
+	require.Contains(s, stdout, `"result":"0x`)
 }
 
 func (s *suite) getStakedAmount(actorType, accName string) (bool, int) {
@@ -363,3 +385,9 @@ func (s *suite) getAccBalance(accName string) int {
 	require.NoError(s, err)
 	return found
 }
+
+// TODO_IMPROVE: use `sessionId` and `supplierName` since those are the two values
+// used to create the primary composite key on-chain to uniquely distinguish relays.
+func relayReferenceKey(appName, supplierName string) string {
+	return fmt.Sprintf("%s/%s", appName, supplierName)
+}
diff --git a/e2e/tests/node.go b/e2e/tests/node.go
index 533f347d7..440313431 100644
--- a/e2e/tests/node.go
+++ b/e2e/tests/node.go
@@ -115,7 +115,9 @@ func (p *pocketdBin) runPocketCmd(args ...string) (*commandResult, error) {
 
 // runCurlPostCmd is a helper to run a command using the local pocketd binary with the flags provided
 func (p *pocketdBin) runCurlPostCmd(rpcUrl string, service string, data string, args ...string) (*commandResult, error) {
-	base := []string{"-v", "-X", "POST", "-H", "'Content-Type: application/json'", "--data", fmt.Sprintf("'%s'", data), fmt.Sprintf("%s/%s", rpcUrl, service)}
+	dataStr := fmt.Sprintf("%s", data)
+	urlStr := fmt.Sprintf("%s/%s", rpcUrl, service)
+	base := []string{"-v", "POST", "-H", "Content-Type: application/json", "--data", dataStr, urlStr}
 	args = append(base, args...)
 	commandStr := "curl " + strings.Join(args, " ") // Create a string representation of the command
 	cmd := exec.Command("curl", args...)
diff --git a/pkg/appgateserver/errors.go b/pkg/appgateserver/errors.go
index eecf99f44..c07a7843f 100644
--- a/pkg/appgateserver/errors.go
+++ b/pkg/appgateserver/errors.go
@@ -10,6 +10,7 @@ var (
 	ErrAppGateMissingAppAddress             = sdkerrors.Register(codespace, 4, "missing application address")
 	ErrAppGateMissingSigningInformation     = sdkerrors.Register(codespace, 5, "missing app client signing information")
 	ErrAppGateMissingListeningEndpoint      = sdkerrors.Register(codespace, 6, "missing app client listening endpoint")
-	ErrAppGateEmptyRelayResponse            = sdkerrors.Register(codespace, 7, "empty relay response")
-	ErrAppGateHandleRelay                   = sdkerrors.Register(codespace, 8, "internal error handling relay request")
+	ErrAppGateEmptyRelayResponseMeta        = sdkerrors.Register(codespace, 7, "empty relay response metadata")
+	ErrAppGateEmptyRelayResponseSignature   = sdkerrors.Register(codespace, 8, "empty relay response signature")
+	ErrAppGateHandleRelay                   = sdkerrors.Register(codespace, 9, "internal error handling relay request")
 )
diff --git a/pkg/appgateserver/relay_verifier.go b/pkg/appgateserver/relay_verifier.go
index 4f86e1cdd..68e4cc418 100644
--- a/pkg/appgateserver/relay_verifier.go
+++ b/pkg/appgateserver/relay_verifier.go
@@ -26,7 +26,16 @@ func (app *appGateServer) verifyResponse(
 
 	// Extract the supplier's signature
 	if relayResponse.Meta == nil {
-		return ErrAppGateEmptyRelayResponse
+		payload := relayResponse.GetPayload()
+		payloadBz := make([]byte, payload.Size())
+		if _, err := payload.MarshalTo(payloadBz); err != nil {
+			return ErrAppGateEmptyRelayResponseMeta.Wrapf(
+				"unable to marshal relay response payload: %s", err,
+			)
+		}
+		return ErrAppGateEmptyRelayResponseSignature.Wrapf(
+			"response payload: %s", relayResponse.Payload,
+		)
 	}
 	supplierSignature := relayResponse.Meta.SupplierSignature
 
diff --git a/pkg/relayer/proxy/relay_verifier.go b/pkg/relayer/proxy/relay_verifier.go
index ba8bf7e32..38da8c911 100644
--- a/pkg/relayer/proxy/relay_verifier.go
+++ b/pkg/relayer/proxy/relay_verifier.go
@@ -97,7 +97,7 @@ func (rp *relayerProxy) VerifyRelayRequest(
 	// matches the relayRequest sessionId.
 	// TODO_INVESTIGATE: Revisit the assumptions above at some point in the future, but good enough for now.
 	if session.SessionId != relayRequest.Meta.SessionHeader.SessionId {
-		return ErrRelayerProxyInvalidSession
+		return ErrRelayerProxyInvalidSession.Wrapf("%+v", session)
 	}
 
 	// Check if the relayRequest is allowed to be served by the relayer proxy.