From d502b775693157411c1eefa1f8bb17c752d045cd Mon Sep 17 00:00:00 2001 From: alecps Date: Tue, 30 Jul 2024 15:21:06 -0400 Subject: [PATCH 01/21] code pointers --- cmd/geth/main.go | 2 +- cmd/utils/cmd.go | 2 +- consensus/istanbul/backend/engine.go | 2 +- consensus/istanbul/core/handler.go | 2 +- core/blockchain.go | 2 +- eth/backend.go | 2 +- node/node.go | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index c841f3c5cb..b7d2cf3554 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -319,7 +319,7 @@ func prepare(ctx *cli.Context) { // geth is the main entry point into the system if no special subcommand is ran. // It creates a default node based on the command line arguments and runs it in // blocking mode, waiting for it to be shut down. -func geth(ctx *cli.Context) error { +func geth(ctx *cli.Context) error { // TODO(Alec) code pointer if args := ctx.Args(); len(args) > 0 { return fmt.Errorf("invalid command: %q", args[0]) } diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index 06d296801a..ed1bf487ff 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -66,7 +66,7 @@ func Fatalf(format string, args ...interface{}) { os.Exit(1) } -func StartNode(ctx *cli.Context, stack *node.Node) { +func StartNode(ctx *cli.Context, stack *node.Node) { // TODO(Alec) code pointer if err := stack.Start(); err != nil { Fatalf("Error starting protocol stack: %v", err) } diff --git a/consensus/istanbul/backend/engine.go b/consensus/istanbul/backend/engine.go index 6fb4999563..41f5f100e6 100644 --- a/consensus/istanbul/backend/engine.go +++ b/consensus/istanbul/backend/engine.go @@ -1058,7 +1058,7 @@ func (sb *Backend) SetStartValidatingBlock(blockNumber *big.Int) error { } // SetStopValidatingBlock sets the block that the validator will stop just before (exclusive range) -func (sb *Backend) SetStopValidatingBlock(blockNumber *big.Int) error { +func (sb *Backend) SetStopValidatingBlock(blockNumber *big.Int) error { // TODO(Alec) code pointer if sb.replicaState == nil { return errNotAValidator } diff --git a/consensus/istanbul/core/handler.go b/consensus/istanbul/core/handler.go index a591fb217a..dbcb09e7b1 100644 --- a/consensus/istanbul/core/handler.go +++ b/consensus/istanbul/core/handler.go @@ -59,7 +59,7 @@ func (c *core) Start() error { } // Stop implements core.Engine.Stop -func (c *core) Stop() error { +func (c *core) Stop() error { // TODO(Alec) code pointer c.stopAllTimers() c.unsubscribeEvents() diff --git a/core/blockchain.go b/core/blockchain.go index 5ebb1a9f72..48b0e8b0e3 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1018,7 +1018,7 @@ func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) { // Stop stops the blockchain service. If any imports are currently in progress // it will abort them using the procInterrupt. -func (bc *BlockChain) Stop() { +func (bc *BlockChain) Stop() { // TODO(Alec) code pointer if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) { return } diff --git a/eth/backend.go b/eth/backend.go index 0aea69f1e6..92a683efe9 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -600,7 +600,7 @@ func (s *Ethereum) Start() error { // Stop implements node.Lifecycle, terminating all internal goroutines used by the // Ethereum protocol. -func (s *Ethereum) Stop() error { +func (s *Ethereum) Stop() error { // TODO(Alec) code pointer // Stop all the peer-related stuff first. s.stopAnnounce() s.ethDialCandidates.Close() diff --git a/node/node.go b/node/node.go index a05209dd02..1f689a9117 100644 --- a/node/node.go +++ b/node/node.go @@ -215,7 +215,7 @@ func (n *Node) Start() error { // Close stops the Node and releases resources acquired in // Node constructor New. -func (n *Node) Close() error { +func (n *Node) Close() error { // TODO(Alec) code pointer n.startStopLock.Lock() defer n.startStopLock.Unlock() From a8a7a9342b7384e8511f70c3cb885d30ea1893b0 Mon Sep 17 00:00:00 2001 From: alecps Date: Wed, 31 Jul 2024 18:29:36 -0400 Subject: [PATCH 02/21] saving progress --- cmd/geth/main.go | 3 ++- cmd/utils/flags.go | 9 +++++++++ consensus/istanbul/backend/backend.go | 9 +++++++++ consensus/istanbul/core/prepare.go | 4 ++++ core/error.go | 3 +++ core/genesis.go | 7 +++++-- eth/backend.go | 2 +- eth/ethconfig/config.go | 3 +++ params/config.go | 12 ++++++++++++ 9 files changed, 48 insertions(+), 4 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index b7d2cf3554..4d551e74b9 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -74,6 +74,7 @@ var ( utils.USBFlag, // utils.SmartCardDaemonPathFlag, utils.OverrideHForkFlag, + utils.L2ForkFlag, utils.TxPoolLocalsFlag, utils.TxPoolNoLocalsFlag, utils.TxPoolJournalFlag, @@ -414,7 +415,7 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) { if timestamp := time.Unix(int64(latest.Time), 0); time.Since(timestamp) < 10*time.Minute { log.Info("Synchronisation completed", "latestnum", latest.Number, "latesthash", latest.Hash(), "age", common.PrettyAge(timestamp)) - stack.Close() + stack.Close() // TODO(Alec) code pointer } } }() diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 272b68d927..c107ee678a 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -243,6 +243,12 @@ var ( Usage: "Manually specify the hfork block, overriding the bundled setting", } + // Celo L2 Migration Flags + L2ForkFlag = cli.Uint64Flag{ + Name: "l2.fork", + Usage: "Block number at which to halt the network for Celo L2 migration", + } + BloomFilterSizeFlag = cli.Uint64Flag{ Name: "bloomfilter.size", Usage: "Megabytes of memory allocated to bloom-filter for pruning", @@ -1723,6 +1729,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc)) godebug.SetGCPercent(int(gogc)) + if ctx.GlobalIsSet(L2ForkFlag.Name) { + cfg.L2Fork = new(big.Int).SetUint64(ctx.GlobalUint64(L2ForkFlag.Name)) + } if ctx.GlobalIsSet(SyncModeFlag.Name) { cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode) } diff --git a/consensus/istanbul/backend/backend.go b/consensus/istanbul/backend/backend.go index b33da9618b..bb03e34a3e 100644 --- a/consensus/istanbul/backend/backend.go +++ b/consensus/istanbul/backend/backend.go @@ -554,6 +554,10 @@ func (sb *Backend) Commit(proposal istanbul.Proposal, aggregatedSeal types.Istan } } sb.onNewConsensusBlock(block, result.Receipts, result.Logs, result.State) + if sb.chain.Config().IsL2(block.Number()) { + sb.logger.Info("L2 hard fork reached, stopping the backend") + sb.StopValidating() + } return nil } @@ -571,6 +575,11 @@ func (sb *Backend) Verify(proposal istanbul.Proposal) (*istanbulCore.StateProces return nil, 0, errInvalidProposal } + // Don't verify blocks after the L2 hard fork + if sb.chain.Config().IsL2(block.Number()) { + return nil, 0, core.ErrPostL2BlockNumber + } + // check bad block if sb.hasBadProposal(block.Hash()) { return nil, 0, core.ErrBannedHash diff --git a/consensus/istanbul/core/prepare.go b/consensus/istanbul/core/prepare.go index be58883190..09c22e2e66 100644 --- a/consensus/istanbul/core/prepare.go +++ b/consensus/istanbul/core/prepare.go @@ -23,6 +23,7 @@ import ( "github.com/celo-org/celo-blockchain/common" "github.com/celo-org/celo-blockchain/consensus/istanbul" + celoCore "github.com/celo-org/celo-blockchain/core" ) func (c *core) sendPrepare() { @@ -109,6 +110,9 @@ func (c *core) verifyPreparedCertificate(preparedCertificate istanbul.PreparedCe func (c *core) verifyProposalAndPCMessages(proposal istanbul.Proposal, pCMessages []istanbul.Message) (*istanbul.View, error) { // Validate the attached proposal if _, err := c.verifyProposal(proposal); err != nil { + if err == celoCore.ErrPostL2BlockNumber { + return nil, err + } return nil, errInvalidPreparedCertificateProposal } diff --git a/core/error.go b/core/error.go index f5515dd858..fdb58bbb21 100644 --- a/core/error.go +++ b/core/error.go @@ -131,4 +131,7 @@ var ( // ErrDenominatedLowMaxFee is returned when a celo denominated transaction, with the current exchange rate, // the MaxFeeInFeeCurrency cannot cover the tx.Fee() ErrDenominatedLowMaxFee = errors.New("CELO denominated tx MaxFeeInCurrency cannot cover gas fee costs") + + // ErrPostL2BlockNumber is returned when the block number is after the L2 migration block + ErrPostL2BlockNumber = errors.New("Block number is after the L2 migration block") ) diff --git a/core/genesis.go b/core/genesis.go index 34a0731ea3..bacb1bdf37 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -148,10 +148,10 @@ func (e *GenesisMismatchError) Error() string { // // The returned chain configuration is never nil. func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) { - return SetupGenesisBlockWithOverride(db, genesis, nil) + return SetupGenesisBlockWithOverride(db, genesis, nil, nil) } -func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideHFork *big.Int) (*params.ChainConfig, common.Hash, error) { +func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideHFork, l2Fork *big.Int) (*params.ChainConfig, common.Hash, error) { if genesis != nil && (genesis.Config == nil || genesis.Config.Istanbul == nil) { return params.MainnetChainConfig, common.Hash{}, errGenesisNoConfig } @@ -207,6 +207,9 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override if overrideHFork != nil { newcfg.HForkBlock = overrideHFork } + if l2Fork != nil { + newcfg.L2Block = l2Fork + } if err := newcfg.CheckConfigForkOrder(); err != nil { return newcfg, common.Hash{}, err } diff --git a/eth/backend.go b/eth/backend.go index 92a683efe9..d9acf8fdf8 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -131,7 +131,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if err != nil { return nil, err } - chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideHFork) + chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideHFork, config.L2Fork) if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { return nil, genesisErr } diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 264eaf7487..788829e28f 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -165,6 +165,9 @@ type Config struct { // HFork block override (TODO: remove after the fork) OverrideHFork *big.Int `toml:",omitempty"` + // l2 fork block override + L2Fork *big.Int `toml:",omitempty"` + // The minimum required peers in order for syncing to be initiated, if left // at 0 then the default will be used. MinSyncPeers int `toml:",omitempty"` diff --git a/params/config.go b/params/config.go index 419cb02bd0..ff80b4d402 100644 --- a/params/config.go +++ b/params/config.go @@ -313,6 +313,7 @@ type ChainConfig struct { GingerbreadBlock *big.Int `json:"gingerbreadBlock,omitempty"` // Gingerbread switch block (nil = no fork, 0 = already activated) GingerbreadP2Block *big.Int `json:"gingerbreadP2Block,omitempty"` // GingerbreadP2 switch block (nil = no fork, 0 = already activated) HForkBlock *big.Int `json:"hforkBlock,omitempty"` // HFork switch block (nil = no fork, 0 = already activated) + L2Block *big.Int `json:"l2Block,omitempty"` // l2 switch block (nil = no fork, 0 = already activated) Istanbul *IstanbulConfig `json:"istanbul,omitempty"` // This does not belong here but passing it to every function is not possible since that breaks @@ -464,6 +465,11 @@ func (c *ChainConfig) IsGingerbreadP2(num *big.Int) bool { return isForked(c.GingerbreadP2Block, num) } +// IsL2 returns whether num represents a block number after the Cel2 fork +func (c *ChainConfig) IsL2(num *big.Int) bool { + return isForked(c.L2Block, num) +} + func (c *ChainConfig) IsHFork(num *big.Int) bool { return isForked(c.HForkBlock, num) } @@ -510,6 +516,7 @@ func (c *ChainConfig) CheckConfigForkOrder() error { {name: "gingerbreadBlock", block: c.GingerbreadBlock}, {name: "gingerbreadP2Block", block: c.GingerbreadP2Block}, {name: "hforkBlock", block: c.HForkBlock}, + {name: "l2Block", block: c.L2Block}, } { if lastFork.name != "" { // Next one must be higher number @@ -593,6 +600,9 @@ func (c *ChainConfig) checkCeloCompatible(newcfg *ChainConfig, head *big.Int) *C if isForkIncompatible(c.HForkBlock, newcfg.HForkBlock, head) { return newCompatError("HFork block", c.HForkBlock, newcfg.HForkBlock) } + if isForkIncompatible(c.L2Block, newcfg.L2Block, head) { + return newCompatError("L2 block", c.L2Block, newcfg.L2Block) + } return nil } @@ -711,6 +721,7 @@ func (c *ChainConfig) DisableGingerbread() *ChainConfig { c.GingerbreadP2Block = nil // Since gingerbread is disabled disable following forks as well c.HForkBlock = nil + c.L2Block = nil return c } @@ -734,6 +745,7 @@ func (c *ChainConfig) DeepCopy() *ChainConfig { GingerbreadBlock: copyBigIntOrNil(c.GingerbreadBlock), GingerbreadP2Block: copyBigIntOrNil(c.GingerbreadP2Block), HForkBlock: copyBigIntOrNil(c.HForkBlock), + L2Block: copyBigIntOrNil(c.L2Block), Istanbul: &IstanbulConfig{ Epoch: c.Istanbul.Epoch, From d9e7f7e4342f1b01a831c9d3a2d96ed609e3e412 Mon Sep 17 00:00:00 2001 From: alecps Date: Mon, 5 Aug 2024 15:53:32 -0400 Subject: [PATCH 03/21] add e2e test, saving progress --- cmd/geth/main.go | 22 ++++++++ consensus/istanbul/backend/backend.go | 10 ++-- consensus/istanbul/backend/engine.go | 2 +- core/blockchain.go | 6 ++ core/genesis.go | 6 +- e2e_test/e2e_bench_test.go | 4 +- e2e_test/e2e_test.go | 80 ++++++++++++++++++++------- e2e_test/e2e_transfer_test.go | 4 +- eth/backend.go | 4 ++ eth/ethconfig/config.go | 2 +- les/client.go | 2 +- params/config.go | 4 +- test/node.go | 3 +- 13 files changed, 112 insertions(+), 37 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 4d551e74b9..ac555a567d 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -390,6 +390,28 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) { } }() + // if ctx.GlobalIsSet(utils.L2ForkFlag.Name) { + // go func() { + // sub := stack.EventMux().Subscribe(ethCore.ChainHeadEvent{}) + // defer sub.Unsubscribe() + // for { + // event := <-sub.Chan() + // if event == nil { + // continue + // } + // var latest *types.Header + // if done, ok := event.Data.(ethCore.ChainHeadEvent); ok { + // latest = done.Block.Header() + // } + + // if ctx.GlobalUint64(utils.L2ForkFlag.Name) >= latest.Number.Uint64() { + // log.Info("L2 Migration Block Reached", "latest", latest.Number.Uint64(), "latesthash", latest.Hash()) + // stack.Close() + // } + // } + // }() + // } + // Spawn a standalone goroutine for status synchronization monitoring, // close the node when synchronization is complete if user required. if ctx.GlobalBool(utils.ExitWhenSyncedFlag.Name) { diff --git a/consensus/istanbul/backend/backend.go b/consensus/istanbul/backend/backend.go index bb03e34a3e..ea6dee75a3 100644 --- a/consensus/istanbul/backend/backend.go +++ b/consensus/istanbul/backend/backend.go @@ -554,9 +554,11 @@ func (sb *Backend) Commit(proposal istanbul.Proposal, aggregatedSeal types.Istan } } sb.onNewConsensusBlock(block, result.Receipts, result.Logs, result.State) + if sb.chain.Config().IsL2(block.Number()) { sb.logger.Info("L2 hard fork reached, stopping the backend") - sb.StopValidating() + sb.Close() + //sb.core.Stop() } return nil } @@ -576,9 +578,9 @@ func (sb *Backend) Verify(proposal istanbul.Proposal) (*istanbulCore.StateProces } // Don't verify blocks after the L2 hard fork - if sb.chain.Config().IsL2(block.Number()) { - return nil, 0, core.ErrPostL2BlockNumber - } + // if sb.chain.Config().IsL2(block.Number()) { + // return nil, 0, core.ErrPostL2BlockNumber + // } // check bad block if sb.hasBadProposal(block.Hash()) { diff --git a/consensus/istanbul/backend/engine.go b/consensus/istanbul/backend/engine.go index 41f5f100e6..6fb4999563 100644 --- a/consensus/istanbul/backend/engine.go +++ b/consensus/istanbul/backend/engine.go @@ -1058,7 +1058,7 @@ func (sb *Backend) SetStartValidatingBlock(blockNumber *big.Int) error { } // SetStopValidatingBlock sets the block that the validator will stop just before (exclusive range) -func (sb *Backend) SetStopValidatingBlock(blockNumber *big.Int) error { // TODO(Alec) code pointer +func (sb *Backend) SetStopValidatingBlock(blockNumber *big.Int) error { if sb.replicaState == nil { return errNotAValidator } diff --git a/core/blockchain.go b/core/blockchain.go index 48b0e8b0e3..8215eca5c6 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -811,6 +811,12 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { // // Note, this function assumes that the `mu` mutex is held! func (bc *BlockChain) writeHeadBlock(block *types.Block) { + if bc.Config().IsL2(block.Number()) { + log.Info("L2 hard fork reached, stopping the blockchain") + bc.StopInsert() + // bc.Stop() + bc.Engine().Close() + } // If the block is on a side chain or an unknown one, force other heads onto it too updateHeads := rawdb.ReadCanonicalHash(bc.db, block.NumberU64()) != block.Hash() diff --git a/core/genesis.go b/core/genesis.go index bacb1bdf37..3e0baa4e89 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -207,9 +207,9 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override if overrideHFork != nil { newcfg.HForkBlock = overrideHFork } - if l2Fork != nil { - newcfg.L2Block = l2Fork - } + // if l2Fork != nil { + // newcfg.L2Block = l2Fork + // } if err := newcfg.CheckConfigForkOrder(); err != nil { return newcfg, common.Hash{}, err } diff --git a/e2e_test/e2e_bench_test.go b/e2e_test/e2e_bench_test.go index 69b3bd4ba3..438d2f6e89 100644 --- a/e2e_test/e2e_bench_test.go +++ b/e2e_test/e2e_bench_test.go @@ -19,7 +19,7 @@ func BenchmarkNet100EmptyBlocks(b *testing.B) { for i := 0; i < b.N; i++ { ac := test.AccountConfig(n, 0) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(b, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(b, err) @@ -45,7 +45,7 @@ func BenchmarkNet1000Txs(b *testing.B) { ac := test.AccountConfig(n, n) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(b, err) accounts := test.Accounts(ac.DeveloperAccounts(), gc.ChainConfig()) network, shutdown, err := test.NewNetwork(ac, gc, ec) diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index fc73486ec6..6fe45379e8 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -45,7 +45,7 @@ func init() { func TestSendCelo(t *testing.T) { ac := test.AccountConfig(3, 2) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -71,7 +71,7 @@ func TestSendCelo(t *testing.T) { func TestTraceSendCeloViaGoldToken(t *testing.T) { ac := test.AccountConfig(3, 2) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -108,7 +108,7 @@ func TestTraceSendCeloViaGoldToken(t *testing.T) { func TestCallTraceTransactionNativeTransfer(t *testing.T) { ac := test.AccountConfig(1, 2) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -146,7 +146,7 @@ func TestCallTraceTransactionNativeTransfer(t *testing.T) { func TestPrestateTransactionNativeTransfer(t *testing.T) { ac := test.AccountConfig(1, 2) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -184,7 +184,7 @@ func TestSingleNodeNetworkManyTxs(t *testing.T) { txsPerIteration := 5 ac := test.AccountConfig(1, 1) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(t, err) gc.Istanbul.Epoch = uint64(iterations) * 50 // avoid the epoch for this test network, shutdown, err := test.NewNetwork(ac, gc, ec) @@ -210,7 +210,7 @@ func TestSingleNodeNetworkManyTxs(t *testing.T) { func TestEpochBlockMarshaling(t *testing.T) { accounts := test.AccountConfig(1, 0) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(accounts, gingerbreadBlock) + gc, ec, err := test.BuildConfig(accounts, gingerbreadBlock, nil) require.NoError(t, err) // Configure the shortest possible epoch, uptimeLookbackWindow minimum is 3 @@ -240,7 +240,7 @@ func TestEpochBlockMarshaling(t *testing.T) { func TestStartStopValidators(t *testing.T) { ac := test.AccountConfig(4, 2) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(t, err) network, _, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -369,6 +369,46 @@ func TestStartStopValidators(t *testing.T) { } +// TODO(Alec) +func TestStopNetworkAtL2Block(t *testing.T) { + ac := test.AccountConfig(3, 2) + gingerbreadBlock := common.Big0 + l2Block := big.NewInt(2) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, l2Block) + require.NoError(t, err) + network, _, err := test.NewNetwork(ac, gc, ec) + require.NoError(t, err) + + // We define our own shutdown function because we don't want to print + // errors about already stopped nodes. Since this test can fail while we + // have stopped nodes. + defer func() { + for _, err := range network.Shutdown() { + if !errors.Is(err, test.ErrTrackerAlreadyStopped) && !errors.Is(err, node.ErrNodeStopped) { + fmt.Println(err.Error()) + } + } + }() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*200) + defer cancel() + + err = network.AwaitBlock(ctx, l2Block.Uint64()) + require.NoError(t, err) + + // err = network.AwaitBlock(ctx, l2Block.Uint64()) + // require.NoError(t, err) + + shortCtx, cancel := context.WithTimeout(context.Background(), time.Second*3) + defer cancel() + + err = network.AwaitBlock(shortCtx, l2Block.Uint64()+1) + // Expect DeadlineExceeded error + if !errors.Is(err, context.DeadlineExceeded) { + t.Fatalf("expecting %q, instead got: %v ", context.DeadlineExceeded.Error(), err) + } +} + // This test was created to reproduce the concurrent map access error in // https://github.com/celo-org/celo-blockchain/issues/1799 // @@ -377,7 +417,7 @@ func TestStartStopValidators(t *testing.T) { func TestBlockTracingConcurrentMapAccess(t *testing.T) { ac := test.AccountConfig(1, 2) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -428,7 +468,7 @@ func TestBlockTracingConcurrentMapAccess(t *testing.T) { func TestBlockTracingSequentialAccess(t *testing.T) { ac := test.AccountConfig(1, 2) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -463,7 +503,7 @@ type rpcCustomTransaction struct { func TestRPCDynamicTxGasPriceWithBigFeeCap(t *testing.T) { ac := test.AccountConfig(3, 2) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -504,7 +544,7 @@ func TestRPCDynamicTxGasPriceWithBigFeeCap(t *testing.T) { func TestRPCDynamicTxGasPriceWithState(t *testing.T) { ac := test.AccountConfig(3, 2) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(t, err) ec.TxLookupLimit = 0 ec.NoPruning = true @@ -582,7 +622,7 @@ func testRPCDynamicTxGasPriceWithoutState(t *testing.T, afterGingerbread, altern } cusdAddress := common.HexToAddress("0xd008") ac := test.AccountConfig(3, 2) - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) ec.TrieDirtyCache = 5 require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) @@ -656,7 +696,7 @@ func pruneStateOfBlock(ctx context.Context, node *test.Node, blockNumber *big.In func runMochaTest(t *testing.T, add_args func(*env.AccountsConfig, *genesis.Config, test.Network) []string) { ac := test.AccountConfig(1, 1) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -702,7 +742,7 @@ func TestEthersJSCompatibility(t *testing.T) { func TestEthersJSCompatibilityDisableAfterGingerbread(t *testing.T) { ac := test.AccountConfig(1, 1) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(t, err) // Check fields present (compatibility set by default) @@ -750,7 +790,7 @@ func TestEthersJSCompatibilityDisableAfterGingerbread(t *testing.T) { func TestEthersJSCompatibilityDisableBeforeGingerbread(t *testing.T) { ac := test.AccountConfig(1, 1) var gingerbreadBlock *big.Int = nil - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(t, err) // Check fields present (compatibility set by default) @@ -807,7 +847,7 @@ func TestEthCompatibilityFieldsOnGenesisBlock(t *testing.T) { var gingerbreadBlock *big.Int = nil // Fist we test without eth compatibility to ensure that the setting has an effect. - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) ec.RPCEthCompatibility = false require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) @@ -826,7 +866,7 @@ func TestEthCompatibilityFieldsOnGenesisBlock(t *testing.T) { // Now we with eth compatility enabled and see that gasLimit and baseFee // are returned on the block. - gc, ec, err = test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err = test.BuildConfig(ac, gingerbreadBlock, nil) ec.RPCEthCompatibility = true require.NoError(t, err) network, shutdown, err = test.NewNetwork(ac, gc, ec) @@ -848,7 +888,7 @@ func TestSettingGingerbreadOnGenesisBlock(t *testing.T) { // Fist we test without gingerbread to ensure that setting the gingerbread // actually has an effect. var gingerbreadBlock *big.Int = nil - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) ec.RPCEthCompatibility = false require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) @@ -867,7 +907,7 @@ func TestSettingGingerbreadOnGenesisBlock(t *testing.T) { // Now we check that setting the gingerbread block at genesis causes gasLimit and baseFee to be set on the block. gingerbreadBlock = big.NewInt(0) - gc, ec, err = test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err = test.BuildConfig(ac, gingerbreadBlock, nil) ec.RPCEthCompatibility = false require.NoError(t, err) network, shutdown, err = test.NewNetwork(ac, gc, ec) @@ -886,7 +926,7 @@ func TestSettingGingerbreadOnGenesisBlock(t *testing.T) { func TestGetFinalizedBlock(t *testing.T) { ac := test.AccountConfig(2, 2) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) diff --git a/e2e_test/e2e_transfer_test.go b/e2e_test/e2e_transfer_test.go index 4ebbbf929f..cdc2f0cdba 100644 --- a/e2e_test/e2e_transfer_test.go +++ b/e2e_test/e2e_transfer_test.go @@ -37,7 +37,7 @@ const ( func TestTransferCELO(t *testing.T) { ac := test.AccountConfig(1, 3) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -259,7 +259,7 @@ func prepareTransaction(txArgs ethapi.TransactionArgs, senderKey *ecdsa.PrivateK func TestTransferERC20(t *testing.T) { ac := test.AccountConfig(1, 3) gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, nil) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) diff --git a/eth/backend.go b/eth/backend.go index d9acf8fdf8..eba1accd7b 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -138,6 +138,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { log.Info("Initialised chain configuration", "config", chainConfig) chainConfig.FullHeaderChainAvailable = config.SyncMode.SyncFullHeaderChain() + if config.L2Fork != nil { + chainConfig.L2Block = config.L2Fork + } + if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb, stack.ResolvePath(config.TrieCleanCacheJournal)); err != nil { log.Error("Failed to recover state", "error", err) } diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 788829e28f..d64f94df62 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -165,7 +165,7 @@ type Config struct { // HFork block override (TODO: remove after the fork) OverrideHFork *big.Int `toml:",omitempty"` - // l2 fork block override + // l2 fork block L2Fork *big.Int `toml:",omitempty"` // The minimum required peers in order for syncing to be initiated, if left diff --git a/les/client.go b/les/client.go index 30ed058af4..fd8c9e7b1c 100644 --- a/les/client.go +++ b/les/client.go @@ -104,7 +104,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) { return nil, err } chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, - config.OverrideHFork) + config.OverrideHFork, config.L2Fork) if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat { return nil, genesisErr } diff --git a/params/config.go b/params/config.go index ff80b4d402..0a72127bf4 100644 --- a/params/config.go +++ b/params/config.go @@ -516,7 +516,7 @@ func (c *ChainConfig) CheckConfigForkOrder() error { {name: "gingerbreadBlock", block: c.GingerbreadBlock}, {name: "gingerbreadP2Block", block: c.GingerbreadP2Block}, {name: "hforkBlock", block: c.HForkBlock}, - {name: "l2Block", block: c.L2Block}, + // {name: "l2Block", block: c.L2Block}, } { if lastFork.name != "" { // Next one must be higher number @@ -721,7 +721,7 @@ func (c *ChainConfig) DisableGingerbread() *ChainConfig { c.GingerbreadP2Block = nil // Since gingerbread is disabled disable following forks as well c.HForkBlock = nil - c.L2Block = nil + // c.L2Block = nil return c } diff --git a/test/node.go b/test/node.go index 74e3f9b879..44de0f6f97 100644 --- a/test/node.go +++ b/test/node.go @@ -344,7 +344,7 @@ func AccountConfig(numValidators, numExternal int) *env.AccountsConfig { // NOTE: Do not edit the Istanbul field of the returned genesis config it will // be overwritten with the corresponding config from the Istanbul field of the // returned eth config. -func BuildConfig(accounts *env.AccountsConfig, gingerbreadBlock *big.Int) (*genesis.Config, *ethconfig.Config, error) { +func BuildConfig(accounts *env.AccountsConfig, gingerbreadBlock, l2MigrationBlock *big.Int) (*genesis.Config, *ethconfig.Config, error) { gc, err := genesis.CreateCommonGenesisConfig( big.NewInt(1), accounts.AdminAccount().Address, @@ -361,6 +361,7 @@ func BuildConfig(accounts *env.AccountsConfig, gingerbreadBlock *big.Int) (*gene // original. ec := ð.Config{} err = copyObject(BaseEthConfig, ec) + ec.L2Fork = l2MigrationBlock return gc, ec, err } From 44ef6aaf8cd286bb8ba37bdd05cae7a965ebff6d Mon Sep 17 00:00:00 2001 From: alecps Date: Wed, 7 Aug 2024 01:29:35 -0400 Subject: [PATCH 04/21] saving progress --- cmd/geth/main.go | 24 +---------- cmd/utils/flags.go | 11 +++-- consensus/istanbul/backend/backend.go | 11 +++-- core/blockchain.go | 15 ++++--- core/genesis.go | 6 +-- e2e_test/e2e_test.go | 9 ++-- eth/backend.go | 6 +-- eth/ethconfig/config.go | 4 +- eth/handler.go | 61 ++++++++++++++++++++++++--- les/client.go | 2 +- params/config.go | 15 +++---- test/node.go | 2 +- 12 files changed, 94 insertions(+), 72 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index ac555a567d..e4ae7fc367 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -74,7 +74,7 @@ var ( utils.USBFlag, // utils.SmartCardDaemonPathFlag, utils.OverrideHForkFlag, - utils.L2ForkFlag, + utils.L2MigrationBlockFlag, utils.TxPoolLocalsFlag, utils.TxPoolNoLocalsFlag, utils.TxPoolJournalFlag, @@ -390,28 +390,6 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) { } }() - // if ctx.GlobalIsSet(utils.L2ForkFlag.Name) { - // go func() { - // sub := stack.EventMux().Subscribe(ethCore.ChainHeadEvent{}) - // defer sub.Unsubscribe() - // for { - // event := <-sub.Chan() - // if event == nil { - // continue - // } - // var latest *types.Header - // if done, ok := event.Data.(ethCore.ChainHeadEvent); ok { - // latest = done.Block.Header() - // } - - // if ctx.GlobalUint64(utils.L2ForkFlag.Name) >= latest.Number.Uint64() { - // log.Info("L2 Migration Block Reached", "latest", latest.Number.Uint64(), "latesthash", latest.Hash()) - // stack.Close() - // } - // } - // }() - // } - // Spawn a standalone goroutine for status synchronization monitoring, // close the node when synchronization is complete if user required. if ctx.GlobalBool(utils.ExitWhenSyncedFlag.Name) { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index c107ee678a..2638efc2f0 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -243,10 +243,9 @@ var ( Usage: "Manually specify the hfork block, overriding the bundled setting", } - // Celo L2 Migration Flags - L2ForkFlag = cli.Uint64Flag{ - Name: "l2.fork", - Usage: "Block number at which to halt the network for Celo L2 migration", + L2MigrationBlockFlag = cli.Uint64Flag{ + Name: "l2migrationblock", + Usage: "Block number at which to halt the network for Celo L2 migration. Last block of Celo as an L1.", } BloomFilterSizeFlag = cli.Uint64Flag{ @@ -1729,8 +1728,8 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc)) godebug.SetGCPercent(int(gogc)) - if ctx.GlobalIsSet(L2ForkFlag.Name) { - cfg.L2Fork = new(big.Int).SetUint64(ctx.GlobalUint64(L2ForkFlag.Name)) + if ctx.GlobalIsSet(L2MigrationBlockFlag.Name) { + cfg.L2MigrationBlock = new(big.Int).SetUint64(ctx.GlobalUint64(L2MigrationBlockFlag.Name)) } if ctx.GlobalIsSet(SyncModeFlag.Name) { cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode) diff --git a/consensus/istanbul/backend/backend.go b/consensus/istanbul/backend/backend.go index ea6dee75a3..202b1a0ddf 100644 --- a/consensus/istanbul/backend/backend.go +++ b/consensus/istanbul/backend/backend.go @@ -555,10 +555,13 @@ func (sb *Backend) Commit(proposal istanbul.Proposal, aggregatedSeal types.Istan } sb.onNewConsensusBlock(block, result.Receipts, result.Logs, result.State) - if sb.chain.Config().IsL2(block.Number()) { - sb.logger.Info("L2 hard fork reached, stopping the backend") - sb.Close() - //sb.core.Stop() + if sb.chain.Config().IsL2Migration(block.Number()) { + sb.logger.Info("L2 migration block reached, stopping the backend") + + // sb.StopAnnouncing() + // sb.StopValidating() + // sb.Close() + // sb.core.Stop() causes deadlock istanbul/core/handler.go:Stop } return nil } diff --git a/core/blockchain.go b/core/blockchain.go index 8215eca5c6..4806e86359 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -811,12 +811,6 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { // // Note, this function assumes that the `mu` mutex is held! func (bc *BlockChain) writeHeadBlock(block *types.Block) { - if bc.Config().IsL2(block.Number()) { - log.Info("L2 hard fork reached, stopping the blockchain") - bc.StopInsert() - // bc.Stop() - bc.Engine().Close() - } // If the block is on a side chain or an unknown one, force other heads onto it too updateHeads := rawdb.ReadCanonicalHash(bc.db, block.NumberU64()) != block.Hash() @@ -843,6 +837,15 @@ func (bc *BlockChain) writeHeadBlock(block *types.Block) { } bc.currentBlock.Store(block) headBlockGauge.Update(int64(block.NumberU64())) + + if bc.Config().IsL2Migration(block.Number()) { + log.Info("L2 migration block reached, stopping the blockchain") + bc.StopInsert() + bc.chainHeadFeed.Send(ChainHeadEvent{Block: block}) + // bc.chainmu.Unlock() + // bc.Stop() + // bc.Engine().Close() can be called from backend Commit + } } // Genesis retrieves the chain's genesis block. diff --git a/core/genesis.go b/core/genesis.go index 3e0baa4e89..218dec9ec2 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -151,7 +151,7 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig return SetupGenesisBlockWithOverride(db, genesis, nil, nil) } -func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideHFork, l2Fork *big.Int) (*params.ChainConfig, common.Hash, error) { +func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideHFork, l2MigrationBlock *big.Int) (*params.ChainConfig, common.Hash, error) { if genesis != nil && (genesis.Config == nil || genesis.Config.Istanbul == nil) { return params.MainnetChainConfig, common.Hash{}, errGenesisNoConfig } @@ -207,8 +207,8 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override if overrideHFork != nil { newcfg.HForkBlock = overrideHFork } - // if l2Fork != nil { - // newcfg.L2Block = l2Fork + // if l2MigrationBlock != nil { + // newcfg.L2MigrationBlock = l2MigrationBlock // } if err := newcfg.CheckConfigForkOrder(); err != nil { return newcfg, common.Hash{}, err diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index 6fe45379e8..1a657cf784 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -373,7 +373,7 @@ func TestStartStopValidators(t *testing.T) { func TestStopNetworkAtL2Block(t *testing.T) { ac := test.AccountConfig(3, 2) gingerbreadBlock := common.Big0 - l2Block := big.NewInt(2) + l2Block := big.NewInt(3) gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, l2Block) require.NoError(t, err) network, _, err := test.NewNetwork(ac, gc, ec) @@ -390,16 +390,13 @@ func TestStopNetworkAtL2Block(t *testing.T) { } }() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*200) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*400) defer cancel() err = network.AwaitBlock(ctx, l2Block.Uint64()) require.NoError(t, err) - // err = network.AwaitBlock(ctx, l2Block.Uint64()) - // require.NoError(t, err) - - shortCtx, cancel := context.WithTimeout(context.Background(), time.Second*3) + shortCtx, cancel := context.WithTimeout(context.Background(), time.Second*2) defer cancel() err = network.AwaitBlock(shortCtx, l2Block.Uint64()+1) diff --git a/eth/backend.go b/eth/backend.go index eba1accd7b..93388f9240 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -131,15 +131,15 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if err != nil { return nil, err } - chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideHFork, config.L2Fork) + chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideHFork, config.L2MigrationBlock) if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { return nil, genesisErr } log.Info("Initialised chain configuration", "config", chainConfig) chainConfig.FullHeaderChainAvailable = config.SyncMode.SyncFullHeaderChain() - if config.L2Fork != nil { - chainConfig.L2Block = config.L2Fork + if config.L2MigrationBlock != nil { + chainConfig.L2MigrationBlock = config.L2MigrationBlock } if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb, stack.ResolvePath(config.TrieCleanCacheJournal)); err != nil { diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index d64f94df62..e1431e964a 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -165,8 +165,8 @@ type Config struct { // HFork block override (TODO: remove after the fork) OverrideHFork *big.Int `toml:",omitempty"` - // l2 fork block - L2Fork *big.Int `toml:",omitempty"` + // l2 migration block, last block of l1 before l2 migration + L2MigrationBlock *big.Int `toml:",omitempty"` // The minimum required peers in order for syncing to be initiated, if left // at 0 then the default will be used. diff --git a/eth/handler.go b/eth/handler.go index d9d42761ab..e3fb63b27b 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -114,10 +114,12 @@ type handler struct { txFetcher *fetcher.TxFetcher peers *peerSet - eventMux *event.TypeMux - txsCh chan core.NewTxsEvent - txsSub event.Subscription - minedBlockSub *event.TypeMuxSubscription + eventMux *event.TypeMux + txsCh chan core.NewTxsEvent + txsSub event.Subscription + minedBlockSub *event.TypeMuxSubscription + newChainHeadCh chan core.ChainHeadEvent + newChainHeadSub event.Subscription whitelist map[uint64]common.Hash @@ -229,6 +231,12 @@ func newHandler(config *handlerConfig) (*handler, error) { if err == nil { atomic.StoreUint32(&h.acceptTxs, 1) // Mark initial sync done on any fetcher import } + + if h.chain.CurrentBlock().Number().Cmp(h.chain.Config().L2MigrationBlock) == 0 { + log.Info("L2 migration block reached, stopping sync", "number", h.chain.CurrentBlock().NumberU64(), "hash", h.chain.CurrentBlock().Hash()) + // h.Stop() + } + return n, err } h.blockFetcher = fetcher.NewBlockFetcher(false, nil, h.chain.GetBlockByHash, validator, h.BroadcastBlock, heighter, nil, inserter, h.removePeer) @@ -467,15 +475,27 @@ func (h *handler) Start(maxPeers int) { // start sync handlers h.wg.Add(1) go h.chainSync.loop() + + if h.chain.Config().L2MigrationBlock.Uint64() > 0 { + h.wg.Add(1) + h.newChainHeadCh = make(chan core.ChainHeadEvent, 10) + h.newChainHeadSub = h.chain.SubscribeChainHeadEvent(h.newChainHeadCh) + go h.l2MigrationLoop() + } } func (h *handler) Stop() { - h.txsSub.Unsubscribe() // quits txBroadcastLoop - h.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop + h.txsSub.Unsubscribe() // quits txBroadcastLoop + h.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop + h.newChainHeadSub.Unsubscribe() // quits l2MigrationLoop // Quit chainSync and txsync64. // After this is done, no new peers will be accepted. - close(h.quitSync) + select { + case <-h.quitSync: + default: + close(h.quitSync) + } h.wg.Wait() // Disconnect existing sessions. @@ -593,6 +613,33 @@ func (h *handler) txBroadcastLoop() { } } +func (h *handler) l2MigrationLoop() { + for { + select { + case event := <-h.newChainHeadCh: + block := event.Block + if block.Number().Cmp(h.chain.Config().L2MigrationBlock) == 0 { + log.Info("L2 Migration Block Reached", "block", block.Number().Uint64(), "hash", block.Hash()) + h.wg.Done() + h.Stop() + return + } + case <-h.newChainHeadSub.Err(): + h.wg.Done() + return + } + } + // for obj := range h.newChainHeadSub.Chan() { + // if event, ok := obj.Data.(core.ChainHeadEvent); ok { + // block := event.Block + // if block.Number().Cmp(h.chain.Config().L2MigrationBlock) == 0 { + // log.Info("L2 Migration Block Reached", "block", block.Number().Uint64(), "hash", block.Hash()) + // go h.Stop() + // } + // } + // } +} + func (h *handler) FindPeers(targets map[enode.ID]bool, purpose p2p.PurposeFlag) map[enode.ID]consensus.Peer { m := make(map[enode.ID]consensus.Peer) for _, p := range h.peers.Peers() { diff --git a/les/client.go b/les/client.go index fd8c9e7b1c..46ca417c64 100644 --- a/les/client.go +++ b/les/client.go @@ -104,7 +104,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) { return nil, err } chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, - config.OverrideHFork, config.L2Fork) + config.OverrideHFork, config.L2MigrationBlock) if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat { return nil, genesisErr } diff --git a/params/config.go b/params/config.go index 0a72127bf4..4f2d418ada 100644 --- a/params/config.go +++ b/params/config.go @@ -313,7 +313,7 @@ type ChainConfig struct { GingerbreadBlock *big.Int `json:"gingerbreadBlock,omitempty"` // Gingerbread switch block (nil = no fork, 0 = already activated) GingerbreadP2Block *big.Int `json:"gingerbreadP2Block,omitempty"` // GingerbreadP2 switch block (nil = no fork, 0 = already activated) HForkBlock *big.Int `json:"hforkBlock,omitempty"` // HFork switch block (nil = no fork, 0 = already activated) - L2Block *big.Int `json:"l2Block,omitempty"` // l2 switch block (nil = no fork, 0 = already activated) + L2MigrationBlock *big.Int `json:"l2MigrationBlock,omitempty"` // l2 migration block / last block of l1 (nil = no fork, 0 = already activated) Istanbul *IstanbulConfig `json:"istanbul,omitempty"` // This does not belong here but passing it to every function is not possible since that breaks @@ -465,9 +465,9 @@ func (c *ChainConfig) IsGingerbreadP2(num *big.Int) bool { return isForked(c.GingerbreadP2Block, num) } -// IsL2 returns whether num represents a block number after the Cel2 fork -func (c *ChainConfig) IsL2(num *big.Int) bool { - return isForked(c.L2Block, num) +// IsL2 returns whether num represents a block number greater than or equal to the L2 migration block (last block before L2) +func (c *ChainConfig) IsL2Migration(num *big.Int) bool { + return isForked(c.L2MigrationBlock, num) } func (c *ChainConfig) IsHFork(num *big.Int) bool { @@ -516,7 +516,6 @@ func (c *ChainConfig) CheckConfigForkOrder() error { {name: "gingerbreadBlock", block: c.GingerbreadBlock}, {name: "gingerbreadP2Block", block: c.GingerbreadP2Block}, {name: "hforkBlock", block: c.HForkBlock}, - // {name: "l2Block", block: c.L2Block}, } { if lastFork.name != "" { // Next one must be higher number @@ -600,9 +599,6 @@ func (c *ChainConfig) checkCeloCompatible(newcfg *ChainConfig, head *big.Int) *C if isForkIncompatible(c.HForkBlock, newcfg.HForkBlock, head) { return newCompatError("HFork block", c.HForkBlock, newcfg.HForkBlock) } - if isForkIncompatible(c.L2Block, newcfg.L2Block, head) { - return newCompatError("L2 block", c.L2Block, newcfg.L2Block) - } return nil } @@ -721,7 +717,6 @@ func (c *ChainConfig) DisableGingerbread() *ChainConfig { c.GingerbreadP2Block = nil // Since gingerbread is disabled disable following forks as well c.HForkBlock = nil - // c.L2Block = nil return c } @@ -745,7 +740,7 @@ func (c *ChainConfig) DeepCopy() *ChainConfig { GingerbreadBlock: copyBigIntOrNil(c.GingerbreadBlock), GingerbreadP2Block: copyBigIntOrNil(c.GingerbreadP2Block), HForkBlock: copyBigIntOrNil(c.HForkBlock), - L2Block: copyBigIntOrNil(c.L2Block), + L2MigrationBlock: copyBigIntOrNil(c.L2MigrationBlock), Istanbul: &IstanbulConfig{ Epoch: c.Istanbul.Epoch, diff --git a/test/node.go b/test/node.go index 44de0f6f97..6242f10efb 100644 --- a/test/node.go +++ b/test/node.go @@ -361,7 +361,7 @@ func BuildConfig(accounts *env.AccountsConfig, gingerbreadBlock, l2MigrationBloc // original. ec := ð.Config{} err = copyObject(BaseEthConfig, ec) - ec.L2Fork = l2MigrationBlock + ec.L2MigrationBlock = l2MigrationBlock return gc, ec, err } From a74427b9b270272c975f05fcf4f94e50af192a95 Mon Sep 17 00:00:00 2001 From: alecps Date: Wed, 7 Aug 2024 17:42:56 -0400 Subject: [PATCH 05/21] stop p2p server, misc improvements for clean shutdown --- consensus/istanbul/backend/backend.go | 9 +++---- core/blockchain.go | 7 +++--- e2e_test/e2e_test.go | 15 ++++-------- eth/fetcher/block_fetcher.go | 7 +++--- eth/fetcher/tx_fetcher.go | 6 ++--- eth/handler.go | 34 +++++++++------------------ miner/worker.go | 10 ++++++++ 7 files changed, 38 insertions(+), 50 deletions(-) diff --git a/consensus/istanbul/backend/backend.go b/consensus/istanbul/backend/backend.go index 202b1a0ddf..22234c4216 100644 --- a/consensus/istanbul/backend/backend.go +++ b/consensus/istanbul/backend/backend.go @@ -556,12 +556,9 @@ func (sb *Backend) Commit(proposal istanbul.Proposal, aggregatedSeal types.Istan sb.onNewConsensusBlock(block, result.Receipts, result.Logs, result.State) if sb.chain.Config().IsL2Migration(block.Number()) { - sb.logger.Info("L2 migration block reached, stopping the backend") - - // sb.StopAnnouncing() - // sb.StopValidating() - // sb.Close() - // sb.core.Stop() causes deadlock istanbul/core/handler.go:Stop + sb.logger.Info("L2 migration block reached, closing istanbul backend", "block", block.NumberU64(), "hash", block.Hash()) + sb.StopAnnouncing() + sb.Close() } return nil } diff --git a/core/blockchain.go b/core/blockchain.go index 4806e86359..8c4b1c2dad 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -839,12 +839,11 @@ func (bc *BlockChain) writeHeadBlock(block *types.Block) { headBlockGauge.Update(int64(block.NumberU64())) if bc.Config().IsL2Migration(block.Number()) { - log.Info("L2 migration block reached, stopping the blockchain") + log.Info("L2 migration block reached, stopping block insertion", "block", block.NumberU64(), "hash", block.Hash()) bc.StopInsert() + // The eth handler has a thread that listens for chain head events and stops the eth handler when the l2 migration block is reached. + // This stops the node from syncing further blocks. bc.chainHeadFeed.Send(ChainHeadEvent{Block: block}) - // bc.chainmu.Unlock() - // bc.Stop() - // bc.Engine().Close() can be called from backend Commit } } diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index 1a657cf784..3c507ec86b 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -376,21 +376,14 @@ func TestStopNetworkAtL2Block(t *testing.T) { l2Block := big.NewInt(3) gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, l2Block) require.NoError(t, err) - network, _, err := test.NewNetwork(ac, gc, ec) + network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) - - // We define our own shutdown function because we don't want to print - // errors about already stopped nodes. Since this test can fail while we - // have stopped nodes. defer func() { - for _, err := range network.Shutdown() { - if !errors.Is(err, test.ErrTrackerAlreadyStopped) && !errors.Is(err, node.ErrNodeStopped) { - fmt.Println(err.Error()) - } - } + log.Info("Shutting down network from e2e test") + shutdown() }() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*400) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*4000) defer cancel() err = network.AwaitBlock(ctx, l2Block.Uint64()) diff --git a/eth/fetcher/block_fetcher.go b/eth/fetcher/block_fetcher.go index 0b19b42d7d..64e41a2365 100644 --- a/eth/fetcher/block_fetcher.go +++ b/eth/fetcher/block_fetcher.go @@ -64,7 +64,8 @@ var ( bodyFilterOutMeter = metrics.NewRegisteredMeter("eth/fetcher/block/filter/bodies/out", nil) ) -var errTerminated = errors.New("terminated") +// ErrTerminated indicates that the fetcher's peer connection has been terminated. +var ErrTerminated = errors.New("terminated") // HeaderRetrievalFn is a callback type for retrieving a header from the local chain. type HeaderRetrievalFn func(common.Hash) *types.Header @@ -254,7 +255,7 @@ func (f *BlockFetcher) Notify(peer string, hash common.Hash, number uint64, time case f.notify <- block: return nil case <-f.quit: - return errTerminated + return ErrTerminated } } @@ -268,7 +269,7 @@ func (f *BlockFetcher) Enqueue(peer string, block *types.Block) error { case f.inject <- op: return nil case <-f.quit: - return errTerminated + return ErrTerminated } } diff --git a/eth/fetcher/tx_fetcher.go b/eth/fetcher/tx_fetcher.go index b9f074a150..79e3da8e43 100644 --- a/eth/fetcher/tx_fetcher.go +++ b/eth/fetcher/tx_fetcher.go @@ -252,7 +252,7 @@ func (f *TxFetcher) Notify(peer string, hashes []common.Hash) error { case f.notify <- announce: return nil case <-f.quit: - return errTerminated + return ErrTerminated } } @@ -316,7 +316,7 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool) case f.cleanup <- &txDelivery{origin: peer, hashes: added, direct: direct}: return nil case <-f.quit: - return errTerminated + return ErrTerminated } } @@ -327,7 +327,7 @@ func (f *TxFetcher) Drop(peer string) error { case f.drop <- &txDrop{peer: peer}: return nil case <-f.quit: - return errTerminated + return ErrTerminated } } diff --git a/eth/handler.go b/eth/handler.go index e3fb63b27b..c69dc969b5 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -232,11 +232,6 @@ func newHandler(config *handlerConfig) (*handler, error) { atomic.StoreUint32(&h.acceptTxs, 1) // Mark initial sync done on any fetcher import } - if h.chain.CurrentBlock().Number().Cmp(h.chain.Config().L2MigrationBlock) == 0 { - log.Info("L2 migration block reached, stopping sync", "number", h.chain.CurrentBlock().NumberU64(), "hash", h.chain.CurrentBlock().Hash()) - // h.Stop() - } - return n, err } h.blockFetcher = fetcher.NewBlockFetcher(false, nil, h.chain.GetBlockByHash, validator, h.BroadcastBlock, heighter, nil, inserter, h.removePeer) @@ -446,7 +441,9 @@ func (h *handler) unregisterPeer(id string) *ethPeer { log.Error("Peer removal from downloader failed", "peer", id, "err", err) } if err := h.txFetcher.Drop(id); err != nil { - log.Error("Peer removal from tx fetcher failed", "peer", id, "err", err) + if !errors.Is(err, fetcher.ErrTerminated) { + log.Error("Peer removal from tx fetcher failed", "peer", id, "err", err) + } } if handler, ok := h.chain.Engine().(consensus.Handler); ok { handler.UnregisterPeer(peer, peer.Peer.Server == h.proxyServer) @@ -476,12 +473,11 @@ func (h *handler) Start(maxPeers int) { h.wg.Add(1) go h.chainSync.loop() - if h.chain.Config().L2MigrationBlock.Uint64() > 0 { - h.wg.Add(1) - h.newChainHeadCh = make(chan core.ChainHeadEvent, 10) - h.newChainHeadSub = h.chain.SubscribeChainHeadEvent(h.newChainHeadCh) - go h.l2MigrationLoop() - } + // Listen for L2 migration block + h.wg.Add(1) + h.newChainHeadCh = make(chan core.ChainHeadEvent, 10) + h.newChainHeadSub = h.chain.SubscribeChainHeadEvent(h.newChainHeadCh) + go h.l2MigrationLoop() } func (h *handler) Stop() { @@ -618,10 +614,11 @@ func (h *handler) l2MigrationLoop() { select { case event := <-h.newChainHeadCh: block := event.Block - if block.Number().Cmp(h.chain.Config().L2MigrationBlock) == 0 { - log.Info("L2 Migration Block Reached", "block", block.Number().Uint64(), "hash", block.Hash()) + if h.chain.Config().IsL2Migration(block.Number()) { + log.Info("L2 Migration Block Reached, stopping handler and p2p server", "block", block.NumberU64(), "hash", block.Hash()) h.wg.Done() h.Stop() + h.server.Stop() return } case <-h.newChainHeadSub.Err(): @@ -629,15 +626,6 @@ func (h *handler) l2MigrationLoop() { return } } - // for obj := range h.newChainHeadSub.Chan() { - // if event, ok := obj.Data.(core.ChainHeadEvent); ok { - // block := event.Block - // if block.Number().Cmp(h.chain.Config().L2MigrationBlock) == 0 { - // log.Info("L2 Migration Block Reached", "block", block.Number().Uint64(), "hash", block.Hash()) - // go h.Stop() - // } - // } - // } } func (h *handler) FindPeers(targets map[enode.ID]bool, purpose p2p.PurposeFlag) map[enode.ID]consensus.Peer { diff --git a/miner/worker.go b/miner/worker.go index 80fd24f62d..2f3736d161 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -400,6 +400,16 @@ func (w *worker) mainLoop() { taskCtx, cancel = context.WithCancel(context.Background()) wg.Add(1) + if w.chainConfig.IsL2Migration(w.chain.CurrentBlock().Number()) { + if w.isRunning() { + log.Info("L2 Migration block reached, stopping block construction", "block", w.chain.CurrentBlock().NumberU64(), "hash", w.chain.CurrentBlock().Hash()) + w.stop() + } + wg.Done() + cancel() + return + } + if w.isRunning() { // engine.NewWork posts the FinalCommitted Event to IBFT to signal the start of the next round if h, ok := w.engine.(consensus.Handler); ok { From 1b5b609b1bed9cb68678d4771bd357b703468526 Mon Sep 17 00:00:00 2001 From: alecps Date: Wed, 7 Aug 2024 18:00:31 -0400 Subject: [PATCH 06/21] cleanup --- cmd/geth/main.go | 4 ++-- cmd/utils/cmd.go | 2 +- consensus/istanbul/backend/backend.go | 5 ----- consensus/istanbul/core/handler.go | 2 +- consensus/istanbul/core/prepare.go | 4 ---- core/blockchain.go | 2 +- core/error.go | 3 --- core/genesis.go | 7 ++----- e2e_test/e2e_test.go | 1 - eth/backend.go | 4 ++-- eth/handler.go | 1 - les/client.go | 2 +- node/node.go | 2 +- 13 files changed, 11 insertions(+), 28 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index e4ae7fc367..531567eba6 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -320,7 +320,7 @@ func prepare(ctx *cli.Context) { // geth is the main entry point into the system if no special subcommand is ran. // It creates a default node based on the command line arguments and runs it in // blocking mode, waiting for it to be shut down. -func geth(ctx *cli.Context) error { // TODO(Alec) code pointer +func geth(ctx *cli.Context) error { if args := ctx.Args(); len(args) > 0 { return fmt.Errorf("invalid command: %q", args[0]) } @@ -415,7 +415,7 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) { if timestamp := time.Unix(int64(latest.Time), 0); time.Since(timestamp) < 10*time.Minute { log.Info("Synchronisation completed", "latestnum", latest.Number, "latesthash", latest.Hash(), "age", common.PrettyAge(timestamp)) - stack.Close() // TODO(Alec) code pointer + stack.Close() } } }() diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index ed1bf487ff..06d296801a 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -66,7 +66,7 @@ func Fatalf(format string, args ...interface{}) { os.Exit(1) } -func StartNode(ctx *cli.Context, stack *node.Node) { // TODO(Alec) code pointer +func StartNode(ctx *cli.Context, stack *node.Node) { if err := stack.Start(); err != nil { Fatalf("Error starting protocol stack: %v", err) } diff --git a/consensus/istanbul/backend/backend.go b/consensus/istanbul/backend/backend.go index 22234c4216..3000562ea8 100644 --- a/consensus/istanbul/backend/backend.go +++ b/consensus/istanbul/backend/backend.go @@ -577,11 +577,6 @@ func (sb *Backend) Verify(proposal istanbul.Proposal) (*istanbulCore.StateProces return nil, 0, errInvalidProposal } - // Don't verify blocks after the L2 hard fork - // if sb.chain.Config().IsL2(block.Number()) { - // return nil, 0, core.ErrPostL2BlockNumber - // } - // check bad block if sb.hasBadProposal(block.Hash()) { return nil, 0, core.ErrBannedHash diff --git a/consensus/istanbul/core/handler.go b/consensus/istanbul/core/handler.go index dbcb09e7b1..a591fb217a 100644 --- a/consensus/istanbul/core/handler.go +++ b/consensus/istanbul/core/handler.go @@ -59,7 +59,7 @@ func (c *core) Start() error { } // Stop implements core.Engine.Stop -func (c *core) Stop() error { // TODO(Alec) code pointer +func (c *core) Stop() error { c.stopAllTimers() c.unsubscribeEvents() diff --git a/consensus/istanbul/core/prepare.go b/consensus/istanbul/core/prepare.go index 09c22e2e66..be58883190 100644 --- a/consensus/istanbul/core/prepare.go +++ b/consensus/istanbul/core/prepare.go @@ -23,7 +23,6 @@ import ( "github.com/celo-org/celo-blockchain/common" "github.com/celo-org/celo-blockchain/consensus/istanbul" - celoCore "github.com/celo-org/celo-blockchain/core" ) func (c *core) sendPrepare() { @@ -110,9 +109,6 @@ func (c *core) verifyPreparedCertificate(preparedCertificate istanbul.PreparedCe func (c *core) verifyProposalAndPCMessages(proposal istanbul.Proposal, pCMessages []istanbul.Message) (*istanbul.View, error) { // Validate the attached proposal if _, err := c.verifyProposal(proposal); err != nil { - if err == celoCore.ErrPostL2BlockNumber { - return nil, err - } return nil, errInvalidPreparedCertificateProposal } diff --git a/core/blockchain.go b/core/blockchain.go index 8c4b1c2dad..0a55b8f30e 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1026,7 +1026,7 @@ func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) { // Stop stops the blockchain service. If any imports are currently in progress // it will abort them using the procInterrupt. -func (bc *BlockChain) Stop() { // TODO(Alec) code pointer +func (bc *BlockChain) Stop() { if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) { return } diff --git a/core/error.go b/core/error.go index fdb58bbb21..f5515dd858 100644 --- a/core/error.go +++ b/core/error.go @@ -131,7 +131,4 @@ var ( // ErrDenominatedLowMaxFee is returned when a celo denominated transaction, with the current exchange rate, // the MaxFeeInFeeCurrency cannot cover the tx.Fee() ErrDenominatedLowMaxFee = errors.New("CELO denominated tx MaxFeeInCurrency cannot cover gas fee costs") - - // ErrPostL2BlockNumber is returned when the block number is after the L2 migration block - ErrPostL2BlockNumber = errors.New("Block number is after the L2 migration block") ) diff --git a/core/genesis.go b/core/genesis.go index 218dec9ec2..34a0731ea3 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -148,10 +148,10 @@ func (e *GenesisMismatchError) Error() string { // // The returned chain configuration is never nil. func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) { - return SetupGenesisBlockWithOverride(db, genesis, nil, nil) + return SetupGenesisBlockWithOverride(db, genesis, nil) } -func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideHFork, l2MigrationBlock *big.Int) (*params.ChainConfig, common.Hash, error) { +func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideHFork *big.Int) (*params.ChainConfig, common.Hash, error) { if genesis != nil && (genesis.Config == nil || genesis.Config.Istanbul == nil) { return params.MainnetChainConfig, common.Hash{}, errGenesisNoConfig } @@ -207,9 +207,6 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override if overrideHFork != nil { newcfg.HForkBlock = overrideHFork } - // if l2MigrationBlock != nil { - // newcfg.L2MigrationBlock = l2MigrationBlock - // } if err := newcfg.CheckConfigForkOrder(); err != nil { return newcfg, common.Hash{}, err } diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index 3c507ec86b..ba0b36835b 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -369,7 +369,6 @@ func TestStartStopValidators(t *testing.T) { } -// TODO(Alec) func TestStopNetworkAtL2Block(t *testing.T) { ac := test.AccountConfig(3, 2) gingerbreadBlock := common.Big0 diff --git a/eth/backend.go b/eth/backend.go index 93388f9240..4d6f1e7eb0 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -131,7 +131,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if err != nil { return nil, err } - chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideHFork, config.L2MigrationBlock) + chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideHFork) if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { return nil, genesisErr } @@ -604,7 +604,7 @@ func (s *Ethereum) Start() error { // Stop implements node.Lifecycle, terminating all internal goroutines used by the // Ethereum protocol. -func (s *Ethereum) Stop() error { // TODO(Alec) code pointer +func (s *Ethereum) Stop() error { // Stop all the peer-related stuff first. s.stopAnnounce() s.ethDialCandidates.Close() diff --git a/eth/handler.go b/eth/handler.go index c69dc969b5..e32d72608c 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -231,7 +231,6 @@ func newHandler(config *handlerConfig) (*handler, error) { if err == nil { atomic.StoreUint32(&h.acceptTxs, 1) // Mark initial sync done on any fetcher import } - return n, err } h.blockFetcher = fetcher.NewBlockFetcher(false, nil, h.chain.GetBlockByHash, validator, h.BroadcastBlock, heighter, nil, inserter, h.removePeer) diff --git a/les/client.go b/les/client.go index 46ca417c64..30ed058af4 100644 --- a/les/client.go +++ b/les/client.go @@ -104,7 +104,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) { return nil, err } chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, - config.OverrideHFork, config.L2MigrationBlock) + config.OverrideHFork) if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat { return nil, genesisErr } diff --git a/node/node.go b/node/node.go index 1f689a9117..a05209dd02 100644 --- a/node/node.go +++ b/node/node.go @@ -215,7 +215,7 @@ func (n *Node) Start() error { // Close stops the Node and releases resources acquired in // Node constructor New. -func (n *Node) Close() error { // TODO(Alec) code pointer +func (n *Node) Close() error { n.startStopLock.Lock() defer n.startStopLock.Unlock() From a6315664568822d926904e77edac109d462e8679 Mon Sep 17 00:00:00 2001 From: alecps Date: Thu, 8 Aug 2024 14:34:02 -0400 Subject: [PATCH 07/21] add comment about defer in eth/handler.go --- eth/handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/handler.go b/eth/handler.go index e32d72608c..84bf8ea045 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -615,7 +615,7 @@ func (h *handler) l2MigrationLoop() { block := event.Block if h.chain.Config().IsL2Migration(block.Number()) { log.Info("L2 Migration Block Reached, stopping handler and p2p server", "block", block.NumberU64(), "hash", block.Hash()) - h.wg.Done() + h.wg.Done() // we don't use 'defer' here because we want to decrement the wait group before calling h.Stop(), otherwise we get a deadlock h.Stop() h.server.Stop() return From c1fe248f48a4980db00e3d1e790a588bbe7ff2cd Mon Sep 17 00:00:00 2001 From: alecps Date: Thu, 8 Aug 2024 20:46:25 -0400 Subject: [PATCH 08/21] misc edits from testing with command line, adds outer cancel from geth command, fixes forkid issue --- cmd/geth/main.go | 30 ++++++++++++++++++++++++++++++ cmd/utils/flags.go | 2 +- core/blockchain.go | 6 +++++- core/forkid/forkid.go | 4 ++++ e2e_test/e2e_test.go | 7 ++++--- miner/worker.go | 11 +++++++---- params/config.go | 4 ++-- 7 files changed, 53 insertions(+), 11 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 531567eba6..1f7318c1a2 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -30,6 +30,7 @@ import ( "github.com/celo-org/celo-blockchain/cmd/utils" "github.com/celo-org/celo-blockchain/common" "github.com/celo-org/celo-blockchain/console/prompt" + ethCore "github.com/celo-org/celo-blockchain/core" "github.com/celo-org/celo-blockchain/core/types" "github.com/celo-org/celo-blockchain/eth" "github.com/celo-org/celo-blockchain/eth/downloader" @@ -327,6 +328,11 @@ func geth(ctx *cli.Context) error { prepare(ctx) stack, backend := makeFullNode(ctx) + + if backend.ChainConfig().IsL2Migration(backend.CurrentBlock().Number()) { + return fmt.Errorf("Attempted to start node with an l2 migration block that has already been reached. latestBlock: %d, hash: %s", backend.CurrentBlock().NumberU64(), backend.CurrentBlock().Hash()) + } + defer stack.Close() startNode(ctx, stack, backend) @@ -421,6 +427,30 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) { }() } + // Spawn a standalone goroutine to monitor the chain head for the l2 migration block. + // When the l2 migration block is reached, close the entire stack. + if ctx.GlobalIsSet(utils.L2MigrationBlockFlag.Name) && ctx.GlobalUint64(utils.L2MigrationBlockFlag.Name) > 0 { + go func() { + chainHeadCh := make(chan ethCore.ChainHeadEvent, 10) + chainHeadSub := backend.SubscribeChainHeadEvent(chainHeadCh) + defer chainHeadSub.Unsubscribe() + for { + select { + case chainHeadEvent := <-chainHeadCh: + block := chainHeadEvent.Block + if backend.ChainConfig().IsL2Migration(block.Number()) { + log.Info("L2 Migration Block Reached, closing entire stack from geth cmd", "block", block.NumberU64(), "hash", block.Hash()) + stack.Close() + return + } + case err := <-chainHeadSub.Err(): + log.Error("Error in outer subscription to blockchain's chainhead event listening for l2 migration block", "err", err) + return + } + } + }() + } + isFullNode := ctx.GlobalString(utils.SyncModeFlag.Name) == "full" || ctx.GlobalString(utils.SyncModeFlag.Name) == "fast" // Miners and proxies only makes sense if a full node is running if ctx.GlobalBool(utils.ProxyFlag.Name) || ctx.GlobalBool(utils.MiningEnabledFlag.Name) || ctx.GlobalBool(utils.DeveloperFlag.Name) { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 2638efc2f0..5471684b31 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -245,7 +245,7 @@ var ( L2MigrationBlockFlag = cli.Uint64Flag{ Name: "l2migrationblock", - Usage: "Block number at which to halt the network for Celo L2 migration. Last block of Celo as an L1.", + Usage: "Block number at which to halt the network for Celo L2 migration. Last block of Celo as an L1. If unset or set to 0, no halt will occur.", } BloomFilterSizeFlag = cli.Uint64Flag{ diff --git a/core/blockchain.go b/core/blockchain.go index 0a55b8f30e..ef2dbbea11 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -811,6 +811,10 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { // // Note, this function assumes that the `mu` mutex is held! func (bc *BlockChain) writeHeadBlock(block *types.Block) { + if bc.Config().IsL2Migration(new(big.Int).Sub(block.Number(), big.NewInt(1))) { + bc.StopInsert() // Just for good measure + log.Crit("Attempt to insert block beyond L2 migration block. This should never happen.", "block", block.NumberU64(), "hash", block.Hash()) + } // If the block is on a side chain or an unknown one, force other heads onto it too updateHeads := rawdb.ReadCanonicalHash(bc.db, block.NumberU64()) != block.Hash() @@ -1093,7 +1097,7 @@ func (bc *BlockChain) Stop() { triedb := bc.stateCache.TrieDB() triedb.SaveCache(bc.cacheConfig.TrieCleanJournal) } - log.Info("Blockchain stopped") + log.Info("Blockchain stopped", "number", bc.CurrentBlock().NumberU64(), "hash", bc.CurrentBlock().Hash()) } // StopInsert interrupts all insertion methods, causing them to return diff --git a/core/forkid/forkid.go b/core/forkid/forkid.go index 4f4628aca9..977adf80ec 100644 --- a/core/forkid/forkid.go +++ b/core/forkid/forkid.go @@ -224,6 +224,10 @@ func gatherForks(config *params.ChainConfig) []uint64 { if !strings.HasSuffix(field.Name, "Block") { continue } + // Do not include L2MigrationBlock in forkid as doing so will prevent syncing with nodes that have not set the L2MigrationBlock flag + if field.Name == "L2MigrationBlock" { + continue + } if field.Type != reflect.TypeOf(new(big.Int)) { continue } diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index ba0b36835b..1bb631161f 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "math/big" + "os" "os/exec" "strings" "sync" @@ -34,10 +35,10 @@ func init() { // This statement is commented out but left here since its very useful for // debugging problems and its non trivial to construct. // - // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stdout, log.TerminalFormat(true)))) + log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stdout, log.TerminalFormat(true)))) // This disables all logging which in general we want, because there is a lot - log.Root().SetHandler(log.DiscardHandler()) + // log.Root().SetHandler(log.DiscardHandler()) } // This test starts a network submits a transaction and waits for the whole @@ -382,7 +383,7 @@ func TestStopNetworkAtL2Block(t *testing.T) { shutdown() }() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*4000) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*200) defer cancel() err = network.AwaitBlock(ctx, l2Block.Uint64()) diff --git a/miner/worker.go b/miner/worker.go index 2f3736d161..13e550d094 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -24,6 +24,7 @@ import ( "github.com/celo-org/celo-blockchain/common" "github.com/celo-org/celo-blockchain/consensus" + istanbulBackend "github.com/celo-org/celo-blockchain/consensus/istanbul/backend" "github.com/celo-org/celo-blockchain/core" "github.com/celo-org/celo-blockchain/core/state" "github.com/celo-org/celo-blockchain/core/types" @@ -239,10 +240,12 @@ func (w *worker) start() { func (w *worker) stop() { atomic.StoreInt32(&w.running, 0) - if istanbul, ok := w.engine.(consensus.Istanbul); ok { - err := istanbul.StopValidating() - if err != nil { - log.Error("Error while calling engine.StopValidating", "err", err) + if istanbul, ok := w.engine.(*istanbulBackend.Backend); ok { + if istanbul.IsValidating() { + err := istanbul.StopValidating() + if err != nil { + log.Error("Error while calling engine.StopValidating", "err", err) + } } } } diff --git a/params/config.go b/params/config.go index 4f2d418ada..febc145d84 100644 --- a/params/config.go +++ b/params/config.go @@ -313,7 +313,7 @@ type ChainConfig struct { GingerbreadBlock *big.Int `json:"gingerbreadBlock,omitempty"` // Gingerbread switch block (nil = no fork, 0 = already activated) GingerbreadP2Block *big.Int `json:"gingerbreadP2Block,omitempty"` // GingerbreadP2 switch block (nil = no fork, 0 = already activated) HForkBlock *big.Int `json:"hforkBlock,omitempty"` // HFork switch block (nil = no fork, 0 = already activated) - L2MigrationBlock *big.Int `json:"l2MigrationBlock,omitempty"` // l2 migration block / last block of l1 (nil = no fork, 0 = already activated) + L2MigrationBlock *big.Int `json:"l2MigrationBlock,omitempty"` // l2 migration block / last block of l1 (nil = no migration, 0 = no migration) Istanbul *IstanbulConfig `json:"istanbul,omitempty"` // This does not belong here but passing it to every function is not possible since that breaks @@ -467,7 +467,7 @@ func (c *ChainConfig) IsGingerbreadP2(num *big.Int) bool { // IsL2 returns whether num represents a block number greater than or equal to the L2 migration block (last block before L2) func (c *ChainConfig) IsL2Migration(num *big.Int) bool { - return isForked(c.L2MigrationBlock, num) + return isForked(c.L2MigrationBlock, num) && c.L2MigrationBlock.Cmp(big.NewInt(0)) > 0 // return false if L2MigrationBlock is nil or 0 } func (c *ChainConfig) IsHFork(num *big.Int) bool { From dca542e2bde8a2be84dad5dd65ab248473cd783a Mon Sep 17 00:00:00 2001 From: alecps Date: Mon, 12 Aug 2024 13:19:15 -0400 Subject: [PATCH 09/21] addresses feedback, makes l2MigrationBlock first block of L2, removes logic to shut down entire node by listening to chain head events --- cmd/geth/main.go | 29 ---------------- cmd/utils/flags.go | 2 +- consensus/istanbul/backend/backend.go | 5 +-- core/blockchain.go | 16 ++++----- e2e_test/e2e_test.go | 4 +-- eth/handler.go | 48 +++++---------------------- miner/worker.go | 24 ++++++++------ params/config.go | 4 +-- 8 files changed, 37 insertions(+), 95 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 1f7318c1a2..9223538eac 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -30,7 +30,6 @@ import ( "github.com/celo-org/celo-blockchain/cmd/utils" "github.com/celo-org/celo-blockchain/common" "github.com/celo-org/celo-blockchain/console/prompt" - ethCore "github.com/celo-org/celo-blockchain/core" "github.com/celo-org/celo-blockchain/core/types" "github.com/celo-org/celo-blockchain/eth" "github.com/celo-org/celo-blockchain/eth/downloader" @@ -329,10 +328,6 @@ func geth(ctx *cli.Context) error { prepare(ctx) stack, backend := makeFullNode(ctx) - if backend.ChainConfig().IsL2Migration(backend.CurrentBlock().Number()) { - return fmt.Errorf("Attempted to start node with an l2 migration block that has already been reached. latestBlock: %d, hash: %s", backend.CurrentBlock().NumberU64(), backend.CurrentBlock().Hash()) - } - defer stack.Close() startNode(ctx, stack, backend) @@ -427,30 +422,6 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) { }() } - // Spawn a standalone goroutine to monitor the chain head for the l2 migration block. - // When the l2 migration block is reached, close the entire stack. - if ctx.GlobalIsSet(utils.L2MigrationBlockFlag.Name) && ctx.GlobalUint64(utils.L2MigrationBlockFlag.Name) > 0 { - go func() { - chainHeadCh := make(chan ethCore.ChainHeadEvent, 10) - chainHeadSub := backend.SubscribeChainHeadEvent(chainHeadCh) - defer chainHeadSub.Unsubscribe() - for { - select { - case chainHeadEvent := <-chainHeadCh: - block := chainHeadEvent.Block - if backend.ChainConfig().IsL2Migration(block.Number()) { - log.Info("L2 Migration Block Reached, closing entire stack from geth cmd", "block", block.NumberU64(), "hash", block.Hash()) - stack.Close() - return - } - case err := <-chainHeadSub.Err(): - log.Error("Error in outer subscription to blockchain's chainhead event listening for l2 migration block", "err", err) - return - } - } - }() - } - isFullNode := ctx.GlobalString(utils.SyncModeFlag.Name) == "full" || ctx.GlobalString(utils.SyncModeFlag.Name) == "fast" // Miners and proxies only makes sense if a full node is running if ctx.GlobalBool(utils.ProxyFlag.Name) || ctx.GlobalBool(utils.MiningEnabledFlag.Name) || ctx.GlobalBool(utils.DeveloperFlag.Name) { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 5471684b31..cd0a8a476f 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -245,7 +245,7 @@ var ( L2MigrationBlockFlag = cli.Uint64Flag{ Name: "l2migrationblock", - Usage: "Block number at which to halt the network for Celo L2 migration. Last block of Celo as an L1. If unset or set to 0, no halt will occur.", + Usage: "Block number at which to halt the network for Celo L2 migration. This is the first block of Celo as an L2, and one after the last block of Celo as an L1. If unset or set to 0, no halt will occur.", } BloomFilterSizeFlag = cli.Uint64Flag{ diff --git a/consensus/istanbul/backend/backend.go b/consensus/istanbul/backend/backend.go index 3000562ea8..047d539585 100644 --- a/consensus/istanbul/backend/backend.go +++ b/consensus/istanbul/backend/backend.go @@ -555,8 +555,9 @@ func (sb *Backend) Commit(proposal istanbul.Proposal, aggregatedSeal types.Istan } sb.onNewConsensusBlock(block, result.Receipts, result.Logs, result.State) - if sb.chain.Config().IsL2Migration(block.Number()) { - sb.logger.Info("L2 migration block reached, closing istanbul backend", "block", block.NumberU64(), "hash", block.Hash()) + nextBlockNum := new(big.Int).Add(block.Number(), big.NewInt(1)) + if sb.chain.Config().IsL2Migration(nextBlockNum) { + sb.logger.Info("The next block is the L2 migration block, stopping announce protocol and closing istanbul backend", "currentBlock", block.NumberU64(), "hash", block.Hash(), "nextBlock", nextBlockNum) sb.StopAnnouncing() sb.Close() } diff --git a/core/blockchain.go b/core/blockchain.go index ef2dbbea11..28b920739f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -811,9 +811,10 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { // // Note, this function assumes that the `mu` mutex is held! func (bc *BlockChain) writeHeadBlock(block *types.Block) { - if bc.Config().IsL2Migration(new(big.Int).Sub(block.Number(), big.NewInt(1))) { - bc.StopInsert() // Just for good measure - log.Crit("Attempt to insert block beyond L2 migration block. This should never happen.", "block", block.NumberU64(), "hash", block.Hash()) + if bc.Config().IsL2Migration(block.Number()) { + log.Error("Attempt to insert block number >= l2MigrationBlock, stopping block insertion", "block", block.NumberU64(), "hash", block.Hash()) + bc.StopInsert() + return } // If the block is on a side chain or an unknown one, force other heads onto it too updateHeads := rawdb.ReadCanonicalHash(bc.db, block.NumberU64()) != block.Hash() @@ -842,12 +843,11 @@ func (bc *BlockChain) writeHeadBlock(block *types.Block) { bc.currentBlock.Store(block) headBlockGauge.Update(int64(block.NumberU64())) - if bc.Config().IsL2Migration(block.Number()) { - log.Info("L2 migration block reached, stopping block insertion", "block", block.NumberU64(), "hash", block.Hash()) + nextBlockNum := new(big.Int).Add(block.Number(), big.NewInt(1)) + if bc.Config().IsL2Migration(nextBlockNum) { + log.Info("The next block is the L2 migration block, stopping block insertion", "currentBlock", block.NumberU64(), "hash", block.Hash(), "nextBlock", nextBlockNum.Uint64()) bc.StopInsert() - // The eth handler has a thread that listens for chain head events and stops the eth handler when the l2 migration block is reached. - // This stops the node from syncing further blocks. - bc.chainHeadFeed.Send(ChainHeadEvent{Block: block}) + return } } diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index 1bb631161f..b405bfd35b 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -386,13 +386,13 @@ func TestStopNetworkAtL2Block(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*200) defer cancel() - err = network.AwaitBlock(ctx, l2Block.Uint64()) + err = network.AwaitBlock(ctx, l2Block.Uint64()-1) require.NoError(t, err) shortCtx, cancel := context.WithTimeout(context.Background(), time.Second*2) defer cancel() - err = network.AwaitBlock(shortCtx, l2Block.Uint64()+1) + err = network.AwaitBlock(shortCtx, l2Block.Uint64()) // Expect DeadlineExceeded error if !errors.Is(err, context.DeadlineExceeded) { t.Fatalf("expecting %q, instead got: %v ", context.DeadlineExceeded.Error(), err) diff --git a/eth/handler.go b/eth/handler.go index 84bf8ea045..896b141581 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -114,12 +114,10 @@ type handler struct { txFetcher *fetcher.TxFetcher peers *peerSet - eventMux *event.TypeMux - txsCh chan core.NewTxsEvent - txsSub event.Subscription - minedBlockSub *event.TypeMuxSubscription - newChainHeadCh chan core.ChainHeadEvent - newChainHeadSub event.Subscription + eventMux *event.TypeMux + txsCh chan core.NewTxsEvent + txsSub event.Subscription + minedBlockSub *event.TypeMuxSubscription whitelist map[uint64]common.Hash @@ -441,7 +439,7 @@ func (h *handler) unregisterPeer(id string) *ethPeer { } if err := h.txFetcher.Drop(id); err != nil { if !errors.Is(err, fetcher.ErrTerminated) { - log.Error("Peer removal from tx fetcher failed", "peer", id, "err", err) + log.Error("Peer removal from tx fetcher failed", "peer", id, "err", err) } } if handler, ok := h.chain.Engine().(consensus.Handler); ok { @@ -471,26 +469,15 @@ func (h *handler) Start(maxPeers int) { // start sync handlers h.wg.Add(1) go h.chainSync.loop() - - // Listen for L2 migration block - h.wg.Add(1) - h.newChainHeadCh = make(chan core.ChainHeadEvent, 10) - h.newChainHeadSub = h.chain.SubscribeChainHeadEvent(h.newChainHeadCh) - go h.l2MigrationLoop() } func (h *handler) Stop() { - h.txsSub.Unsubscribe() // quits txBroadcastLoop - h.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop - h.newChainHeadSub.Unsubscribe() // quits l2MigrationLoop + h.txsSub.Unsubscribe() // quits txBroadcastLoop + h.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop // Quit chainSync and txsync64. // After this is done, no new peers will be accepted. - select { - case <-h.quitSync: - default: - close(h.quitSync) - } + close(h.quitSync) h.wg.Wait() // Disconnect existing sessions. @@ -608,25 +595,6 @@ func (h *handler) txBroadcastLoop() { } } -func (h *handler) l2MigrationLoop() { - for { - select { - case event := <-h.newChainHeadCh: - block := event.Block - if h.chain.Config().IsL2Migration(block.Number()) { - log.Info("L2 Migration Block Reached, stopping handler and p2p server", "block", block.NumberU64(), "hash", block.Hash()) - h.wg.Done() // we don't use 'defer' here because we want to decrement the wait group before calling h.Stop(), otherwise we get a deadlock - h.Stop() - h.server.Stop() - return - } - case <-h.newChainHeadSub.Err(): - h.wg.Done() - return - } - } -} - func (h *handler) FindPeers(targets map[enode.ID]bool, purpose p2p.PurposeFlag) map[enode.ID]consensus.Peer { m := make(map[enode.ID]consensus.Peer) for _, p := range h.peers.Peers() { diff --git a/miner/worker.go b/miner/worker.go index 13e550d094..72c2814330 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -18,6 +18,7 @@ package miner import ( "context" + "math/big" "sync" "sync/atomic" "time" @@ -395,24 +396,23 @@ func (w *worker) mainLoop() { defer wg.Wait() txsCh := make(chan core.NewTxsEvent, txChanSize) - generateNewBlock := func() { + generateNewBlock := func(nextBlockNum *big.Int) { if cancel != nil { cancel() } - wg.Wait() - taskCtx, cancel = context.WithCancel(context.Background()) - wg.Add(1) - if w.chainConfig.IsL2Migration(w.chain.CurrentBlock().Number()) { + if w.chainConfig.IsL2Migration(nextBlockNum) { if w.isRunning() { - log.Info("L2 Migration block reached, stopping block construction", "block", w.chain.CurrentBlock().NumberU64(), "hash", w.chain.CurrentBlock().Hash()) + log.Info("The next block is the L2 migration block, stopping block construction", "currentBlock", w.chain.CurrentBlock().NumberU64(), "hash", w.chain.CurrentBlock().Hash(), "nextBlock", nextBlockNum.Uint64()) w.stop() } - wg.Done() - cancel() return } + wg.Wait() + taskCtx, cancel = context.WithCancel(context.Background()) + wg.Add(1) + if w.isRunning() { // engine.NewWork posts the FinalCommitted Event to IBFT to signal the start of the next round if h, ok := w.engine.(consensus.Handler); ok { @@ -434,10 +434,12 @@ func (w *worker) mainLoop() { for { select { case <-w.startCh: - generateNewBlock() + nextBlockNum := new(big.Int).Add(w.chain.CurrentBlock().Number(), big.NewInt(1)) + generateNewBlock(nextBlockNum) - case <-w.chainHeadCh: - generateNewBlock() + case ev := <-w.chainHeadCh: + nextBlockNum := new(big.Int).Add(ev.Block.Number(), big.NewInt(1)) + generateNewBlock(nextBlockNum) case ev := <-w.txsCh: // Drain tx sub channel as a validator, diff --git a/params/config.go b/params/config.go index febc145d84..e012344f8d 100644 --- a/params/config.go +++ b/params/config.go @@ -313,7 +313,7 @@ type ChainConfig struct { GingerbreadBlock *big.Int `json:"gingerbreadBlock,omitempty"` // Gingerbread switch block (nil = no fork, 0 = already activated) GingerbreadP2Block *big.Int `json:"gingerbreadP2Block,omitempty"` // GingerbreadP2 switch block (nil = no fork, 0 = already activated) HForkBlock *big.Int `json:"hforkBlock,omitempty"` // HFork switch block (nil = no fork, 0 = already activated) - L2MigrationBlock *big.Int `json:"l2MigrationBlock,omitempty"` // l2 migration block / last block of l1 (nil = no migration, 0 = no migration) + L2MigrationBlock *big.Int `json:"l2MigrationBlock,omitempty"` // l2 migration block / first block of Celo as L2 / 1 + last block of Celo as L1 (nil = no migration, 0 = no migration) Istanbul *IstanbulConfig `json:"istanbul,omitempty"` // This does not belong here but passing it to every function is not possible since that breaks @@ -465,7 +465,7 @@ func (c *ChainConfig) IsGingerbreadP2(num *big.Int) bool { return isForked(c.GingerbreadP2Block, num) } -// IsL2 returns whether num represents a block number greater than or equal to the L2 migration block (last block before L2) +// IsL2Migration returns whether num represents a block number greater than or equal to the L2 migration block (fist block of Celo as L2 / 1 + last block of Celo as L1) func (c *ChainConfig) IsL2Migration(num *big.Int) bool { return isForked(c.L2MigrationBlock, num) && c.L2MigrationBlock.Cmp(big.NewInt(0)) > 0 // return false if L2MigrationBlock is nil or 0 } From f9ad4ee2f6c21627df83b4e07638cfd34a8b3d89 Mon Sep 17 00:00:00 2001 From: alecps Date: Wed, 14 Aug 2024 17:42:44 -0400 Subject: [PATCH 10/21] make InsertChain return an error when stopped due to l2 migration block --- core/blockchain.go | 3 +++ core/blockchain_test.go | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/core/blockchain.go b/core/blockchain.go index 28b920739f..d8eab43d13 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1882,6 +1882,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er // If the chain is terminating, stop processing blocks if bc.insertStopped() { log.Debug("Abort during block processing") + if bc.Config().IsL2Migration(block.Number()) { + err = errInsertionInterrupted + } break } // If the header is a banned one, straight out abort diff --git a/core/blockchain_test.go b/core/blockchain_test.go index f73b91b64e..d46da17402 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -38,6 +38,7 @@ import ( "github.com/celo-org/celo-blockchain/ethdb" "github.com/celo-org/celo-blockchain/params" "github.com/celo-org/celo-blockchain/trie" + "github.com/stretchr/testify/require" ) // So we can deterministically seed different blockchains @@ -205,6 +206,25 @@ func TestLastBlock(t *testing.T) { } } +func TestNoInsertPastL2MigrationBlock(t *testing.T) { + _, blockchain, err := newCanonical(mockEngine.NewFaker(), 0, true) + if err != nil { + t.Fatalf("failed to create pristine chain: %v", err) + } + defer blockchain.Stop() + + migrationBlock := 2 + blockchain.chainConfig.L2MigrationBlock = big.NewInt(int64(migrationBlock)) + + blocks := makeBlockChain(blockchain.CurrentBlock(), 100, mockEngine.NewFullFaker(), blockchain.db, 0) + failedBlock, err := blockchain.InsertChain(blocks) + require.EqualError(t, err, errInsertionInterrupted.Error()) + require.EqualValues(t, migrationBlock-1, failedBlock) + if blocks[migrationBlock-2].Hash() != rawdb.ReadHeadBlockHash(blockchain.db) { + t.Fatalf("Write/Get HeadBlockHash failed") + } +} + // Tests that given a starting canonical chain of a given size, it can be extended // with various length chains. func TestExtendCanonicalHeaders(t *testing.T) { testExtendCanonical(t, false) } From 13b2208c6298dea49c276adcd4b3b8451f5ce9f7 Mon Sep 17 00:00:00 2001 From: alecps Date: Thu, 15 Aug 2024 15:41:56 -0400 Subject: [PATCH 11/21] add more rigorous e2e test --- e2e_test/e2e_test.go | 120 +++++++++++++++++++++++++++----- test/node.go | 159 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 242 insertions(+), 37 deletions(-) diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index b405bfd35b..87a870cc25 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -370,33 +370,119 @@ func TestStartStopValidators(t *testing.T) { } -func TestStopNetworkAtL2Block(t *testing.T) { - ac := test.AccountConfig(3, 2) +func testStopNetworkAtL2Block(t *testing.T, ctx context.Context, network test.Network, l2Block *big.Int) { + err := network.AwaitBlock(ctx, l2Block.Uint64()-1) + require.NoError(t, err) + + shortCtx, cancel := context.WithTimeout(ctx, time.Second*2) + defer cancel() + + // fail if any node adds a new block >= the migration block + var wg sync.WaitGroup + for _, n := range network { + wg.Add(1) + go func(n *test.Node) { + defer wg.Done() + err = n.Tracker.AwaitBlock(shortCtx, l2Block.Uint64()) + if !errors.Is(err, context.DeadlineExceeded) { + t.Fatalf("expecting %q, instead got: %v ", context.DeadlineExceeded.Error(), err) + } + }(n) + } + wg.Wait() +} + +func TestStopNetworkAtl2BlockSimple(t *testing.T) { + numValidators := 3 + numFullNodes := 2 + ac := test.AccountConfig(numValidators, 2) gingerbreadBlock := common.Big0 - l2Block := big.NewInt(3) - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, l2Block) + l2BlockOG := big.NewInt(3) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, l2BlockOG) require.NoError(t, err) - network, shutdown, err := test.NewNetwork(ac, gc, ec) + network, _, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) - defer func() { - log.Info("Shutting down network from e2e test") - shutdown() - }() + network, shutdown, err := test.AddNetworkFullNodes(network, ec, uint64(numFullNodes)) + require.NoError(t, err) + defer shutdown() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*200) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*400) defer cancel() - err = network.AwaitBlock(ctx, l2Block.Uint64()-1) + testStopNetworkAtL2Block(t, ctx, network, l2BlockOG) +} + +func TestStopNetworkAtL2Block(t *testing.T) { + numValidators := 3 + numFullNodes := 2 + ac := test.AccountConfig(numValidators, 2) + gingerbreadBlock := common.Big0 + l2BlockOG := big.NewInt(3) + gc, ec, err := test.BuildConfig(ac, gingerbreadBlock, l2BlockOG) require.NoError(t, err) + network, _, err := test.NewNetwork(ac, gc, ec) + require.NoError(t, err) + network, shutdown, err := test.AddNetworkFullNodes(network, ec, uint64(numFullNodes)) + require.NoError(t, err) + defer shutdown() - shortCtx, cancel := context.WithTimeout(context.Background(), time.Second*2) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*500) defer cancel() - err = network.AwaitBlock(shortCtx, l2Block.Uint64()) - // Expect DeadlineExceeded error - if !errors.Is(err, context.DeadlineExceeded) { - t.Fatalf("expecting %q, instead got: %v ", context.DeadlineExceeded.Error(), err) - } + testStopNetworkAtL2Block(t, ctx, network, l2BlockOG) + + shutdown() + + // Restart nodes with --l2-migration-block set to the next block + err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{1, 1, 1, 1, 1}) + require.NoError(t, err) + + l2BlockPlusOne := new(big.Int).Add(l2BlockOG, big.NewInt(1)) + + testStopNetworkAtL2Block(t, ctx, network, l2BlockPlusOne) + + shutdown() + + // Restart nodes with --l2-migration-block set to the same block + err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{1, 1, 1, 1, 1}) + require.NoError(t, err) + + testStopNetworkAtL2Block(t, ctx, network, l2BlockPlusOne) + + shutdown() + + // Restart nodes with different --l2-migration-block offsets + // If 2/3 validators (validators are the first 3 nodes in the network array) + // have the same migration block, the network should not be able to add any more blocks + err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{1, 1, 2, 2, 2}) + require.NoError(t, err) + + testStopNetworkAtL2Block(t, ctx, network, l2BlockPlusOne) + + shutdown() + + // Restart nodes with different --l2-migration-block offsets + // If 2/3 validators (validators are the first 3 nodes in the network array) + // have a greater migration block, the rest of the network should be able to add more blocks + err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{1, 2, 2, 2, 2}) + require.NoError(t, err) + + l2BlockPlusTwo := new(big.Int).Add(l2BlockOG, big.NewInt(2)) + + testStopNetworkAtL2Block(t, ctx, network[:1], l2BlockPlusOne) + testStopNetworkAtL2Block(t, ctx, network[1:], l2BlockPlusTwo) + + // TODO(Alec) + + // shutdown() + + // Restart nodes with --l2-migration-block set to a prev block + // err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{-1, -1, -1, -1, -1}) + // require.NoError(t, err) // TODO(Alec) + + // l2BlockMinusOne := new(big.Int).Sub(l2BlockOG, big.NewInt(1)) + + // testStopNetworkAtL2Block(t, ctx, network, l2BlockMinusOne) } // This test was created to reproduce the concurrent map access error in diff --git a/test/node.go b/test/node.go index 6242f10efb..3fe15a1846 100644 --- a/test/node.go +++ b/test/node.go @@ -15,6 +15,7 @@ import ( ethereum "github.com/celo-org/celo-blockchain" "github.com/celo-org/celo-blockchain/eth/ethconfig" + "github.com/celo-org/celo-blockchain/log" "github.com/celo-org/celo-blockchain/common" "github.com/celo-org/celo-blockchain/consensus/istanbul" @@ -60,7 +61,7 @@ var ( } BaseEthConfig = ð.Config{ - SyncMode: downloader.FullSync, + SyncMode: downloader.FullSync, // TODO(Alec) do we need to test different sync modes? MinSyncPeers: 1, DatabaseCache: 256, DatabaseHandles: 256, @@ -110,7 +111,51 @@ type Node struct { SentTxs []*types.Transaction } -// NewNode creates a new running node with the provided config. +// NewFullNode creates a new running (non-validator) node with the provided config. +func NewFullNode( + nc *node.Config, + ec *eth.Config, + genesis *core.Genesis, +) (*Node, error) { + + // Copy the node config so we can modify it without damaging the original + ncCopy := *nc + + // p2p key and address, this is not the same as the validator key. + p2pKey, err := crypto.GenerateKey() + if err != nil { + return nil, err + } + ncCopy.P2P.PrivateKey = p2pKey + + // Make temp datadir + datadir, err := ioutil.TempDir("", "celo_datadir") + if err != nil { + return nil, err + } + ncCopy.DataDir = datadir + + // copy the base eth config, so we can modify it without damaging the + // original. + ecCopy := ð.Config{} + err = copyObject(ec, ecCopy) + if err != nil { + return nil, err + } + ecCopy.Genesis = genesis + ecCopy.NetworkId = genesis.Config.ChainID.Uint64() + ecCopy.Istanbul.Validator = false + + node := &Node{ + Config: &ncCopy, + EthConfig: ecCopy, + Tracker: NewTracker(), + } + + return node, node.Start() +} + +// NewNode creates a new running validator node with the provided config. func NewNode( validatorAccount *env.Account, nc *node.Config, @@ -201,21 +246,23 @@ func (n *Node) Start() error { // The ListenAddr is set at p2p server startup, save it here. n.P2PListenAddr = n.Node.Server().ListenAddr - // Import the node key into the keystore and then unlock it, the keystore - // is the interface used for signing operations so the node key needs to be - // inside it. - ks, _, err := n.Config.GetKeyStore() - if err != nil { - return err - } - n.AccountManager().AddBackend(ks) - account, err := ks.ImportECDSA(n.Key, "") - if err != nil { - return err - } - err = ks.TimedUnlock(account, "", 0) - if err != nil { - return err + if n.EthConfig.Istanbul.Validator { + // Import the node key into the keystore and then unlock it, the keystore + // is the interface used for signing operations so the node key needs to be + // inside it. + ks, _, err := n.Config.GetKeyStore() + if err != nil { + return err + } + n.AccountManager().AddBackend(ks) + account, err := ks.ImportECDSA(n.Key, "") + if err != nil { + return err + } + err = ks.TimedUnlock(account, "", 0) + if err != nil { + return err + } } _, _, err = core.SetupGenesisBlock(n.Eth.ChainDb(), n.EthConfig.Genesis) @@ -230,9 +277,12 @@ func (n *Node) Start() error { if err != nil { return err } - err = n.Eth.StartMining() - if err != nil { - return err + + if n.EthConfig.Istanbul.Validator { + err = n.Eth.StartMining() + if err != nil { + return err + } } // Note we need to use the LocalNode from the p2p server because that is @@ -441,6 +491,46 @@ func NewNetwork(accounts *env.AccountsConfig, gc *genesis.Config, ec *eth.Config } shutdown := func() { + log.Info("Shutting down network from test") + for _, err := range network.Shutdown() { + fmt.Println(err.Error()) + } + } + + return network, shutdown, nil +} + +func AddNetworkFullNodes(network Network, ec *eth.Config, numFullNodes uint64) (Network, func(), error) { + var fullNodes Network = make([]*Node, numFullNodes) + genesis := network[0].EthConfig.Genesis + + for i := uint64(0); i < numFullNodes; i++ { + n, err := NewFullNode(baseNodeConfig, ec, genesis) + if err != nil { + return nil, nil, fmt.Errorf("failed to build full node for network: %v", err) + } + fullNodes[i] = n + } + + // Connect nodes to each other + for i := range fullNodes { + fullNodes[i].AddPeers(network...) + fullNodes[i].AddPeers(fullNodes[i+1:]...) + } + + network = append(network, fullNodes...) + + // Give nodes some time to connect. Also there is a race condition in + // miner.worker its field snapshotBlock is set only when new transactions + // are received or commitNewWork is called. But both of these happen in + // goroutines separate to the call to miner.Start and miner.Start does not + // wait for snapshotBlock to be set. Therefore there is currently no way to + // know when it is safe to call estimate gas. What we do here is sleep a + // bit and cross our fingers. + time.Sleep(25 * time.Millisecond) + + shutdown := func() { + log.Info("Shutting down network from test") for _, err := range network.Shutdown() { fmt.Println(err.Error()) } @@ -486,6 +576,35 @@ func (n Network) Shutdown() []error { return errors } +func (n Network) RestartNetworkWithMigrationBlockOffsets(l2MigrationBlockOG *big.Int, offsets []int64) error { + if len(offsets) != len(n) { + return fmt.Errorf("number of l2BlockMigration offsets must match number of nodes") + } + + for i, node := range n { + node.EthConfig.L2MigrationBlock = new(big.Int).Add(l2MigrationBlockOG, big.NewInt(offsets[i])) + err := node.Start() + if err != nil { + return err + } + } + + for i, node := range n { + node.AddPeers(n[:i]...) + } + + // We need to wait here to allow the call to "Backend.RefreshValPeers" to + // complete before adding peers. This is because "Backend.RefreshValPeers" + // deletes all peers and then re-adds any peers from the cached + // connections, but in the case that peers were recently added there may + // not have been enough time to connect to them and populate the connection + // cache, and in that case "Backend.RefreshValPeers" simply removes all the + // peers. + time.Sleep(25 * time.Millisecond) + + return nil +} + // Uses the client to suggest a gas price and to estimate the gas. func BuildSignedTransaction( client *ethclient.Client, From 5789b30809ab01ace71b30fda0484ab375647eb5 Mon Sep 17 00:00:00 2001 From: alecps Date: Thu, 15 Aug 2024 19:04:01 -0400 Subject: [PATCH 12/21] cleanup --- core/blockchain_test.go | 2 +- core/error.go | 3 +++ e2e_test/e2e_test.go | 38 ++++++++++++++++++++++++++++++-------- test/node.go | 6 +++++- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index d46da17402..1d0dcae0c7 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -216,7 +216,7 @@ func TestNoInsertPastL2MigrationBlock(t *testing.T) { migrationBlock := 2 blockchain.chainConfig.L2MigrationBlock = big.NewInt(int64(migrationBlock)) - blocks := makeBlockChain(blockchain.CurrentBlock(), 100, mockEngine.NewFullFaker(), blockchain.db, 0) + blocks := makeBlockChain(blockchain.CurrentBlock(), 100000000, mockEngine.NewFullFaker(), blockchain.db, 0) failedBlock, err := blockchain.InsertChain(blocks) require.EqualError(t, err, errInsertionInterrupted.Error()) require.EqualValues(t, migrationBlock-1, failedBlock) diff --git a/core/error.go b/core/error.go index f5515dd858..0f2d0a154e 100644 --- a/core/error.go +++ b/core/error.go @@ -33,6 +33,9 @@ var ( ErrNoGenesis = errors.New("genesis not found in chain") errSideChainReceipts = errors.New("side blocks can't be accepted as ancient chain data") + + // ErrL2Migration is returned when the current block is greater than or equal to the L2 migration block + ErrL2Migration = errors.New("chain has migrated to L2, data exists beyond the configured migration block") ) // List of evm-call-message pre-checking errors. All state transition messages will diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index 87a870cc25..db89790d7b 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -412,6 +412,29 @@ func TestStopNetworkAtl2BlockSimple(t *testing.T) { testStopNetworkAtL2Block(t, ctx, network, l2BlockOG) } +/* +Test cases for stopping at L2 migration block: + +- [x] (cli/demo) node is syncing, not-validating, hits migration block + - [X] (cli/demo) node restarts with same --l2migrationblock, does not produce new blocks + - [x] (cli/demo) node restarts with --l2migrationblock + 1, produces one new block + - [X] (cli/demo) node restarts with --l2migrationblock - 1, does not produce new blocks, keeps current head at previous migration block, logs error message +- [x] (e2e test) node is synced and following chain, not-validating, hits migration block + - [x] (e2e test) node restarts with same --l2migrationblock, does not produce new blocks + - [x] (e2e test) node restarts with --l2migrationblock + 1, produces one new block + - [x] (e2e test) node restarts with --l2migrationblock - 1, does not produce new blocks, keeps current head at previous migration block, logs error message +- [skip? Is this worth testing?] node is syncing, validating, hits migration block + - [skip?] node restarts with same --l2migrationblock, does not produce new blocks + - [skip?] node restarts with --l2migrationblock + 1, produces one new block + - [skip?] node restarts with --l2migrationblock - 1, does not produce new blocks, keeps current head at previous migration block, logs error message +- [x] (e2e test) node is synced and following chain, validating, hits migration block + - [x] (e2e test) node restarts with same --l2migrationblock, does not produce new blocks + - [x] (e2e test) node restarts with --l2migrationblock + 1, produces one new block + - [x] (e2e test) node restarts with --l2migrationblock - 1, does not produce new blocks, keeps current head at previous migration block, logs error message +- [x] (e2e test) Thresholds - when majority of validators are at migration block, full nodes CANNOT progress even if they have a higher number configured +- [x] (e2e test) Thresholds - when minority of validators are at migration block, full nodes CAN progress if they have a higher number configured +*/ + func TestStopNetworkAtL2Block(t *testing.T) { numValidators := 3 numFullNodes := 2 @@ -472,17 +495,16 @@ func TestStopNetworkAtL2Block(t *testing.T) { testStopNetworkAtL2Block(t, ctx, network[:1], l2BlockPlusOne) testStopNetworkAtL2Block(t, ctx, network[1:], l2BlockPlusTwo) - // TODO(Alec) - - // shutdown() + shutdown() // Restart nodes with --l2-migration-block set to a prev block - // err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{-1, -1, -1, -1, -1}) - // require.NoError(t, err) // TODO(Alec) - - // l2BlockMinusOne := new(big.Int).Sub(l2BlockOG, big.NewInt(1)) + err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{-1, -1, -1, -1, -1}) + // TODO(Alec) it would actually be nice to return errors here, require.Error(t, err, core.ErrL2Migration.Error()) + require.NoError(t, err) - // testStopNetworkAtL2Block(t, ctx, network, l2BlockMinusOne) + // The network should be unchanged + testStopNetworkAtL2Block(t, ctx, network[:1], l2BlockPlusOne) + testStopNetworkAtL2Block(t, ctx, network[1:], l2BlockPlusTwo) } // This test was created to reproduce the concurrent map access error in diff --git a/test/node.go b/test/node.go index 3fe15a1846..312fce8697 100644 --- a/test/node.go +++ b/test/node.go @@ -581,13 +581,17 @@ func (n Network) RestartNetworkWithMigrationBlockOffsets(l2MigrationBlockOG *big return fmt.Errorf("number of l2BlockMigration offsets must match number of nodes") } + errors := []error{} for i, node := range n { node.EthConfig.L2MigrationBlock = new(big.Int).Add(l2MigrationBlockOG, big.NewInt(offsets[i])) err := node.Start() if err != nil { - return err + errors = append(errors, err) } } + if len(errors) > 0 { + return fmt.Errorf("failed to restart network: %v", errors) + } for i, node := range n { node.AddPeers(n[:i]...) From b6ca113153295b77a987de35f2cdadb623480eb6 Mon Sep 17 00:00:00 2001 From: alecps Date: Thu, 15 Aug 2024 21:06:03 -0400 Subject: [PATCH 13/21] more cleanup --- cmd/geth/main.go | 1 - core/blockchain.go | 3 +++ core/error.go | 3 --- e2e_test/e2e_test.go | 5 ++--- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 9223538eac..531567eba6 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -327,7 +327,6 @@ func geth(ctx *cli.Context) error { prepare(ctx) stack, backend := makeFullNode(ctx) - defer stack.Close() startNode(ctx, stack, backend) diff --git a/core/blockchain.go b/core/blockchain.go index d8eab43d13..4f7aca4188 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -811,6 +811,9 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { // // Note, this function assumes that the `mu` mutex is held! func (bc *BlockChain) writeHeadBlock(block *types.Block) { + // Normally the check at the end of the function will pass first, but if a node is restarted + // with the same l2-migration-block configured after already reaching and stopping on l2-migration-block, + // this check will pass first and log an error. if bc.Config().IsL2Migration(block.Number()) { log.Error("Attempt to insert block number >= l2MigrationBlock, stopping block insertion", "block", block.NumberU64(), "hash", block.Hash()) bc.StopInsert() diff --git a/core/error.go b/core/error.go index 0f2d0a154e..f5515dd858 100644 --- a/core/error.go +++ b/core/error.go @@ -33,9 +33,6 @@ var ( ErrNoGenesis = errors.New("genesis not found in chain") errSideChainReceipts = errors.New("side blocks can't be accepted as ancient chain data") - - // ErrL2Migration is returned when the current block is greater than or equal to the L2 migration block - ErrL2Migration = errors.New("chain has migrated to L2, data exists beyond the configured migration block") ) // List of evm-call-message pre-checking errors. All state transition messages will diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index db89790d7b..6daad1d886 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "math/big" - "os" "os/exec" "strings" "sync" @@ -35,10 +34,10 @@ func init() { // This statement is commented out but left here since its very useful for // debugging problems and its non trivial to construct. // - log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stdout, log.TerminalFormat(true)))) + // log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stdout, log.TerminalFormat(true)))) // This disables all logging which in general we want, because there is a lot - // log.Root().SetHandler(log.DiscardHandler()) + log.Root().SetHandler(log.DiscardHandler()) } // This test starts a network submits a transaction and waits for the whole From ee7ee61fcc2b12bbdc68f1b50658aa93a9874068 Mon Sep 17 00:00:00 2001 From: alecps Date: Mon, 19 Aug 2024 11:33:17 -0400 Subject: [PATCH 14/21] fixes lint error: --- e2e_test/e2e_test.go | 51 +++++++++++--------------------------------- 1 file changed, 13 insertions(+), 38 deletions(-) diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index 6daad1d886..805ce6d0be 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -369,7 +369,7 @@ func TestStartStopValidators(t *testing.T) { } -func testStopNetworkAtL2Block(t *testing.T, ctx context.Context, network test.Network, l2Block *big.Int) { +func runStopNetworkAtL2BlockTest(ctx context.Context, t *testing.T, network test.Network, l2Block *big.Int) { err := network.AwaitBlock(ctx, l2Block.Uint64()-1) require.NoError(t, err) @@ -383,9 +383,7 @@ func testStopNetworkAtL2Block(t *testing.T, ctx context.Context, network test.Ne go func(n *test.Node) { defer wg.Done() err = n.Tracker.AwaitBlock(shortCtx, l2Block.Uint64()) - if !errors.Is(err, context.DeadlineExceeded) { - t.Fatalf("expecting %q, instead got: %v ", context.DeadlineExceeded.Error(), err) - } + require.EqualError(t, err, context.DeadlineExceeded.Error()) }(n) } wg.Wait() @@ -405,35 +403,12 @@ func TestStopNetworkAtl2BlockSimple(t *testing.T) { require.NoError(t, err) defer shutdown() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*400) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*40) defer cancel() - testStopNetworkAtL2Block(t, ctx, network, l2BlockOG) + runStopNetworkAtL2BlockTest(ctx, t, network, l2BlockOG) } -/* -Test cases for stopping at L2 migration block: - -- [x] (cli/demo) node is syncing, not-validating, hits migration block - - [X] (cli/demo) node restarts with same --l2migrationblock, does not produce new blocks - - [x] (cli/demo) node restarts with --l2migrationblock + 1, produces one new block - - [X] (cli/demo) node restarts with --l2migrationblock - 1, does not produce new blocks, keeps current head at previous migration block, logs error message -- [x] (e2e test) node is synced and following chain, not-validating, hits migration block - - [x] (e2e test) node restarts with same --l2migrationblock, does not produce new blocks - - [x] (e2e test) node restarts with --l2migrationblock + 1, produces one new block - - [x] (e2e test) node restarts with --l2migrationblock - 1, does not produce new blocks, keeps current head at previous migration block, logs error message -- [skip? Is this worth testing?] node is syncing, validating, hits migration block - - [skip?] node restarts with same --l2migrationblock, does not produce new blocks - - [skip?] node restarts with --l2migrationblock + 1, produces one new block - - [skip?] node restarts with --l2migrationblock - 1, does not produce new blocks, keeps current head at previous migration block, logs error message -- [x] (e2e test) node is synced and following chain, validating, hits migration block - - [x] (e2e test) node restarts with same --l2migrationblock, does not produce new blocks - - [x] (e2e test) node restarts with --l2migrationblock + 1, produces one new block - - [x] (e2e test) node restarts with --l2migrationblock - 1, does not produce new blocks, keeps current head at previous migration block, logs error message -- [x] (e2e test) Thresholds - when majority of validators are at migration block, full nodes CANNOT progress even if they have a higher number configured -- [x] (e2e test) Thresholds - when minority of validators are at migration block, full nodes CAN progress if they have a higher number configured -*/ - func TestStopNetworkAtL2Block(t *testing.T) { numValidators := 3 numFullNodes := 2 @@ -448,10 +423,10 @@ func TestStopNetworkAtL2Block(t *testing.T) { require.NoError(t, err) defer shutdown() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*500) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*100) defer cancel() - testStopNetworkAtL2Block(t, ctx, network, l2BlockOG) + runStopNetworkAtL2BlockTest(ctx, t, network, l2BlockOG) shutdown() @@ -461,7 +436,7 @@ func TestStopNetworkAtL2Block(t *testing.T) { l2BlockPlusOne := new(big.Int).Add(l2BlockOG, big.NewInt(1)) - testStopNetworkAtL2Block(t, ctx, network, l2BlockPlusOne) + runStopNetworkAtL2BlockTest(ctx, t, network, l2BlockPlusOne) shutdown() @@ -469,7 +444,7 @@ func TestStopNetworkAtL2Block(t *testing.T) { err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{1, 1, 1, 1, 1}) require.NoError(t, err) - testStopNetworkAtL2Block(t, ctx, network, l2BlockPlusOne) + runStopNetworkAtL2BlockTest(ctx, t, network, l2BlockPlusOne) shutdown() @@ -479,7 +454,7 @@ func TestStopNetworkAtL2Block(t *testing.T) { err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{1, 1, 2, 2, 2}) require.NoError(t, err) - testStopNetworkAtL2Block(t, ctx, network, l2BlockPlusOne) + runStopNetworkAtL2BlockTest(ctx, t, network, l2BlockPlusOne) shutdown() @@ -491,8 +466,8 @@ func TestStopNetworkAtL2Block(t *testing.T) { l2BlockPlusTwo := new(big.Int).Add(l2BlockOG, big.NewInt(2)) - testStopNetworkAtL2Block(t, ctx, network[:1], l2BlockPlusOne) - testStopNetworkAtL2Block(t, ctx, network[1:], l2BlockPlusTwo) + runStopNetworkAtL2BlockTest(ctx, t, network[:1], l2BlockPlusOne) + runStopNetworkAtL2BlockTest(ctx, t, network[1:], l2BlockPlusTwo) shutdown() @@ -502,8 +477,8 @@ func TestStopNetworkAtL2Block(t *testing.T) { require.NoError(t, err) // The network should be unchanged - testStopNetworkAtL2Block(t, ctx, network[:1], l2BlockPlusOne) - testStopNetworkAtL2Block(t, ctx, network[1:], l2BlockPlusTwo) + runStopNetworkAtL2BlockTest(ctx, t, network[:1], l2BlockPlusOne) + runStopNetworkAtL2BlockTest(ctx, t, network[1:], l2BlockPlusTwo) } // This test was created to reproduce the concurrent map access error in From 89f77dec1eb148b44a5a017a0b9e0be8373bb714 Mon Sep 17 00:00:00 2001 From: alecps Date: Mon, 19 Aug 2024 12:00:52 -0400 Subject: [PATCH 15/21] bump test timeout --- core/blockchain_test.go | 4 +++- e2e_test/e2e_test.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 1d0dcae0c7..9eebc973dc 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -216,10 +216,12 @@ func TestNoInsertPastL2MigrationBlock(t *testing.T) { migrationBlock := 2 blockchain.chainConfig.L2MigrationBlock = big.NewInt(int64(migrationBlock)) - blocks := makeBlockChain(blockchain.CurrentBlock(), 100000000, mockEngine.NewFullFaker(), blockchain.db, 0) + blocks := makeBlockChain(blockchain.CurrentBlock(), 1000000, mockEngine.NewFullFaker(), blockchain.db, 0) failedBlock, err := blockchain.InsertChain(blocks) require.EqualError(t, err, errInsertionInterrupted.Error()) + // Compare with migrationBlock-1 because failedBlock is the index of the failed block in the blocks[] array, not in the actual blockchain. require.EqualValues(t, migrationBlock-1, failedBlock) + // Only the first block in blocks[] should be inserted if blocks[migrationBlock-2].Hash() != rawdb.ReadHeadBlockHash(blockchain.db) { t.Fatalf("Write/Get HeadBlockHash failed") } diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index 805ce6d0be..06875b8c3d 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -423,7 +423,7 @@ func TestStopNetworkAtL2Block(t *testing.T) { require.NoError(t, err) defer shutdown() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*100) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*400) defer cancel() runStopNetworkAtL2BlockTest(ctx, t, network, l2BlockOG) From 4a6efb7ca7b86a55572e65b248abdb4b09aef22a Mon Sep 17 00:00:00 2001 From: alecps Date: Mon, 19 Aug 2024 13:08:42 -0400 Subject: [PATCH 16/21] add fast node to test --- e2e_test/e2e_test.go | 24 ++++++++++++++++-------- test/node.go | 32 +++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index 06875b8c3d..814756b030 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -15,6 +15,7 @@ import ( "github.com/celo-org/celo-blockchain/common" "github.com/celo-org/celo-blockchain/common/hexutil" "github.com/celo-org/celo-blockchain/core/types" + "github.com/celo-org/celo-blockchain/eth/downloader" "github.com/celo-org/celo-blockchain/eth/tracers" "github.com/celo-org/celo-blockchain/log" "github.com/celo-org/celo-blockchain/mycelo/env" @@ -392,6 +393,7 @@ func runStopNetworkAtL2BlockTest(ctx context.Context, t *testing.T, network test func TestStopNetworkAtl2BlockSimple(t *testing.T) { numValidators := 3 numFullNodes := 2 + numFastNodes := 1 ac := test.AccountConfig(numValidators, 2) gingerbreadBlock := common.Big0 l2BlockOG := big.NewInt(3) @@ -399,8 +401,11 @@ func TestStopNetworkAtl2BlockSimple(t *testing.T) { require.NoError(t, err) network, _, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) - network, shutdown, err := test.AddNetworkFullNodes(network, ec, uint64(numFullNodes)) + network, _, err = test.AddNonValidatorNodes(network, ec, uint64(numFullNodes), downloader.FullSync) require.NoError(t, err) + network, shutdown, err := test.AddNonValidatorNodes(network, ec, uint64(numFastNodes), downloader.FastSync) + require.NoError(t, err) + defer shutdown() ctx, cancel := context.WithTimeout(context.Background(), time.Second*40) @@ -412,6 +417,7 @@ func TestStopNetworkAtl2BlockSimple(t *testing.T) { func TestStopNetworkAtL2Block(t *testing.T) { numValidators := 3 numFullNodes := 2 + numFastNodes := 1 ac := test.AccountConfig(numValidators, 2) gingerbreadBlock := common.Big0 l2BlockOG := big.NewInt(3) @@ -419,8 +425,11 @@ func TestStopNetworkAtL2Block(t *testing.T) { require.NoError(t, err) network, _, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) - network, shutdown, err := test.AddNetworkFullNodes(network, ec, uint64(numFullNodes)) + network, _, err = test.AddNonValidatorNodes(network, ec, uint64(numFullNodes), downloader.FullSync) require.NoError(t, err) + network, shutdown, err := test.AddNonValidatorNodes(network, ec, uint64(numFastNodes), downloader.FastSync) + require.NoError(t, err) + defer shutdown() ctx, cancel := context.WithTimeout(context.Background(), time.Second*400) @@ -431,7 +440,7 @@ func TestStopNetworkAtL2Block(t *testing.T) { shutdown() // Restart nodes with --l2-migration-block set to the next block - err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{1, 1, 1, 1, 1}) + err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{1, 1, 1, 1, 1, 1}) require.NoError(t, err) l2BlockPlusOne := new(big.Int).Add(l2BlockOG, big.NewInt(1)) @@ -441,7 +450,7 @@ func TestStopNetworkAtL2Block(t *testing.T) { shutdown() // Restart nodes with --l2-migration-block set to the same block - err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{1, 1, 1, 1, 1}) + err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{1, 1, 1, 1, 1, 1}) require.NoError(t, err) runStopNetworkAtL2BlockTest(ctx, t, network, l2BlockPlusOne) @@ -451,7 +460,7 @@ func TestStopNetworkAtL2Block(t *testing.T) { // Restart nodes with different --l2-migration-block offsets // If 2/3 validators (validators are the first 3 nodes in the network array) // have the same migration block, the network should not be able to add any more blocks - err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{1, 1, 2, 2, 2}) + err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{1, 1, 2, 2, 2, 2}) require.NoError(t, err) runStopNetworkAtL2BlockTest(ctx, t, network, l2BlockPlusOne) @@ -461,7 +470,7 @@ func TestStopNetworkAtL2Block(t *testing.T) { // Restart nodes with different --l2-migration-block offsets // If 2/3 validators (validators are the first 3 nodes in the network array) // have a greater migration block, the rest of the network should be able to add more blocks - err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{1, 2, 2, 2, 2}) + err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{1, 2, 2, 2, 2, 2}) require.NoError(t, err) l2BlockPlusTwo := new(big.Int).Add(l2BlockOG, big.NewInt(2)) @@ -472,8 +481,7 @@ func TestStopNetworkAtL2Block(t *testing.T) { shutdown() // Restart nodes with --l2-migration-block set to a prev block - err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{-1, -1, -1, -1, -1}) - // TODO(Alec) it would actually be nice to return errors here, require.Error(t, err, core.ErrL2Migration.Error()) + err = network.RestartNetworkWithMigrationBlockOffsets(l2BlockOG, []int64{-1, -1, -1, -1, -1, -1}) require.NoError(t, err) // The network should be unchanged diff --git a/test/node.go b/test/node.go index 312fce8697..4b189a4983 100644 --- a/test/node.go +++ b/test/node.go @@ -111,11 +111,12 @@ type Node struct { SentTxs []*types.Transaction } -// NewFullNode creates a new running (non-validator) node with the provided config. -func NewFullNode( +// NewNonValidatorNode creates a new running non-validator node with the provided config. +func NewNonValidatorNode( nc *node.Config, ec *eth.Config, genesis *core.Genesis, + syncmode downloader.SyncMode, ) (*Node, error) { // Copy the node config so we can modify it without damaging the original @@ -145,6 +146,14 @@ func NewFullNode( ecCopy.Genesis = genesis ecCopy.NetworkId = genesis.Config.ChainID.Uint64() ecCopy.Istanbul.Validator = false + ecCopy.SyncMode = syncmode + + // We set these values here to avoid a panic in the eth service when these values are unset during fast sync + if syncmode == downloader.FastSync { + ecCopy.TrieCleanCache = 5 + ecCopy.TrieDirtyCache = 5 + ecCopy.SnapshotCache = 5 + } node := &Node{ Config: &ncCopy, @@ -500,25 +509,26 @@ func NewNetwork(accounts *env.AccountsConfig, gc *genesis.Config, ec *eth.Config return network, shutdown, nil } -func AddNetworkFullNodes(network Network, ec *eth.Config, numFullNodes uint64) (Network, func(), error) { - var fullNodes Network = make([]*Node, numFullNodes) +// AddNonValidatorNodes Adds non-validator nodes to the network with the specified sync mode. +func AddNonValidatorNodes(network Network, ec *eth.Config, numNodes uint64, syncmode downloader.SyncMode) (Network, func(), error) { + var nodes Network = make([]*Node, numNodes) genesis := network[0].EthConfig.Genesis - for i := uint64(0); i < numFullNodes; i++ { - n, err := NewFullNode(baseNodeConfig, ec, genesis) + for i := uint64(0); i < numNodes; i++ { + n, err := NewNonValidatorNode(baseNodeConfig, ec, genesis, syncmode) if err != nil { return nil, nil, fmt.Errorf("failed to build full node for network: %v", err) } - fullNodes[i] = n + nodes[i] = n } // Connect nodes to each other - for i := range fullNodes { - fullNodes[i].AddPeers(network...) - fullNodes[i].AddPeers(fullNodes[i+1:]...) + for i := range nodes { + nodes[i].AddPeers(network...) + nodes[i].AddPeers(nodes[i+1:]...) } - network = append(network, fullNodes...) + network = append(network, nodes...) // Give nodes some time to connect. Also there is a race condition in // miner.worker its field snapshotBlock is set only when new transactions From b18991f144c24abd01cb47128420a3970d4f8199 Mon Sep 17 00:00:00 2001 From: alecps Date: Mon, 19 Aug 2024 13:27:23 -0400 Subject: [PATCH 17/21] shorten chain to insert in test, fix timeout --- core/blockchain_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 9eebc973dc..a25a424ff0 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -216,7 +216,7 @@ func TestNoInsertPastL2MigrationBlock(t *testing.T) { migrationBlock := 2 blockchain.chainConfig.L2MigrationBlock = big.NewInt(int64(migrationBlock)) - blocks := makeBlockChain(blockchain.CurrentBlock(), 1000000, mockEngine.NewFullFaker(), blockchain.db, 0) + blocks := makeBlockChain(blockchain.CurrentBlock(), 10000, mockEngine.NewFullFaker(), blockchain.db, 0) failedBlock, err := blockchain.InsertChain(blocks) require.EqualError(t, err, errInsertionInterrupted.Error()) // Compare with migrationBlock-1 because failedBlock is the index of the failed block in the blocks[] array, not in the actual blockchain. From f8a3aacde22862a023c62cc2b7a569f279e0ef4b Mon Sep 17 00:00:00 2001 From: alecps Date: Mon, 19 Aug 2024 17:24:12 -0400 Subject: [PATCH 18/21] fixes test --- core/blockchain_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index a25a424ff0..3b63c0beb0 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -213,10 +213,11 @@ func TestNoInsertPastL2MigrationBlock(t *testing.T) { } defer blockchain.Stop() + blockchain.chainConfig = blockchain.chainConfig.DeepCopy() migrationBlock := 2 blockchain.chainConfig.L2MigrationBlock = big.NewInt(int64(migrationBlock)) - blocks := makeBlockChain(blockchain.CurrentBlock(), 10000, mockEngine.NewFullFaker(), blockchain.db, 0) + blocks := makeBlockChain(blockchain.CurrentBlock(), 100000, mockEngine.NewFullFaker(), blockchain.db, 0) failedBlock, err := blockchain.InsertChain(blocks) require.EqualError(t, err, errInsertionInterrupted.Error()) // Compare with migrationBlock-1 because failedBlock is the index of the failed block in the blocks[] array, not in the actual blockchain. From 8a287884b7ae36a1e6e72fc98f2c8662b999357e Mon Sep 17 00:00:00 2001 From: alecps Date: Mon, 16 Sep 2024 14:43:30 -0400 Subject: [PATCH 19/21] respond to some feedback --- e2e_test/e2e_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index 814756b030..d219182a37 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -383,14 +383,14 @@ func runStopNetworkAtL2BlockTest(ctx context.Context, t *testing.T, network test wg.Add(1) go func(n *test.Node) { defer wg.Done() - err = n.Tracker.AwaitBlock(shortCtx, l2Block.Uint64()) + err := n.Tracker.AwaitBlock(shortCtx, l2Block.Uint64()) require.EqualError(t, err, context.DeadlineExceeded.Error()) }(n) } wg.Wait() } -func TestStopNetworkAtl2BlockSimple(t *testing.T) { +func TestStopNetworkAtL2BlockSimple(t *testing.T) { numValidators := 3 numFullNodes := 2 numFastNodes := 1 From c7bea3217e78c76eb96958ccfd27592f99afaba4 Mon Sep 17 00:00:00 2001 From: alecps Date: Mon, 16 Sep 2024 15:44:27 -0400 Subject: [PATCH 20/21] use channel to collect timeout errors awaiting post migration block --- e2e_test/e2e_test.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index d219182a37..0fb57c4aa6 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -379,15 +379,24 @@ func runStopNetworkAtL2BlockTest(ctx context.Context, t *testing.T, network test // fail if any node adds a new block >= the migration block var wg sync.WaitGroup + errorChan := make(chan error, len(network)) + for _, n := range network { wg.Add(1) go func(n *test.Node) { defer wg.Done() err := n.Tracker.AwaitBlock(shortCtx, l2Block.Uint64()) - require.EqualError(t, err, context.DeadlineExceeded.Error()) + errorChan <- err }(n) } + wg.Wait() + close(errorChan) + + // Collect and check errors + for err := range errorChan { + require.EqualError(t, err, context.DeadlineExceeded.Error()) + } } func TestStopNetworkAtL2BlockSimple(t *testing.T) { From 7c566686c753f95d4738c9ee5aaf3637cce130b6 Mon Sep 17 00:00:00 2001 From: alecps Date: Mon, 16 Sep 2024 23:12:09 -0400 Subject: [PATCH 21/21] remove call to backend.Close() --- consensus/istanbul/backend/backend.go | 1 - 1 file changed, 1 deletion(-) diff --git a/consensus/istanbul/backend/backend.go b/consensus/istanbul/backend/backend.go index 047d539585..da28e9366e 100644 --- a/consensus/istanbul/backend/backend.go +++ b/consensus/istanbul/backend/backend.go @@ -559,7 +559,6 @@ func (sb *Backend) Commit(proposal istanbul.Proposal, aggregatedSeal types.Istan if sb.chain.Config().IsL2Migration(nextBlockNum) { sb.logger.Info("The next block is the L2 migration block, stopping announce protocol and closing istanbul backend", "currentBlock", block.NumberU64(), "hash", block.Hash(), "nextBlock", nextBlockNum) sb.StopAnnouncing() - sb.Close() } return nil }