Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate warp backend from Subnet-EVM #387

Merged
merged 7 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions params/avalanche_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ const (

// The base cost to charge per atomic transaction. Added in Apricot Phase 5.
AtomicTxBaseCost uint64 = 10_000

// Warp Parameters
WarpDefaultQuorumNumerator uint64 = 67
WarpQuorumNumeratorMinimum uint64 = 33
WarpQuorumDenominator uint64 = 100
)

// Constants for message sizes
Expand Down
2 changes: 2 additions & 0 deletions plugin/evm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ type Config struct {
AdminAPIDir string `json:"admin-api-dir"`
CorethAdminAPIEnabled bool `json:"coreth-admin-api-enabled"` // Deprecated: use AdminAPIEnabled instead
CorethAdminAPIDir string `json:"coreth-admin-api-dir"` // Deprecated: use AdminAPIDir instead
WarpAPIEnabled bool `json:"warp-api-enabled"`

// EnabledEthAPIs is a list of Ethereum services that should be enabled
// If none is specified, then we use the default list [defaultEnabledAPIs]
Expand Down Expand Up @@ -118,6 +119,7 @@ type Config struct {
AllowMissingTries bool `json:"allow-missing-tries"` // If enabled, warnings preventing an incomplete trie index are suppressed
PopulateMissingTries *uint64 `json:"populate-missing-tries,omitempty"` // Sets the starting point for re-populating missing tries. Disables re-generation if nil.
PopulateMissingTriesParallelism int `json:"populate-missing-tries-parallelism"` // Number of concurrent readers to use when re-populating missing tries on startup.
PruneWarpDB bool `json:"prune-warp-db-enabled"` // Determines if the warpDB should be cleared on startup

// Metric Settings
MetricsExpensiveEnabled bool `json:"metrics-expensive-enabled"` // Debug-level metrics that might impact runtime performance
Expand Down
10 changes: 10 additions & 0 deletions plugin/evm/message/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ type RequestHandler interface {
HandleAtomicTrieLeafsRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, leafsRequest LeafsRequest) ([]byte, error)
HandleBlockRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, request BlockRequest) ([]byte, error)
HandleCodeRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, codeRequest CodeRequest) ([]byte, error)
HandleMessageSignatureRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, signatureRequest MessageSignatureRequest) ([]byte, error)
HandleBlockSignatureRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, signatureRequest BlockSignatureRequest) ([]byte, error)
}

// ResponseHandler handles response for a sent request
Expand Down Expand Up @@ -74,6 +76,14 @@ func (NoopRequestHandler) HandleCodeRequest(ctx context.Context, nodeID ids.Node
return nil, nil
}

func (NoopRequestHandler) HandleMessageSignatureRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, signatureRequest MessageSignatureRequest) ([]byte, error) {
return nil, nil
}

func (NoopRequestHandler) HandleBlockSignatureRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, signatureRequest BlockSignatureRequest) ([]byte, error) {
return nil, nil
}

// CrossChainRequestHandler interface handles incoming requests from another chain
type CrossChainRequestHandler interface {
HandleEthCallRequest(ctx context.Context, requestingchainID ids.ID, requestID uint32, ethCallRequest EthCallRequest) ([]byte, error)
Expand Down
13 changes: 12 additions & 1 deletion plugin/evm/message/leafs_request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,9 @@ type mockHandler struct {
handleStateTrieCalled,
handleAtomicTrieCalled,
handleBlockRequestCalled,
handleCodeRequestCalled bool
handleCodeRequestCalled,
handleMessageSignatureCalled,
handleBlockSignatureCalled bool
}

func (m *mockHandler) HandleStateTrieLeafsRequest(context.Context, ids.NodeID, uint32, LeafsRequest) ([]byte, error) {
Expand All @@ -200,6 +202,15 @@ func (m *mockHandler) HandleCodeRequest(context.Context, ids.NodeID, uint32, Cod
return nil, nil
}

func (m *mockHandler) HandleMessageSignatureRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, signatureRequest MessageSignatureRequest) ([]byte, error) {
m.handleMessageSignatureCalled = true
return nil, nil
}
func (m *mockHandler) HandleBlockSignatureRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, signatureRequest BlockSignatureRequest) ([]byte, error) {
m.handleBlockSignatureCalled = true
return nil, nil
}

