Skip to content

Commit 50ef4ab

Browse files
JonathanOppenheimeralarso16ceyonur
authored
sync: coreth PR #963,981,1009: sync package extension and related items (#1679)
Signed-off-by: Jonathan Oppenheimer <[email protected]> Signed-off-by: Jonathan Oppenheimer <[email protected]> Co-authored-by: Austin Larson <[email protected]> Co-authored-by: Ceyhun Onur <[email protected]>
1 parent 15ab729 commit 50ef4ab

File tree

18 files changed

+476
-121
lines changed

18 files changed

+476
-121
lines changed

consensus/dummy/consensus.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,39 @@ type Mode struct {
3434
ModeSkipCoinbase bool
3535
}
3636

37-
type DummyEngine struct {
38-
consensusMode Mode
39-
desiredDelayExcess *acp226.DelayExcess
40-
}
37+
type (
38+
OnFinalizeAndAssembleCallbackType = func(
39+
header *types.Header,
40+
parent *types.Header,
41+
state *state.StateDB,
42+
txs []*types.Transaction,
43+
) (
44+
extraData []byte,
45+
blockFeeContribution *big.Int,
46+
extDataGasUsed *big.Int,
47+
err error,
48+
)
49+
50+
OnExtraStateChangeType = func(
51+
block *types.Block,
52+
parent *types.Header,
53+
statedb *state.StateDB,
54+
) (
55+
blockFeeContribution *big.Int,
56+
extDataGasUsed *big.Int,
57+
err error,
58+
)
59+
60+
ConsensusCallbacks struct {
61+
OnFinalizeAndAssemble OnFinalizeAndAssembleCallbackType
62+
OnExtraStateChange OnExtraStateChangeType
63+
}
64+
65+
DummyEngine struct {
66+
consensusMode Mode
67+
desiredDelayExcess *acp226.DelayExcess
68+
}
69+
)
4170

4271
func NewDummyEngine(
4372
mode Mode,

plugin/evm/block_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616

1717
"github.com/ava-labs/subnet-evm/params"
1818
"github.com/ava-labs/subnet-evm/params/extras"
19+
"github.com/ava-labs/subnet-evm/plugin/evm/extension"
1920
"github.com/ava-labs/subnet-evm/precompile/precompileconfig"
2021
)
2122

