From 38ad7f528980684cd88505470b78c37350011cf9 Mon Sep 17 00:00:00 2001 From: Darioush Jalali Date: Fri, 10 Feb 2023 09:54:50 -0800 Subject: [PATCH] Get signature endpoint: alternative PR with different packaging (#507) * base warp backend * add signature caching * add docs * error handling * pr fixes * basic signature request * hash unsigned message for key * implement new Request and RequestHandler interfaces * signature handler impl without constructing one * fix import * quick pr fixes and merge * quick pr fixes and merge * save signature instead of whole msg * use avaGO cache * rename warpBackend and docs * fix nits * Update plugin/evm/warp_backend.go Co-authored-by: aaronbuchwald * Update plugin/evm/warp_backend.go Co-authored-by: aaronbuchwald * fix pr nits * pr fixes and testing * type check for caching * handlers and request before tests * fix imports * signature handler with stats and test * use memdb and remove extra test * remove unused * fix imports * fix imports * nit * update license year * use require noError * saving message in db and pr fixes * create noop signature handler and refactor code handler * get signature endpoint * add api arg to evm client * Update sync/handlers/handler.go Co-authored-by: aaronbuchwald * update backend return value * refactor handlers to network handler * change constructor of handler stats * pr cleanups * warp api * initialize warp backend * build fix * wip * warp api follows eth api pattern * cleanup and comments * clean up response * fix warp client return type * nits for get-signature-endpoint (#502) Co-authored-by: Darioush Jalali Co-authored-by: aaronbuchwald Co-authored-by: Ceyhun Onur Co-authored-by: cam-schultz <78878559+cam-schultz@users.noreply.github.com> * resolve merge conflict * warp: Group packages for symmetry w/ sync * more reshuffle * more shuffle * pr comments * fix * update to []byte * update svc return type * rename arg * fix type * add stats pkg --------- Co-authored-by: Matthew Lam Co-authored-by: aaronbuchwald Co-authored-by: cam-schultz Co-authored-by: Ceyhun Onur Co-authored-by: cam-schultz <78878559+cam-schultz@users.noreply.github.com> --- handlers/stats/mock_stats.go | 22 --------- handlers/stats/stats.go | 38 ---------------- plugin/evm/config.go | 1 + .../evm/network_handler.go | 21 +++++---- plugin/evm/vm.go | 31 ++++++++++--- {plugin/evm/warp => warp}/backend.go | 0 {plugin/evm/warp => warp}/backend_test.go | 0 .../handlers}/signature_request.go | 6 +-- .../handlers}/signature_request_test.go | 14 +++--- .../warp => warp/handlers}/stats/stats.go | 0 warp/warp_client.go | 45 +++++++++++++++++++ warp/warp_service.go | 26 +++++++++++ 12 files changed, 118 insertions(+), 86 deletions(-) delete mode 100644 handlers/stats/mock_stats.go delete mode 100644 handlers/stats/stats.go rename handlers/handler.go => plugin/evm/network_handler.go (81%) rename {plugin/evm/warp => warp}/backend.go (100%) rename {plugin/evm/warp => warp}/backend_test.go (100%) rename {handlers/warp => warp/handlers}/signature_request.go (95%) rename {handlers/warp => warp/handlers}/signature_request_test.go (88%) rename {handlers/warp => warp/handlers}/stats/stats.go (100%) create mode 100644 warp/warp_client.go create mode 100644 warp/warp_service.go diff --git a/handlers/stats/mock_stats.go b/handlers/stats/mock_stats.go deleted file mode 100644 index 45ffb7aaa2..0000000000 --- a/handlers/stats/mock_stats.go +++ /dev/null @@ -1,22 +0,0 @@ -// (c) 2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package stats - -import ( - warpStats "github.com/ava-labs/subnet-evm/handlers/warp/stats" - syncStats "github.com/ava-labs/subnet-evm/sync/handlers/stats" -) - -var _ HandlerStats = &MockHandlerStats{} - -// MockHandlerStats is mock for capturing and asserting on handler metrics in test -type MockHandlerStats struct { - syncStats.MockHandlerStats - warpStats.MockSignatureRequestHandlerStats -} - -func (m *MockHandlerStats) Reset() { - m.MockHandlerStats.Reset() - m.MockSignatureRequestHandlerStats.Reset() -} diff --git a/handlers/stats/stats.go b/handlers/stats/stats.go deleted file mode 100644 index 357eb18f0b..0000000000 --- a/handlers/stats/stats.go +++ /dev/null @@ -1,38 +0,0 @@ -// (c) 2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package stats - -import ( - warpStats "github.com/ava-labs/subnet-evm/handlers/warp/stats" - syncStats "github.com/ava-labs/subnet-evm/sync/handlers/stats" -) - -var ( - _ HandlerStats = &handlerStats{} - _ HandlerStats = &MockHandlerStats{} -) - -// HandlerStats reports prometheus metrics for the network handlers -type HandlerStats interface { - warpStats.SignatureRequestHandlerStats - syncStats.HandlerStats -} - -type handlerStats struct { - // State sync handler metrics - syncStats.HandlerStats - - // Warp handler metrics - warpStats.SignatureRequestHandlerStats -} - -func NewHandlerStats(enabled bool) HandlerStats { - if !enabled { - return &MockHandlerStats{} - } - return &handlerStats{ - HandlerStats: syncStats.NewHandlerStats(enabled), - SignatureRequestHandlerStats: warpStats.NewStats(enabled), - } -} diff --git a/plugin/evm/config.go b/plugin/evm/config.go index fcb0fa297c..6abaed1093 100644 --- a/plugin/evm/config.go +++ b/plugin/evm/config.go @@ -84,6 +84,7 @@ type Config struct { // Subnet EVM APIs SnowmanAPIEnabled bool `json:"snowman-api-enabled"` + WarpAPIEnabled bool `json:"warp-api-enabled"` AdminAPIEnabled bool `json:"admin-api-enabled"` AdminAPIDir string `json:"admin-api-dir"` diff --git a/handlers/handler.go b/plugin/evm/network_handler.go similarity index 81% rename from handlers/handler.go rename to plugin/evm/network_handler.go index 0ab0a626fb..529249fbc1 100644 --- a/handlers/handler.go +++ b/plugin/evm/network_handler.go @@ -1,20 +1,19 @@ // (c) 2023, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package handlers +package evm import ( "context" "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/subnet-evm/handlers/stats" - warpHandlers "github.com/ava-labs/subnet-evm/handlers/warp" "github.com/ava-labs/subnet-evm/metrics" "github.com/ava-labs/subnet-evm/plugin/evm/message" - "github.com/ava-labs/subnet-evm/sync/handlers" syncHandlers "github.com/ava-labs/subnet-evm/sync/handlers" + syncStats "github.com/ava-labs/subnet-evm/sync/handlers/stats" "github.com/ava-labs/subnet-evm/trie" + warpHandlers "github.com/ava-labs/subnet-evm/warp/handlers" ) var _ message.RequestHandler = &networkHandler{} @@ -26,18 +25,18 @@ type networkHandler struct { signatureRequestHandler warpHandlers.SignatureRequestHandler } -// NewNetworkHandler constructs the handler for serving network requests. -func NewNetworkHandler( - provider handlers.SyncDataProvider, +// newNetworkHandler constructs the handler for serving network requests. +func newNetworkHandler( + provider syncHandlers.SyncDataProvider, evmTrieDB *trie.Database, networkCodec codec.Manager, ) message.RequestHandler { - handlerStats := stats.NewHandlerStats(metrics.Enabled) + syncStats := syncStats.NewHandlerStats(metrics.Enabled) return &networkHandler{ // State sync handlers - stateTrieLeafsRequestHandler: syncHandlers.NewLeafsRequestHandler(evmTrieDB, provider, networkCodec, handlerStats), - blockRequestHandler: syncHandlers.NewBlockRequestHandler(provider, networkCodec, handlerStats), - codeRequestHandler: syncHandlers.NewCodeRequestHandler(evmTrieDB.DiskDB(), networkCodec, handlerStats), + stateTrieLeafsRequestHandler: syncHandlers.NewLeafsRequestHandler(evmTrieDB, provider, networkCodec, syncStats), + blockRequestHandler: syncHandlers.NewBlockRequestHandler(provider, networkCodec, syncStats), + codeRequestHandler: syncHandlers.NewCodeRequestHandler(evmTrieDB.DiskDB(), networkCodec, syncStats), // TODO: initialize actual signature request handler when warp is ready signatureRequestHandler: &warpHandlers.NoopSignatureRequestHandler{}, diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 3d61c9bb64..92a9f05dfd 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -15,7 +15,6 @@ import ( "time" avalanchegoMetrics "github.com/ava-labs/avalanchego/api/metrics" - "github.com/ava-labs/subnet-evm/handlers" "github.com/prometheus/client_golang/prometheus" "github.com/ava-labs/subnet-evm/commontype" @@ -36,6 +35,7 @@ import ( statesyncclient "github.com/ava-labs/subnet-evm/sync/client" "github.com/ava-labs/subnet-evm/sync/client/stats" "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/warp" // Force-load tracer engine to trigger registration // @@ -81,9 +81,10 @@ const ( // and fail verification maxFutureBlockTime = 10 * time.Second - decidedCacheSize = 100 - missingCacheSize = 50 - unverifiedCacheSize = 50 + decidedCacheSize = 100 + missingCacheSize = 50 + unverifiedCacheSize = 50 + warpSignatureCacheSize = 500 // Prefixes for metrics gatherers ethMetricsPrefix = "eth" @@ -102,6 +103,7 @@ var ( lastAcceptedKey = []byte("last_accepted_key") acceptedPrefix = []byte("snowman_accepted") metadataPrefix = []byte("metadata") + warpPrefix = []byte("warp") ethDBPrefix = []byte("ethdb") ) @@ -177,6 +179,10 @@ type VM struct { // block. acceptedBlockDB database.Database + // [warpDB] is used to store warp message signatures + // set to a prefixDB with the prefix [warpPrefix] + warpDB database.Database + toEngine chan<- commonEng.Message syntacticBlockValidator BlockValidator @@ -206,6 +212,10 @@ type VM struct { // State sync server and client StateSyncServer StateSyncClient + + // Avalanche Warp Messaging backend + // Used to serve BLS signatures of warp messages over RPC + warpBackend warp.WarpBackend } /* @@ -274,6 +284,7 @@ func (vm *VM) Initialize( vm.db = versiondb.New(baseDB) vm.acceptedBlockDB = prefixdb.New(acceptedPrefix, vm.db) vm.metadataDB = prefixdb.New(metadataPrefix, vm.db) + vm.warpDB = prefixdb.New(warpPrefix, vm.db) if vm.config.InspectDatabase { start := time.Now() @@ -415,6 +426,9 @@ func (vm *VM) Initialize( vm.Network = peer.NewNetwork(appSender, vm.networkCodec, message.CrossChainCodec, chainCtx.NodeID, vm.config.MaxOutboundActiveRequests, vm.config.MaxOutboundActiveCrossChainRequests) vm.client = peer.NewNetworkClient(vm.Network) + // initialize warp backend + vm.warpBackend = warp.NewWarpBackend(vm.ctx, vm.warpDB, warpSignatureCacheSize) + if err := vm.initializeChain(lastAcceptedHash, vm.ethConfig); err != nil { return err } @@ -606,7 +620,7 @@ func (vm *VM) setAppRequestHandlers() { }, ) - networkHandler := handlers.NewNetworkHandler(vm.blockChain, evmTrieDB, vm.networkCodec) + networkHandler := newNetworkHandler(vm.blockChain, evmTrieDB, vm.networkCodec) vm.Network.SetRequestHandler(networkHandler) } @@ -795,6 +809,13 @@ func (vm *VM) CreateHandlers(context.Context) (map[string]*commonEng.HTTPHandler enabledAPIs = append(enabledAPIs, "snowman") } + if vm.config.WarpAPIEnabled { + if err := handler.RegisterName("warp", &warp.WarpAPI{Backend: vm.warpBackend}); err != nil { + return nil, err + } + enabledAPIs = append(enabledAPIs, "warp") + } + log.Info(fmt.Sprintf("Enabled APIs: %s", strings.Join(enabledAPIs, ", "))) apis[ethRPCEndpoint] = &commonEng.HTTPHandler{ LockOptions: commonEng.NoLock, diff --git a/plugin/evm/warp/backend.go b/warp/backend.go similarity index 100% rename from plugin/evm/warp/backend.go rename to warp/backend.go diff --git a/plugin/evm/warp/backend_test.go b/warp/backend_test.go similarity index 100% rename from plugin/evm/warp/backend_test.go rename to warp/backend_test.go diff --git a/handlers/warp/signature_request.go b/warp/handlers/signature_request.go similarity index 95% rename from handlers/warp/signature_request.go rename to warp/handlers/signature_request.go index 13b6c7e66b..6cf28efd74 100644 --- a/handlers/warp/signature_request.go +++ b/warp/handlers/signature_request.go @@ -1,7 +1,7 @@ // (c) 2023, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package warp +package handlers import ( "context" @@ -9,9 +9,9 @@ import ( "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/subnet-evm/handlers/warp/stats" "github.com/ava-labs/subnet-evm/plugin/evm/message" - "github.com/ava-labs/subnet-evm/plugin/evm/warp" + "github.com/ava-labs/subnet-evm/warp" + "github.com/ava-labs/subnet-evm/warp/handlers/stats" "github.com/ethereum/go-ethereum/log" ) diff --git a/handlers/warp/signature_request_test.go b/warp/handlers/signature_request_test.go similarity index 88% rename from handlers/warp/signature_request_test.go rename to warp/handlers/signature_request_test.go index e2b435c509..fa2d53664d 100644 --- a/handlers/warp/signature_request_test.go +++ b/warp/handlers/signature_request_test.go @@ -1,7 +1,7 @@ // (c) 2023, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package warp +package handlers import ( "context" @@ -14,9 +14,9 @@ import ( "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/hashing" "github.com/ava-labs/avalanchego/vms/platformvm/teleporter" - "github.com/ava-labs/subnet-evm/handlers/stats" "github.com/ava-labs/subnet-evm/plugin/evm/message" - "github.com/ava-labs/subnet-evm/plugin/evm/warp" + "github.com/ava-labs/subnet-evm/warp" + "github.com/ava-labs/subnet-evm/warp/handlers/stats" "github.com/stretchr/testify/require" ) @@ -38,12 +38,12 @@ func TestSignatureHandler(t *testing.T) { require.NoError(t, err) unknownMessageID := ids.GenerateTestID() - mockHandlerStats := &stats.MockHandlerStats{} + mockHandlerStats := &stats.MockSignatureRequestHandlerStats{} signatureRequestHandler := NewSignatureRequestHandler(warpBackend, message.Codec, mockHandlerStats) tests := map[string]struct { setup func() (request message.SignatureRequest, expectedResponse []byte) - verifyStats func(t *testing.T, stats *stats.MockHandlerStats) + verifyStats func(t *testing.T, stats *stats.MockSignatureRequestHandlerStats) }{ "normal": { setup: func() (request message.SignatureRequest, expectedResponse []byte) { @@ -51,7 +51,7 @@ func TestSignatureHandler(t *testing.T) { MessageID: messageID, }, signature[:] }, - verifyStats: func(t *testing.T, stats *stats.MockHandlerStats) { + verifyStats: func(t *testing.T, stats *stats.MockSignatureRequestHandlerStats) { require.EqualValues(t, 1, mockHandlerStats.SignatureRequestCount) require.EqualValues(t, 1, mockHandlerStats.SignatureRequestHit) require.EqualValues(t, 0, mockHandlerStats.SignatureRequestMiss) @@ -64,7 +64,7 @@ func TestSignatureHandler(t *testing.T) { MessageID: unknownMessageID, }, nil }, - verifyStats: func(t *testing.T, stats *stats.MockHandlerStats) { + verifyStats: func(t *testing.T, stats *stats.MockSignatureRequestHandlerStats) { require.EqualValues(t, 1, mockHandlerStats.SignatureRequestCount) require.EqualValues(t, 1, mockHandlerStats.SignatureRequestMiss) require.EqualValues(t, 0, mockHandlerStats.SignatureRequestHit) diff --git a/handlers/warp/stats/stats.go b/warp/handlers/stats/stats.go similarity index 100% rename from handlers/warp/stats/stats.go rename to warp/handlers/stats/stats.go diff --git a/warp/warp_client.go b/warp/warp_client.go new file mode 100644 index 0000000000..bfa4638efb --- /dev/null +++ b/warp/warp_client.go @@ -0,0 +1,45 @@ +// (c) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package warp + +import ( + "context" + "fmt" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/subnet-evm/rpc" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ WarpClient = (*warpClient)(nil) + +type WarpClient interface { + GetSignature(ctx context.Context, messageID ids.ID) ([]byte, error) +} + +// warpClient implementation for interacting with EVM [chain] +type warpClient struct { + client *rpc.Client +} + +// NewWarpClient returns a WarpClient for interacting with EVM [chain] +func NewWarpClient(uri, chain string) (WarpClient, error) { + client, err := rpc.Dial(fmt.Sprintf("%s/ext/bc/%s/rpc", uri, chain)) + if err != nil { + return nil, fmt.Errorf("failed to dial client. err: %w", err) + } + return &warpClient{ + client: client, + }, nil +} + +// GetSignature requests the BLS signature associated with a messageID +func (c *warpClient) GetSignature(ctx context.Context, messageID ids.ID) ([]byte, error) { + var res hexutil.Bytes + err := c.client.CallContext(ctx, &res, "warp_getSignature", messageID) + if err != nil { + return nil, fmt.Errorf("call to warp_getSignature failed. err: %w", err) + } + return res, err +} diff --git a/warp/warp_service.go b/warp/warp_service.go new file mode 100644 index 0000000000..3e2a003207 --- /dev/null +++ b/warp/warp_service.go @@ -0,0 +1,26 @@ +// (c) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package warp + +import ( + "context" + "fmt" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// WarpAPI introduces snowman specific functionality to the evm +type WarpAPI struct { + Backend WarpBackend +} + +// GetSignature returns the BLS signature associated with a messageID. +func (api *WarpAPI) GetSignature(ctx context.Context, messageID ids.ID) (hexutil.Bytes, error) { + signature, err := api.Backend.GetSignature(ctx, messageID) + if err != nil { + return nil, fmt.Errorf("failed to get signature for with error %w", err) + } + return signature[:], nil +}