func (m *mockHandler) reset() {
m.handleStateTrieCalled = false
m.handleAtomicTrieCalled = false
Expand Down
49 changes: 49 additions & 0 deletions plugin/evm/message/signature_request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package message

import (
"context"
"fmt"

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/crypto/bls"
)

var (
_ Request = MessageSignatureRequest{}
_ Request = BlockSignatureRequest{}
)

// MessageSignatureRequest is used to request a warp message's signature.
type MessageSignatureRequest struct {
MessageID ids.ID `serialize:"true"`
}

func (s MessageSignatureRequest) String() string {
return fmt.Sprintf("MessageSignatureRequest(MessageID=%s)", s.MessageID.String())
}

func (s MessageSignatureRequest) Handle(ctx context.Context, nodeID ids.NodeID, requestID uint32, handler RequestHandler) ([]byte, error) {
return handler.HandleMessageSignatureRequest(ctx, nodeID, requestID, s)
}

// BlockSignatureRequest is used to request a warp message's signature.
type BlockSignatureRequest struct {
BlockID ids.ID `serialize:"true"`
}

func (s BlockSignatureRequest) String() string {
return fmt.Sprintf("BlockSignatureRequest(BlockID=%s)", s.BlockID.String())
}

func (s BlockSignatureRequest) Handle(ctx context.Context, nodeID ids.NodeID, requestID uint32, handler RequestHandler) ([]byte, error) {
return handler.HandleBlockSignatureRequest(ctx, nodeID, requestID, s)
}

// SignatureResponse is the response to a BlockSignatureRequest or MessageSignatureRequest.
// The response contains a BLS signature of the requested message, signed by the responding node's BLS private key.
type SignatureResponse struct {
Signature [bls.SignatureLen]byte `serialize:"true"`
}
73 changes: 73 additions & 0 deletions plugin/evm/message/signature_request_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package message

import (
"encoding/base64"
"encoding/hex"
"testing"

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/stretchr/testify/require"
)

// TestMarshalMessageSignatureRequest asserts that the structure or serialization logic hasn't changed, primarily to
// ensure compatibility with the network.
func TestMarshalMessageSignatureRequest(t *testing.T) {
signatureRequest := MessageSignatureRequest{
MessageID: ids.ID{68, 79, 70, 65, 72, 73, 64, 107},
}

base64MessageSignatureRequest := "AABET0ZBSElAawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
signatureRequestBytes, err := Codec.Marshal(Version, signatureRequest)
require.NoError(t, err)
require.Equal(t, base64MessageSignatureRequest, base64.StdEncoding.EncodeToString(signatureRequestBytes))

var s MessageSignatureRequest
_, err = Codec.Unmarshal(signatureRequestBytes, &s)
require.NoError(t, err)
require.Equal(t, signatureRequest.MessageID, s.MessageID)
}

// TestMarshalBlockSignatureRequest asserts that the structure or serialization logic hasn't changed, primarily to
// ensure compatibility with the network.
func TestMarshalBlockSignatureRequest(t *testing.T) {
signatureRequest := BlockSignatureRequest{
BlockID: ids.ID{68, 79, 70, 65, 72, 73, 64, 107},
}

base64BlockSignatureRequest := "AABET0ZBSElAawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
signatureRequestBytes, err := Codec.Marshal(Version, signatureRequest)
require.NoError(t, err)
require.Equal(t, base64BlockSignatureRequest, base64.StdEncoding.EncodeToString(signatureRequestBytes))

var s BlockSignatureRequest
_, err = Codec.Unmarshal(signatureRequestBytes, &s)
require.NoError(t, err)
require.Equal(t, signatureRequest.BlockID, s.BlockID)
}

// TestMarshalSignatureResponse asserts that the structure or serialization logic hasn't changed, primarily to
// ensure compatibility with the network.
func TestMarshalSignatureResponse(t *testing.T) {
var signature [bls.SignatureLen]byte
sig, err := hex.DecodeString("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
require.NoError(t, err, "failed to decode string to hex")

copy(signature[:], sig)
signatureResponse := SignatureResponse{
Signature: signature,
}

base64SignatureResponse := "AAABI0VniavN7wEjRWeJq83vASNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8="
signatureResponseBytes, err := Codec.Marshal(Version, signatureResponse)
require.NoError(t, err)
require.Equal(t, base64SignatureResponse, base64.StdEncoding.EncodeToString(signatureResponseBytes))

var s SignatureResponse
_, err = Codec.Unmarshal(signatureResponseBytes, &s)
require.NoError(t, err)
require.Equal(t, signatureResponse.Signature, s.Signature)
}
72 changes: 72 additions & 0 deletions plugin/evm/network_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package evm

import (
"context"

"github.com/ava-labs/avalanchego/codec"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/coreth/ethdb"
"github.com/ava-labs/coreth/metrics"
"github.com/ava-labs/coreth/plugin/evm/message"
syncHandlers "github.com/ava-labs/coreth/sync/handlers"
syncStats "github.com/ava-labs/coreth/sync/handlers/stats"
"github.com/ava-labs/coreth/trie"
"github.com/ava-labs/coreth/warp"
warpHandlers "github.com/ava-labs/coreth/warp/handlers"
)

var _ message.RequestHandler = &networkHandler{}

type networkHandler struct {
stateTrieLeafsRequestHandler *syncHandlers.LeafsRequestHandler
atomicTrieLeafsRequestHandler *syncHandlers.LeafsRequestHandler
blockRequestHandler *syncHandlers.BlockRequestHandler
codeRequestHandler *syncHandlers.CodeRequestHandler
signatureRequestHandler *warpHandlers.SignatureRequestHandler
}

// newNetworkHandler constructs the handler for serving network requests.
func newNetworkHandler(
provider syncHandlers.SyncDataProvider,
diskDB ethdb.KeyValueReader,
evmTrieDB *trie.Database,
atomicTrieDB *trie.Database,
warpBackend warp.Backend,
networkCodec codec.Manager,
) message.RequestHandler {
syncStats := syncStats.NewHandlerStats(metrics.Enabled)
return &networkHandler{
stateTrieLeafsRequestHandler: syncHandlers.NewLeafsRequestHandler(evmTrieDB, provider, networkCodec, syncStats),
atomicTrieLeafsRequestHandler: syncHandlers.NewLeafsRequestHandler(atomicTrieDB, nil, networkCodec, syncStats),
blockRequestHandler: syncHandlers.NewBlockRequestHandler(provider, networkCodec, syncStats),
codeRequestHandler: syncHandlers.NewCodeRequestHandler(diskDB, networkCodec, syncStats),
signatureRequestHandler: warpHandlers.NewSignatureRequestHandler(warpBackend, networkCodec),
}
}

func (n networkHandler) HandleStateTrieLeafsRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, leafsRequest message.LeafsRequest) ([]byte, error) {
return n.stateTrieLeafsRequestHandler.OnLeafsRequest(ctx, nodeID, requestID, leafsRequest)
}

func (n networkHandler) HandleAtomicTrieLeafsRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, leafsRequest message.LeafsRequest) ([]byte, error) {
return n.atomicTrieLeafsRequestHandler.OnLeafsRequest(ctx, nodeID, requestID, leafsRequest)
}