@@ -26,8 +27,9 @@ func TestHandlePrecompileAccept(t *testing.T) {
2627

2728
db := rawdb.NewMemoryDatabase()
2829
vm := &VM{
29-
chaindb: db,
30-
chainConfig: params.TestChainConfig,
30+
chaindb: db,
31+
chainConfig: params.TestChainConfig,
32+
extensionConfig: &extension.Config{},
3133
}
3234

3335
precompileAddr := common.Address{0x05}

plugin/evm/config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ func (d Duration) MarshalJSON() ([]byte, error) {
236236
}
237237

238238
// validate returns an error if this is an invalid config.
239-
func (c *Config) validate(_ uint32) error {
239+
func (c *Config) validate(uint32) error {
240240
if c.PopulateMissingTries != nil && (c.OfflinePruning || c.Pruning) {
241241
return fmt.Errorf("cannot enable populate missing tries while offline pruning (enabled: %t)/pruning (enabled: %t) are enabled", c.OfflinePruning, c.Pruning)
242242
}

plugin/evm/extension/config.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,30 @@
44
package extension
55

66
import (
7+
"context"
78
"errors"
89

10+
"github.com/ava-labs/avalanchego/database"
11+
"github.com/ava-labs/avalanchego/database/versiondb"
12+
"github.com/ava-labs/avalanchego/ids"
13+
"github.com/ava-labs/avalanchego/network/p2p"
14+
"github.com/ava-labs/avalanchego/snow/consensus/snowman"
15+
"github.com/ava-labs/avalanchego/snow/engine/snowman/block"
916
"github.com/ava-labs/avalanchego/utils/timer/mockable"
17+
"github.com/ava-labs/libevm/common"
18+
"github.com/ava-labs/libevm/core/types"
19+
"github.com/prometheus/client_golang/prometheus"
1020

21+
"github.com/ava-labs/subnet-evm/consensus/dummy"
22+
"github.com/ava-labs/subnet-evm/core"
23+
"github.com/ava-labs/subnet-evm/params"
24+
"github.com/ava-labs/subnet-evm/params/extras"
25+
"github.com/ava-labs/subnet-evm/plugin/evm/config"
1126
"github.com/ava-labs/subnet-evm/plugin/evm/message"
1227
"github.com/ava-labs/subnet-evm/plugin/evm/sync"
1328
"github.com/ava-labs/subnet-evm/sync/handlers"
29+
30+
avalanchecommon "github.com/ava-labs/avalanchego/snow/engine/common"
1431
)
1532

1633
var (
@@ -20,6 +37,85 @@ var (
2037
errNilClock = errors.New("nil clock")
2138
)
2239

40+
type ExtensibleVM interface {
41+
// SetExtensionConfig sets the configuration for the VM extension
42+
// Should be called before any other method and only once
43+
SetExtensionConfig(config *Config) error
44+
// NewClient returns a client to send messages with for the given protocol
45+
NewClient(protocol uint64) *p2p.Client
46+
// AddHandler registers a server handler for an application protocol
47+
AddHandler(protocol uint64, handler p2p.Handler) error
48+
// GetExtendedBlock returns the VMBlock for the given ID or an error if the block is not found
49+
GetExtendedBlock(context.Context, ids.ID) (ExtendedBlock, error)
50+
// LastAcceptedExtendedBlock returns the last accepted VM block
51+
LastAcceptedExtendedBlock() ExtendedBlock
52+
// ChainConfig returns the chain config for the VM
53+
ChainConfig() *params.ChainConfig
54+
// P2PValidators returns the validators for the network
55+
P2PValidators() *p2p.Validators
56+
// Blockchain returns the blockchain client
57+
Blockchain() *core.BlockChain
58+
// Config returns the configuration for the VM
59+
Config() config.Config
60+
// MetricRegistry returns the metric registry for the VM
61+
MetricRegistry() *prometheus.Registry
62+
// ReadLastAccepted returns the last accepted block hash and height
63+
ReadLastAccepted() (common.Hash, uint64, error)
64+
// VersionDB returns the versioned database for the VM
65+
VersionDB() *versiondb.Database
66+
}
67+
68+
// InnerVM is the interface that must be implemented by the VM
69+
// that's being wrapped by the extension
70+
type InnerVM interface {
71+
ExtensibleVM
72+
avalanchecommon.VM
73+
block.ChainVM
74+
block.BuildBlockWithContextChainVM
75+
block.StateSyncableVM
76+
}
77+
78+
// ExtendedBlock is a block that can be used by the extension
79+
type ExtendedBlock interface {
80+
snowman.Block
81+
GetEthBlock() *types.Block
82+
GetBlockExtension() BlockExtension
83+
}
84+
85+
type BlockExtender interface {
86+
// NewBlockExtension is called when a new block is created
87+
NewBlockExtension(b ExtendedBlock) (BlockExtension, error)
88+
}
89+
90+
// BlockExtension allows the VM extension to handle block processing events.
91+
type BlockExtension interface {
92+
// SyntacticVerify verifies the block syntactically
93+
// it can be implemented to extend inner block verification
94+
SyntacticVerify(rules extras.Rules) error
95+
// SemanticVerify verifies the block semantically
96+
// it can be implemented to extend inner block verification
97+
SemanticVerify() error
98+
// CleanupVerified is called when a block has passed SemanticVerify and SynctacticVerify,
99+
// and should be cleaned up due to error or verification runs under non-write mode. This
100+
// does not return an error because the block has already been verified.
101+
CleanupVerified()
102+
// Accept is called when a block is accepted by the block manager. Accept takes a
103+
// database.Batch that contains the changes that were made to the database as a result
104+
// of accepting the block. The changes in the batch should be flushed to the database in this method.
105+
Accept(acceptedBatch database.Batch) error
106+
// Reject is called when a block is rejected by the block manager
107+
Reject() error
108+
}
109+
110+
// BuilderMempool is a mempool that's used in the block builder
111+
type BuilderMempool interface {
112+
// PendingLen returns the number of pending transactions
113+
// that are waiting to be included in a block
114+
PendingLen() int
115+
// SubscribePendingTxs returns a channel that's signaled when there are pending transactions
116+
SubscribePendingTxs() <-chan struct{}
117+
}
118+
23119
// LeafRequestConfig is the configuration to handle leaf requests
24120
// in the network and syncer
25121
type LeafRequestConfig struct {
@@ -33,13 +129,29 @@ type LeafRequestConfig struct {
33129

34130
// Config is the configuration for the VM extension
35131
type Config struct {
132+
// ConsensusCallbacks is the consensus callbacks to use
133+
// for the VM to be used in consensus engine.
134+
// Callback functions can be nil.
135+
ConsensusCallbacks dummy.ConsensusCallbacks
36136
// SyncSummaryProvider is the sync summary provider to use
37137
// for the VM to be used in syncer.
38138
// It's required and should be non-nil
39139
SyncSummaryProvider sync.SummaryProvider
140+
// SyncExtender can extend the syncer to handle custom sync logic.
141+
// It's optional and can be nil
142+
SyncExtender sync.Extender
40143
// SyncableParser is to parse summary messages from the network.
41144
// It's required and should be non-nil
42145
SyncableParser message.SyncableParser
146+
// BlockExtender allows the VM extension to create an extension to handle block processing events.
147+
// It's optional and can be nil
148+
BlockExtender BlockExtender
149+
// ExtraSyncLeafHandlerConfig is the extra configuration to handle leaf requests
150+
// in the network and syncer. It's optional and can be nil
151+
ExtraSyncLeafHandlerConfig *LeafRequestConfig
152+
// ExtraMempool is the mempool to be used in the block builder.
153+
// It's optional and can be nil
154+
ExtraMempool BuilderMempool
43155
// Clock is the clock to use for time related operations.
44156
// It's optional and can be nil
45157
Clock *mockable.Clock
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package message
5+
6+
import (
7+
"encoding/base64"
8+
"testing"
9+
10+
"github.com/ava-labs/avalanchego/snow/engine/snowman/block"
11+
"github.com/ava-labs/libevm/common"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestMarshalBlockSyncSummary(t *testing.T) {
16+
blockSyncSummary, err := NewBlockSyncSummary(common.Hash{1}, 2, common.Hash{3})
17+
require.NoError(t, err)
18+
19+
require.Equal(t, common.Hash{1}, blockSyncSummary.GetBlockHash())
20+
require.Equal(t, uint64(2), blockSyncSummary.Height())
21+
require.Equal(t, common.Hash{3}, blockSyncSummary.GetBlockRoot())
22+
23+
expectedBase64Bytes := "AAAAAAAAAAAAAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
24+
require.Equal(t, expectedBase64Bytes, base64.StdEncoding.EncodeToString(blockSyncSummary.Bytes()))
25+
26+
parser := NewBlockSyncSummaryParser()
27+
called := false
28+
acceptImplTest := func(Syncable) (block.StateSyncMode, error) {
29+
called = true
30+
return block.StateSyncSkipped, nil
31+
}
32+
s, err := parser.Parse(blockSyncSummary.Bytes(), acceptImplTest)
33+
require.NoError(t, err)
34+
require.Equal(t, blockSyncSummary.GetBlockHash(), s.GetBlockHash())
35+
require.Equal(t, blockSyncSummary.Height(), s.Height())
36+
require.Equal(t, blockSyncSummary.GetBlockRoot(), s.GetBlockRoot())
37+
require.Equal(t, blockSyncSummary.Bytes(), s.Bytes())
38+
39+
mode, err := s.Accept(t.Context())
40+
require.NoError(t, err)
41+
require.Equal(t, block.StateSyncSkipped, mode)
42+
require.True(t, called)
43+
}

plugin/evm/message/codec.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,11 @@ func init() {
2222
c := linearcodec.NewDefault()
2323

2424
// Skip registration to keep registeredTypes unchanged after legacy gossip deprecation
25-
c.SkipRegistrations(1)
25+
// Gossip types and sync summary type removed from codec
26+
c.SkipRegistrations(2)
2627

2728
errs := wrappers.Errs{}
2829
errs.Add(
29-
// Types for state sync frontier consensus
30-
c.RegisterType(BlockSyncSummary{}),
31-
3230
// state sync types
3331
c.RegisterType(BlockRequest{}),
3432
c.RegisterType(BlockResponse{}),

plugin/evm/vm.go

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ type VM struct {
187187

188188
config config.Config
189189

190+
chainID *big.Int
190191
genesisHash common.Hash
191192
chainConfig *params.ChainConfig
192193
ethConfig ethconfig.Config
@@ -279,7 +280,7 @@ func (vm *VM) Initialize(
279280
appSender commonEng.AppSender,
280281
) error {
281282
vm.ctx = chainCtx
282-
vm.stateSyncDone = make(chan struct{})
283+
283284
cfg, deprecateMsg, err := config.GetConfig(configBytes, vm.ctx.NetworkID)
284285
if err != nil {
285286
return fmt.Errorf("failed to get config: %w", err)
@@ -341,14 +342,22 @@ func (vm *VM) Initialize(
341342
return err
342343
}
343344

345+
// vm.ChainConfig() should be available for wrapping VMs before vm.initializeChain()
346+
vm.chainConfig = g.Config
347+
vm.chainID = g.Config.ChainID
348+
344349
vm.ethConfig = ethconfig.NewDefaultConfig()
345350
vm.ethConfig.Genesis = g
346-
// NetworkID here is different than Avalanche's NetworkID.
347-
// Avalanche's NetworkID represents the Avalanche network is running on
348-
// like Fuji, Mainnet, Local, etc.
349-
// The NetworkId here is kept same as ChainID to be compatible with
350-
// Ethereum tooling.
351-
vm.ethConfig.NetworkId = g.Config.ChainID.Uint64()
351+
vm.ethConfig.NetworkId = vm.chainID.Uint64()
352+
vm.genesisHash = vm.ethConfig.Genesis.ToBlock().Hash() // must create genesis hash before [vm.ReadLastAccepted]
353+
lastAcceptedHash, lastAcceptedHeight, err := vm.ReadLastAccepted()
354+
if err != nil {
355+
return err
356+
}
357+
log.Info("read last accepted",
358+
"hash", lastAcceptedHash,
359+
"height", lastAcceptedHeight,
360+
)
352361

353362
// Set minimum price for mining and default gas price oracle value to the min
354363
// gas price to prevent so transactions and blocks all use the correct fees
@@ -438,20 +447,6 @@ func (vm *VM) Initialize(
438447
vm.ethConfig.Miner.Etherbase = constants.BlackholeAddr
439448
}
440449

441-
vm.chainConfig = g.Config
442-
443-
// create genesisHash after applying upgradeBytes in case
444-
// upgradeBytes modifies genesis.
445-
vm.genesisHash = vm.ethConfig.Genesis.ToBlock().Hash() // must create genesis hash before [vm.readLastAccepted]
446-
lastAcceptedHash, lastAcceptedHeight, err := vm.readLastAccepted()
447-
if err != nil {
448-
return err
449-
}
450-
log.Info("read last accepted",
451-
"hash", lastAcceptedHash,
452-
"height", lastAcceptedHeight,
453-
)
454-
455450
vm.networkCodec = message.Codec
456451
vm.Network, err = network.NewNetwork(vm.ctx, appSender, vm.networkCodec, vm.config.MaxOutboundActiveRequests, vm.sdkMetrics)
457452
if err != nil {
@@ -494,7 +489,6 @@ func (vm *VM) Initialize(
494489
if err != nil {
495490
return err
496491
}
497-
498492
if err := vm.initializeChain(lastAcceptedHash, vm.ethConfig); err != nil {
499493
return err
500494
}
@@ -730,6 +724,7 @@ func (vm *VM) initializeStateSync(lastAcceptedHeight uint64) error {
730724
MetadataDB: vm.metadataDB,
731725
Acceptor: vm,
732726
Parser: vm.extensionConfig.SyncableParser,
727+
Extender: nil,
733728
})
734729

735730
// If StateSync is disabled, clear any ongoing summary so that we will not attempt to resume
@@ -803,7 +798,6 @@ func (vm *VM) onBootstrapStarted() error {
803798
// Ensure snapshots are initialized before bootstrapping (i.e., if state sync is skipped).
804799
// Note calling this function has no effect if snapshots are already initialized.
805800
vm.blockChain.InitializeSnapshots()
806-
807801
return nil
808802
}
809803

@@ -1310,7 +1304,7 @@ func (vm *VM) startContinuousProfiler() {
13101304
// last accepted block hash and height by reading directly from [vm.chaindb] instead of relying
13111305
// on [chain].
13121306
// Note: assumes [vm.chaindb] and [vm.genesisHash] have been initialized.
1313-
func (vm *VM) readLastAccepted() (common.Hash, uint64, error) {
1307+
func (vm *VM) ReadLastAccepted() (common.Hash, uint64, error) {
13141308
// Attempt to load last accepted block to determine if it is necessary to
13151309
// initialize state with the genesis block.
13161310
lastAcceptedBytes, lastAcceptedErr := vm.acceptedBlockDB.Get(lastAcceptedKey)

0 commit comments

Comments
 (0)