From bfbaa6bd054570d898711d88e13d03c0b1dd98e6 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 16 Nov 2023 11:31:11 +0100 Subject: [PATCH 1/2] [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 * 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 * chore: review feedback improvements Co-authored-by: Daniel Olshansky * chore: review feedback improvements Co-authored-by: Daniel Olshansky * 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 * 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 * 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 --------- Co-authored-by: Redouane Lakrache Co-authored-by: Daniel Olshansky 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. From dd3434115ee126f3b432644e185e41031cfbc2d3 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Thu, 16 Nov 2023 17:44:04 +0100 Subject: [PATCH 2/2] [Relayminer] chore: cleanup after 177 (#190) * chore: fix godoc comments * chore: remove unused code --- pkg/deps/config/config.go | 13 ++++++------- pkg/relayer/cmd/cmd.go | 38 -------------------------------------- 2 files changed, 6 insertions(+), 45 deletions(-) diff --git a/pkg/deps/config/config.go b/pkg/deps/config/config.go index 9621fa1de..8a73cd40c 100644 --- a/pkg/deps/config/config.go +++ b/pkg/deps/config/config.go @@ -36,9 +36,9 @@ func SupplyConfig( return deps, nil } -// NewSupplyEventsQueryClientFn constructs an EventsQueryClient instance and returns -// a new depinject.Config which is supplied with the given deps and the new -// EventsQueryClient. +// NewSupplyEventsQueryClientFn returns a new function which constructs an +// EventsQueryClient instance and returns a new depinject.Config which is supplied +// with the given deps and the new EventsQueryClient. func NewSupplyEventsQueryClientFn( pocketNodeWebsocketUrl string, ) SupplierFn { @@ -53,10 +53,9 @@ func NewSupplyEventsQueryClientFn( } } -// NewSupplyBlockClientFn returns a function with constructs a BlockClient instance -// with the given nodeURL and returns a new -// depinject.Config which is supplied with the given deps and the new -// BlockClient. +// NewSupplyBlockClientFn returns a function which constructs a BlockClient instance +// with the given nodeURL and returns a new depinject.Config which is supplied with +// the given deps and the new BlockClient. func NewSupplyBlockClientFn(pocketNodeWebsocketUrl string) SupplierFn { return func( ctx context.Context, diff --git a/pkg/relayer/cmd/cmd.go b/pkg/relayer/cmd/cmd.go index 4f43c4ff2..36bb1f371 100644 --- a/pkg/relayer/cmd/cmd.go +++ b/pkg/relayer/cmd/cmd.go @@ -13,8 +13,6 @@ import ( "github.com/spf13/cobra" "github.com/pokt-network/poktroll/cmd/signals" - "github.com/pokt-network/poktroll/pkg/client/block" - eventsquery "github.com/pokt-network/poktroll/pkg/client/events_query" "github.com/pokt-network/poktroll/pkg/client/supplier" "github.com/pokt-network/poktroll/pkg/client/tx" "github.com/pokt-network/poktroll/pkg/deps/config" @@ -148,42 +146,6 @@ func getPocketNodeWebsocketUrl() (string, error) { return fmt.Sprintf("ws://%s/websocket", pocketNodeURL.Host), nil } -// newSupplyEventsQueryClientFn constructs an EventsQueryClient instance and returns -// a new depinject.Config which is supplied with the given deps and the new -// EventsQueryClient. -func newSupplyEventsQueryClientFn( - pocketNodeWebsocketUrl string, -) config.SupplierFn { - return func( - _ context.Context, - deps depinject.Config, - _ *cobra.Command, - ) (depinject.Config, error) { - eventsQueryClient := eventsquery.NewEventsQueryClient(pocketNodeWebsocketUrl) - - return depinject.Configs(deps, depinject.Supply(eventsQueryClient)), nil - } -} - -// newSupplyBlockClientFn returns a function with constructs a BlockClient instance -// with the given nodeURL and returns a new -// depinject.Config which is supplied with the given deps and the new -// BlockClient. -func newSupplyBlockClientFn(pocketNodeWebsocketUrl string) config.SupplierFn { - return func( - ctx context.Context, - deps depinject.Config, - _ *cobra.Command, - ) (depinject.Config, error) { - blockClient, err := block.NewBlockClient(ctx, deps, pocketNodeWebsocketUrl) - if err != nil { - return nil, err - } - - return depinject.Configs(deps, depinject.Supply(blockClient)), nil - } -} - // supplyMiner constructs a Miner instance and returns a new depinject.Config // which is supplied with the given deps and the new Miner. func supplyMiner(