func (n networkHandler) HandleBlockRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, blockRequest message.BlockRequest) ([]byte, error) {
return n.blockRequestHandler.OnBlockRequest(ctx, nodeID, requestID, blockRequest)
}

func (n networkHandler) HandleCodeRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, codeRequest message.CodeRequest) ([]byte, error) {
return n.codeRequestHandler.OnCodeRequest(ctx, nodeID, requestID, codeRequest)
}

func (n networkHandler) HandleMessageSignatureRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, messageSignatureRequest message.MessageSignatureRequest) ([]byte, error) {
return n.signatureRequestHandler.OnMessageSignatureRequest(ctx, nodeID, requestID, messageSignatureRequest)
}

func (n networkHandler) HandleBlockSignatureRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, blockSignatureRequest message.BlockSignatureRequest) ([]byte, error) {
return n.signatureRequestHandler.OnBlockSignatureRequest(ctx, nodeID, requestID, blockSignatureRequest)
}
51 changes: 41 additions & 10 deletions plugin/evm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ import (
"github.com/ava-labs/coreth/rpc"
statesyncclient "github.com/ava-labs/coreth/sync/client"
"github.com/ava-labs/coreth/sync/client/stats"
"github.com/ava-labs/coreth/sync/handlers"
handlerstats "github.com/ava-labs/coreth/sync/handlers/stats"
"github.com/ava-labs/coreth/trie"
"github.com/ava-labs/coreth/utils"
"github.com/ava-labs/coreth/warp"
warpValidators "github.com/ava-labs/coreth/warp/validators"

"github.com/prometheus/client_golang/prometheus"
// Force-load tracer engine to trigger registration
Expand Down Expand Up @@ -122,11 +122,12 @@ const (
defaultMempoolSize = 4096
codecVersion = uint16(0)

secpCacheSize = 1024
decidedCacheSize = 10 * units.MiB
missingCacheSize = 50
unverifiedCacheSize = 5 * units.MiB
bytesToIDCacheSize = 5 * units.MiB
secpCacheSize = 1024
decidedCacheSize = 10 * units.MiB
missingCacheSize = 50
unverifiedCacheSize = 5 * units.MiB
bytesToIDCacheSize = 5 * units.MiB
warpSignatureCacheSize = 500

targetAtomicTxsSize = 40 * units.KiB

Expand Down Expand Up @@ -178,6 +179,7 @@ var (
lastAcceptedKey = []byte("last_accepted_key")
acceptedPrefix = []byte("snowman_accepted")
metadataPrefix = []byte("metadata")
warpPrefix = []byte("warp")
ethDBPrefix = []byte("ethdb")

// Prefixes for atomic trie
Expand Down Expand Up @@ -279,6 +281,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
Expand Down Expand Up @@ -328,6 +334,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.Backend
}

// Codec implements the secp256k1fx interface
Expand Down Expand Up @@ -419,6 +429,10 @@ func (vm *VM) Initialize(
vm.db = versiondb.New(db)
vm.acceptedBlockDB = prefixdb.New(acceptedPrefix, vm.db)
vm.metadataDB = prefixdb.New(metadataPrefix, vm.db)
// Note warpDB is not part of versiondb because it is not necessary
// that warp signatures are committed to the database atomically with
// the last accepted block.
vm.warpDB = prefixdb.New(warpPrefix, db)

if vm.config.InspectDatabase {
start := time.Now()
Expand Down Expand Up @@ -558,6 +572,16 @@ func (vm *VM) Initialize(
vm.Network = peer.NewNetwork(vm.router, 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.NewBackend(vm.ctx.NetworkID, vm.ctx.ChainID, vm.ctx.WarpSigner, vm, vm.warpDB, warpSignatureCacheSize)

// clear warpdb on initialization if config enabled
if vm.config.PruneWarpDB {
if err := vm.warpBackend.Clear(); err != nil {
return fmt.Errorf("failed to prune warpDB: %w", err)
}
}

if err := vm.initializeChain(lastAcceptedHash); err != nil {
return err
}
Expand Down Expand Up @@ -1128,15 +1152,15 @@ func (vm *VM) setAppRequestHandlers() {
Cache: vm.config.StateSyncServerTrieCache,
},
)
syncRequestHandler := handlers.NewSyncHandler(
networkHandler := newNetworkHandler(
vm.blockChain,
vm.chaindb,
evmTrieDB,
vm.atomicTrie.TrieDB(),
vm.warpBackend,
vm.networkCodec,
handlerstats.NewHandlerStats(metrics.Enabled),
)
vm.Network.SetRequestHandler(syncRequestHandler)
vm.Network.SetRequestHandler(networkHandler)
}

// setCrossChainAppRequestHandler sets the request handlers for the VM to serve cross chain
Expand Down Expand Up @@ -1333,6 +1357,13 @@ func (vm *VM) CreateHandlers(context.Context) (map[string]http.Handler, error) {
}
enabledAPIs = append(enabledAPIs, "snowman")
}
if vm.config.WarpAPIEnabled {
validatorsState := warpValidators.NewState(vm.ctx)
if err := handler.RegisterName("warp", warp.NewAPI(vm.ctx.NetworkID, vm.ctx.SubnetID, vm.ctx.ChainID, validatorsState, vm.warpBackend, vm.client)); err != nil {
return nil, err
}
enabledAPIs = append(enabledAPIs, "warp")
}

log.Info(fmt.Sprintf("Enabled APIs: %s", strings.Join(enabledAPIs, ", ")))
apis[ethRPCEndpoint] = handler
Expand Down
Loading