Skip to content

Commit

Permalink
Merge branch 'master' into bump-min-go-version
Browse files Browse the repository at this point in the history
  • Loading branch information
ceyonur authored Jan 4, 2024
2 parents 0bf3ed3 + b302fa0 commit 1593c72
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 53 deletions.
7 changes: 7 additions & 0 deletions plugin/evm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/ava-labs/subnet-evm/core/txpool"
"github.com/ava-labs/subnet-evm/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/spf13/cast"
)

Expand Down Expand Up @@ -208,6 +209,12 @@ type Config struct {
// * 0: means no limit
// * N: means N block limit [HEAD-N+1, HEAD] and delete extra indexes
TxLookupLimit uint64 `json:"tx-lookup-limit"`

// WarpOffChainMessages encodes off-chain messages (unrelated to any on-chain event ie. block or AddressedCall)
// that the node should be willing to sign.
// Note: only supports AddressedCall payloads as defined here:
// https://github.com/ava-labs/avalanchego/tree/7623ffd4be915a5185c9ed5e11fa9be15a6e1f00/vms/platformvm/warp/payload#addressedcall
WarpOffChainMessages []hexutil.Bytes `json:"warp-off-chain-messages"`
}

// EthAPIs returns an array of strings representing the Eth APIs that should be enabled
Expand Down
11 changes: 9 additions & 2 deletions plugin/evm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,8 +465,15 @@ func (vm *VM) Initialize(
vm.Network = peer.NewNetwork(p2pNetwork, 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)
// Initialize warp backend
offchainWarpMessages := make([][]byte, len(vm.config.WarpOffChainMessages))
for i, hexMsg := range vm.config.WarpOffChainMessages {
offchainWarpMessages[i] = []byte(hexMsg)
}
vm.warpBackend, err = warp.NewBackend(vm.ctx.NetworkID, vm.ctx.ChainID, vm.ctx.WarpSigner, vm, vm.warpDB, warpSignatureCacheSize, offchainWarpMessages)
if err != nil {
return err
}

// clear warpdb on initialization if config enabled
if vm.config.PruneWarpDB {
Expand Down
14 changes: 8 additions & 6 deletions precompile/precompileconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@ type PredicateContext struct {
}

// Predicater is an optional interface for StatefulPrecompileContracts to implement.
// If implemented, the predicate will be enforced on every transaction in a block, prior to
// the block's execution.
// If VerifyPredicate returns an error, the block will fail verification with no further processing.
// WARNING: If you are implementing a custom precompile, beware that subnet-evm
// will not maintain backwards compatibility of this interface and your code should not
// rely on this. Designed for use only by precompiles that ship with subnet-evm.
// If implemented, the predicate will be called for each predicate included in the
// access list of a transaction.
// PredicateGas will be called while calculating the IntrinsicGas of a transaction
// causing it to be dropped if the total gas goes above the tx gas limit.
// VerifyPredicate is used to populate a bit set of predicates verified prior to
// block execution, which can be accessed via the StateDB during execution.
// The bitset is stored in the block, so that historical blocks can be re-verified
// without calling VerifyPredicate.
type Predicater interface {
PredicateGas(predicateBytes []byte) (uint64, error)
VerifyPredicate(predicateContext *PredicateContext, predicateBytes []byte) error
Expand Down
73 changes: 54 additions & 19 deletions warp/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package warp

import (
"context"
"errors"
"fmt"

"github.com/ava-labs/avalanchego/cache"
Expand All @@ -19,7 +20,10 @@ import (
"github.com/ethereum/go-ethereum/log"
)

var _ Backend = &backend{}
var (
_ Backend = &backend{}
errParsingOffChainMessage = errors.New("failed to parse off-chain message")
)

const batchSize = ethdb.IdealBatchSize

Expand Down Expand Up @@ -48,28 +52,56 @@ type Backend interface {

// backend implements Backend, keeps track of warp messages, and generates message signatures.
type backend struct {
networkID uint32
sourceChainID ids.ID
db database.Database
warpSigner avalancheWarp.Signer
blockClient BlockClient
messageSignatureCache *cache.LRU[ids.ID, [bls.SignatureLen]byte]
blockSignatureCache *cache.LRU[ids.ID, [bls.SignatureLen]byte]
messageCache *cache.LRU[ids.ID, *avalancheWarp.UnsignedMessage]
networkID uint32
sourceChainID ids.ID
db database.Database
warpSigner avalancheWarp.Signer
blockClient BlockClient
messageSignatureCache *cache.LRU[ids.ID, [bls.SignatureLen]byte]
blockSignatureCache *cache.LRU[ids.ID, [bls.SignatureLen]byte]
messageCache *cache.LRU[ids.ID, *avalancheWarp.UnsignedMessage]
offchainAddressedCallMsgs map[ids.ID]*avalancheWarp.UnsignedMessage
}

// NewBackend creates a new Backend, and initializes the signature cache and message tracking database.
func NewBackend(networkID uint32, sourceChainID ids.ID, warpSigner avalancheWarp.Signer, blockClient BlockClient, db database.Database, cacheSize int) Backend {
return &backend{
networkID: networkID,
sourceChainID: sourceChainID,
db: db,
warpSigner: warpSigner,
blockClient: blockClient,
messageSignatureCache: &cache.LRU[ids.ID, [bls.SignatureLen]byte]{Size: cacheSize},
blockSignatureCache: &cache.LRU[ids.ID, [bls.SignatureLen]byte]{Size: cacheSize},
messageCache: &cache.LRU[ids.ID, *avalancheWarp.UnsignedMessage]{Size: cacheSize},
func NewBackend(
networkID uint32,
sourceChainID ids.ID,
warpSigner avalancheWarp.Signer,
blockClient BlockClient,
db database.Database,
cacheSize int,
offchainMessages [][]byte,
) (Backend, error) {
b := &backend{
networkID: networkID,
sourceChainID: sourceChainID,
db: db,
warpSigner: warpSigner,
blockClient: blockClient,
messageSignatureCache: &cache.LRU[ids.ID, [bls.SignatureLen]byte]{Size: cacheSize},
blockSignatureCache: &cache.LRU[ids.ID, [bls.SignatureLen]byte]{Size: cacheSize},
messageCache: &cache.LRU[ids.ID, *avalancheWarp.UnsignedMessage]{Size: cacheSize},
offchainAddressedCallMsgs: make(map[ids.ID]*avalancheWarp.UnsignedMessage),
}
return b, b.initOffChainMessages(offchainMessages)
}

func (b *backend) initOffChainMessages(offchainMessages [][]byte) error {
for i, offchainMsg := range offchainMessages {
unsignedMsg, err := avalancheWarp.ParseUnsignedMessage(offchainMsg)
if err != nil {
return fmt.Errorf("%w at index %d: %w", errParsingOffChainMessage, i, err)
}

_, err = payload.ParseAddressedCall(unsignedMsg.Payload)
if err != nil {
return fmt.Errorf("%w at index %d as AddressedCall: %w", errParsingOffChainMessage, i, err)
}
b.offchainAddressedCallMsgs[unsignedMsg.ID()] = unsignedMsg
}

return nil
}

func (b *backend) Clear() error {
Expand Down Expand Up @@ -160,6 +192,9 @@ func (b *backend) GetMessage(messageID ids.ID) (*avalancheWarp.UnsignedMessage,
if message, ok := b.messageCache.Get(messageID); ok {
return message, nil
}
if message, ok := b.offchainAddressedCallMsgs[messageID]; ok {
return message, nil
}

unsignedMessageBytes, err := b.db.Get(messageID[:])
if err != nil {
Expand Down
103 changes: 81 additions & 22 deletions warp/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/ava-labs/avalanchego/snow/consensus/snowman"
"github.com/ava-labs/avalanchego/snow/engine/common"
"github.com/ava-labs/avalanchego/snow/engine/snowman/block"
"github.com/ava-labs/avalanchego/utils"
"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/ava-labs/avalanchego/utils/hashing"
avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp"
Expand All @@ -22,18 +23,32 @@ import (
)

var (
networkID uint32 = 54321
sourceChainID = ids.GenerateTestID()
testPayload = []byte("test")
networkID uint32 = 54321
sourceChainID = ids.GenerateTestID()
testSourceAddress = utils.RandomBytes(20)
testPayload = []byte("test")
testUnsignedMessage *avalancheWarp.UnsignedMessage
)

func init() {
testAddressedCallPayload, err := payload.NewAddressedCall(testSourceAddress, testPayload)
if err != nil {
panic(err)
}
testUnsignedMessage, err = avalancheWarp.NewUnsignedMessage(networkID, sourceChainID, testAddressedCallPayload.Bytes())
if err != nil {
panic(err)
}
}

func TestClearDB(t *testing.T) {
db := memdb.New()

sk, err := bls.NewSecretKey()
require.NoError(t, err)
warpSigner := avalancheWarp.NewSigner(sk, networkID, sourceChainID)
backendIntf := NewBackend(networkID, sourceChainID, warpSigner, nil, db, 500)
backendIntf, err := NewBackend(networkID, sourceChainID, warpSigner, nil, db, 500, nil)
require.NoError(t, err)
backend, ok := backendIntf.(*backend)
require.True(t, ok)

Expand Down Expand Up @@ -76,20 +91,19 @@ func TestAddAndGetValidMessage(t *testing.T) {
sk, err := bls.NewSecretKey()
require.NoError(t, err)
warpSigner := avalancheWarp.NewSigner(sk, networkID, sourceChainID)
backend := NewBackend(networkID, sourceChainID, warpSigner, nil, db, 500)

// Create a new unsigned message and add it to the warp backend.
unsignedMsg, err := avalancheWarp.NewUnsignedMessage(networkID, sourceChainID, testPayload)
backend, err := NewBackend(networkID, sourceChainID, warpSigner, nil, db, 500, nil)
require.NoError(t, err)
err = backend.AddMessage(unsignedMsg)

// Add testUnsignedMessage to the warp backend
err = backend.AddMessage(testUnsignedMessage)
require.NoError(t, err)

// Verify that a signature is returned successfully, and compare to expected signature.
messageID := unsignedMsg.ID()
messageID := testUnsignedMessage.ID()
signature, err := backend.GetMessageSignature(messageID)
require.NoError(t, err)

expectedSig, err := warpSigner.Sign(unsignedMsg)
expectedSig, err := warpSigner.Sign(testUnsignedMessage)
require.NoError(t, err)
require.Equal(t, expectedSig, signature[:])
}
Expand All @@ -100,12 +114,11 @@ func TestAddAndGetUnknownMessage(t *testing.T) {
sk, err := bls.NewSecretKey()
require.NoError(t, err)
warpSigner := avalancheWarp.NewSigner(sk, networkID, sourceChainID)
backend := NewBackend(networkID, sourceChainID, warpSigner, nil, db, 500)
unsignedMsg, err := avalancheWarp.NewUnsignedMessage(networkID, sourceChainID, testPayload)
backend, err := NewBackend(networkID, sourceChainID, warpSigner, nil, db, 500, nil)
require.NoError(t, err)

// Try getting a signature for a message that was not added.
messageID := unsignedMsg.ID()
messageID := testUnsignedMessage.ID()
_, err = backend.GetMessageSignature(messageID)
require.Error(t, err)
}
Expand Down Expand Up @@ -133,7 +146,8 @@ func TestGetBlockSignature(t *testing.T) {
sk, err := bls.NewSecretKey()
require.NoError(err)
warpSigner := avalancheWarp.NewSigner(sk, networkID, sourceChainID)
backend := NewBackend(networkID, sourceChainID, warpSigner, testVM, db, 500)
backend, err := NewBackend(networkID, sourceChainID, warpSigner, testVM, db, 500, nil)
require.NoError(err)

blockHashPayload, err := payload.NewHash(blkID)
require.NoError(err)
Expand All @@ -158,20 +172,65 @@ func TestZeroSizedCache(t *testing.T) {
warpSigner := avalancheWarp.NewSigner(sk, networkID, sourceChainID)

// Verify zero sized cache works normally, because the lru cache will be initialized to size 1 for any size parameter <= 0.
backend := NewBackend(networkID, sourceChainID, warpSigner, nil, db, 0)

// Create a new unsigned message and add it to the warp backend.
unsignedMsg, err := avalancheWarp.NewUnsignedMessage(networkID, sourceChainID, testPayload)
backend, err := NewBackend(networkID, sourceChainID, warpSigner, nil, db, 0, nil)
require.NoError(t, err)
err = backend.AddMessage(unsignedMsg)

// Add testUnsignedMessage to the warp backend
err = backend.AddMessage(testUnsignedMessage)
require.NoError(t, err)

// Verify that a signature is returned successfully, and compare to expected signature.
messageID := unsignedMsg.ID()
messageID := testUnsignedMessage.ID()
signature, err := backend.GetMessageSignature(messageID)
require.NoError(t, err)

expectedSig, err := warpSigner.Sign(unsignedMsg)
expectedSig, err := warpSigner.Sign(testUnsignedMessage)
require.NoError(t, err)
require.Equal(t, expectedSig, signature[:])
}

func TestOffChainMessages(t *testing.T) {
type test struct {
offchainMessages [][]byte
check func(require *require.Assertions, b Backend)
err error
}
sk, err := bls.NewSecretKey()
require.NoError(t, err)
warpSigner := avalancheWarp.NewSigner(sk, networkID, sourceChainID)

for name, test := range map[string]test{
"no offchain messages": {},
"single off-chain message": {
offchainMessages: [][]byte{
testUnsignedMessage.Bytes(),
},
check: func(require *require.Assertions, b Backend) {
msg, err := b.GetMessage(testUnsignedMessage.ID())
require.NoError(err)
require.Equal(testUnsignedMessage.Bytes(), msg.Bytes())

signature, err := b.GetMessageSignature(testUnsignedMessage.ID())
require.NoError(err)
expectedSignatureBytes, err := warpSigner.Sign(msg)
require.NoError(err)
require.Equal(expectedSignatureBytes, signature[:])
},
},
"invalid message": {
offchainMessages: [][]byte{{1, 2, 3}},
err: errParsingOffChainMessage,
},
} {
t.Run(name, func(t *testing.T) {
require := require.New(t)
db := memdb.New()

backend, err := NewBackend(networkID, sourceChainID, warpSigner, nil, db, 0, test.offchainMessages)
require.ErrorIs(err, test.err)
if test.check != nil {
test.check(require, backend)
}
})
}
}
Loading

0 comments on commit 1593c72

Please sign in to comment.