diff --git a/arbnode/execution/blockchain.go b/arbnode/execution/blockchain.go index 0ce76d8ccd..230902726b 100644 --- a/arbnode/execution/blockchain.go +++ b/arbnode/execution/blockchain.go @@ -26,15 +26,17 @@ import ( ) type CachingConfig struct { - Archive bool `koanf:"archive"` - BlockCount uint64 `koanf:"block-count"` - BlockAge time.Duration `koanf:"block-age"` - TrieTimeLimit time.Duration `koanf:"trie-time-limit"` - TrieDirtyCache int `koanf:"trie-dirty-cache"` - TrieCleanCache int `koanf:"trie-clean-cache"` - SnapshotCache int `koanf:"snapshot-cache"` - DatabaseCache int `koanf:"database-cache"` - SnapshotRestoreGasLimit uint64 `koanf:"snapshot-restore-gas-limit"` + Archive bool `koanf:"archive"` + BlockCount uint64 `koanf:"block-count"` + BlockAge time.Duration `koanf:"block-age"` + TrieTimeLimit time.Duration `koanf:"trie-time-limit"` + TrieDirtyCache int `koanf:"trie-dirty-cache"` + TrieCleanCache int `koanf:"trie-clean-cache"` + SnapshotCache int `koanf:"snapshot-cache"` + DatabaseCache int `koanf:"database-cache"` + SnapshotRestoreGasLimit uint64 `koanf:"snapshot-restore-gas-limit"` + MaxNumberOfBlocksToSkipStateSaving uint32 `koanf:"max-number-of-blocks-to-skip-state-saving"` + MaxAmountOfGasToSkipStateSaving uint64 `koanf:"max-amount-of-gas-to-skip-state-saving"` } func CachingConfigAddOptions(prefix string, f *flag.FlagSet) { @@ -47,18 +49,22 @@ func CachingConfigAddOptions(prefix string, f *flag.FlagSet) { f.Int(prefix+".snapshot-cache", DefaultCachingConfig.SnapshotCache, "amount of memory in megabytes to cache state snapshots with") f.Int(prefix+".database-cache", DefaultCachingConfig.DatabaseCache, "amount of memory in megabytes to cache database contents with") f.Uint64(prefix+".snapshot-restore-gas-limit", DefaultCachingConfig.SnapshotRestoreGasLimit, "maximum gas rolled back to recover snapshot") + f.Uint32(prefix+".max-number-of-blocks-to-skip-state-saving", DefaultCachingConfig.MaxNumberOfBlocksToSkipStateSaving, "maximum number of blocks to skip state saving to persistent storage (archive node only)") + f.Uint64(prefix+".max-amount-of-gas-to-skip-state-saving", DefaultCachingConfig.MaxAmountOfGasToSkipStateSaving, "maximum amount of gas in blocks to skip saving state to Persistent storage (archive node only)") } var DefaultCachingConfig = CachingConfig{ - Archive: false, - BlockCount: 128, - BlockAge: 30 * time.Minute, - TrieTimeLimit: time.Hour, - TrieDirtyCache: 1024, - TrieCleanCache: 600, - SnapshotCache: 400, - DatabaseCache: 2048, - SnapshotRestoreGasLimit: 300_000_000_000, + Archive: false, + BlockCount: 128, + BlockAge: 30 * time.Minute, + TrieTimeLimit: time.Hour, + TrieDirtyCache: 1024, + TrieCleanCache: 600, + SnapshotCache: 400, + DatabaseCache: 2048, + SnapshotRestoreGasLimit: 300_000_000_000, + MaxNumberOfBlocksToSkipStateSaving: 127, + MaxAmountOfGasToSkipStateSaving: 15 * 1000 * 1000, } func DefaultCacheConfigFor(stack *node.Node, cachingConfig *CachingConfig) *core.CacheConfig { @@ -68,18 +74,20 @@ func DefaultCacheConfigFor(stack *node.Node, cachingConfig *CachingConfig) *core } return &core.CacheConfig{ - TrieCleanLimit: cachingConfig.TrieCleanCache, - TrieCleanJournal: stack.ResolvePath(baseConf.TrieCleanCacheJournal), - TrieCleanRejournal: baseConf.TrieCleanCacheRejournal, - TrieCleanNoPrefetch: baseConf.NoPrefetch, - TrieDirtyLimit: cachingConfig.TrieDirtyCache, - TrieDirtyDisabled: cachingConfig.Archive, - TrieTimeLimit: cachingConfig.TrieTimeLimit, - TriesInMemory: cachingConfig.BlockCount, - TrieRetention: cachingConfig.BlockAge, - SnapshotLimit: cachingConfig.SnapshotCache, - Preimages: baseConf.Preimages, - SnapshotRestoreMaxGas: cachingConfig.SnapshotRestoreGasLimit, + TrieCleanLimit: cachingConfig.TrieCleanCache, + TrieCleanJournal: stack.ResolvePath(baseConf.TrieCleanCacheJournal), + TrieCleanRejournal: baseConf.TrieCleanCacheRejournal, + TrieCleanNoPrefetch: baseConf.NoPrefetch, + TrieDirtyLimit: cachingConfig.TrieDirtyCache, + TrieDirtyDisabled: cachingConfig.Archive, + TrieTimeLimit: cachingConfig.TrieTimeLimit, + TriesInMemory: cachingConfig.BlockCount, + TrieRetention: cachingConfig.BlockAge, + SnapshotLimit: cachingConfig.SnapshotCache, + Preimages: baseConf.Preimages, + SnapshotRestoreMaxGas: cachingConfig.SnapshotRestoreGasLimit, + MaxNumberOfBlocksToSkipStateSaving: cachingConfig.MaxNumberOfBlocksToSkipStateSaving, + MaxAmountOfGasToSkipStateSaving: cachingConfig.MaxAmountOfGasToSkipStateSaving, } } diff --git a/go-ethereum b/go-ethereum index 45efc8230c..89f53b035d 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 45efc8230c2561cf56652dabccdd670101f75b0c +Subproject commit 89f53b035d7a8b9b1ff8599958bf0c55efcdf718 diff --git a/system_tests/arbtrace_test.go b/system_tests/arbtrace_test.go index 4aab5c71bd..78907aa622 100644 --- a/system_tests/arbtrace_test.go +++ b/system_tests/arbtrace_test.go @@ -147,7 +147,7 @@ func TestArbTraceForwarding(t *testing.T) { nodeConfig := arbnode.ConfigDefaultL1Test() nodeConfig.RPC.ClassicRedirect = ipcPath nodeConfig.RPC.ClassicRedirectTimeout = time.Second - _, _, _, l2stack, _, _, _, l1stack := createTestNodeOnL1WithConfigImpl(t, ctx, true, nodeConfig, nil, nil, nil, nil) + _, _, _, l2stack, _, _, _, l1stack := createTestNodeOnL1WithConfigImpl(t, ctx, true, nodeConfig, nil, nil, nil) defer requireClose(t, l1stack) defer requireClose(t, l2stack) diff --git a/system_tests/common_test.go b/system_tests/common_test.go index 9fd002bd94..7bc1c11758 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -499,13 +499,13 @@ func DeployOnTestL1( } func createL2BlockChain( - t *testing.T, l2info *BlockchainTestInfo, dataDir string, chainConfig *params.ChainConfig, cacheConfig *core.CacheConfig, + t *testing.T, l2info *BlockchainTestInfo, dataDir string, chainConfig *params.ChainConfig, cacheConfig *execution.CachingConfig, ) (*BlockchainTestInfo, *node.Node, ethdb.Database, ethdb.Database, *core.BlockChain) { return createL2BlockChainWithStackConfig(t, l2info, dataDir, chainConfig, nil, nil, cacheConfig) } func createL2BlockChainWithStackConfig( - t *testing.T, l2info *BlockchainTestInfo, dataDir string, chainConfig *params.ChainConfig, initMessage *arbostypes.ParsedInitMessage, stackConfig *node.Config, cacheConfig *core.CacheConfig, + t *testing.T, l2info *BlockchainTestInfo, dataDir string, chainConfig *params.ChainConfig, initMessage *arbostypes.ParsedInitMessage, stackConfig *node.Config, cacheConfig *execution.CachingConfig, ) (*BlockchainTestInfo, *node.Node, ethdb.Database, ethdb.Database, *core.BlockChain) { if l2info == nil { l2info = NewArbTestInfo(t, chainConfig.ChainID) @@ -536,7 +536,11 @@ func createL2BlockChainWithStackConfig( SerializedChainConfig: serializedChainConfig, } } - blockchain, err := execution.WriteOrTestBlockChain(chainDb, cacheConfig, initReader, chainConfig, initMessage, arbnode.ConfigDefaultL2Test().TxLookupLimit, 0) + var coreCacheConfig *core.CacheConfig + if cacheConfig != nil { + coreCacheConfig = execution.DefaultCacheConfigFor(stack, cacheConfig) + } + blockchain, err := execution.WriteOrTestBlockChain(chainDb, coreCacheConfig, initReader, chainConfig, initMessage, arbnode.ConfigDefaultL2Test().TxLookupLimit, 0) Require(t, err) return l2info, stack, chainDb, arbDb, blockchain @@ -571,7 +575,7 @@ func createTestNodeOnL1WithConfig( l2info info, currentNode *arbnode.Node, l2client *ethclient.Client, l1info info, l1backend *eth.Ethereum, l1client *ethclient.Client, l1stack *node.Node, ) { - l2info, currentNode, l2client, _, l1info, l1backend, l1client, l1stack = createTestNodeOnL1WithConfigImpl(t, ctx, isSequencer, nodeConfig, chainConfig, stackConfig, nil, nil) + l2info, currentNode, l2client, _, l1info, l1backend, l1client, l1stack = createTestNodeOnL1WithConfigImpl(t, ctx, isSequencer, nodeConfig, chainConfig, stackConfig, nil) return } @@ -582,7 +586,6 @@ func createTestNodeOnL1WithConfigImpl( nodeConfig *arbnode.Config, chainConfig *params.ChainConfig, stackConfig *node.Config, - cacheConfig *core.CacheConfig, l2info_in info, ) ( l2info info, currentNode *arbnode.Node, l2client *ethclient.Client, l2stack *node.Node, @@ -604,7 +607,7 @@ func createTestNodeOnL1WithConfigImpl( l2info = NewArbTestInfo(t, chainConfig.ChainID) } addresses, initMessage := DeployOnTestL1(t, ctx, l1info, l1client, chainConfig) - _, l2stack, l2chainDb, l2arbDb, l2blockchain = createL2BlockChainWithStackConfig(t, l2info, "", chainConfig, initMessage, stackConfig, cacheConfig) + _, l2stack, l2chainDb, l2arbDb, l2blockchain = createL2BlockChainWithStackConfig(t, l2info, "", chainConfig, initMessage, stackConfig, &nodeConfig.Caching) var sequencerTxOptsPtr *bind.TransactOpts var dataSigner signature.DataSignerFunc if isSequencer { @@ -650,7 +653,7 @@ func CreateTestL2WithConfig( AddDefaultValNode(t, ctx, nodeConfig, true) - l2info, stack, chainDb, arbDb, blockchain := createL2BlockChain(t, l2Info, "", params.ArbitrumDevTestChainConfig(), nil) + l2info, stack, chainDb, arbDb, blockchain := createL2BlockChain(t, l2Info, "", params.ArbitrumDevTestChainConfig(), &nodeConfig.Caching) currentNode, err := arbnode.CreateNode(ctx, stack, chainDb, arbDb, NewFetcherFromConfig(nodeConfig), blockchain, nil, nil, nil, nil, nil, feedErrChan) Require(t, err) @@ -755,7 +758,9 @@ func Create2ndNodeWithConfig( txOpts := l1info.GetDefaultTransactOpts("Sequencer", ctx) chainConfig := first.Execution.ArbInterface.BlockChain().Config() initMessage := getInitMessage(ctx, t, l1client, first.DeployInfo) - l2blockchain, err := execution.WriteOrTestBlockChain(l2chainDb, nil, initReader, chainConfig, initMessage, arbnode.ConfigDefaultL2Test().TxLookupLimit, 0) + + coreCacheConfig := execution.DefaultCacheConfigFor(l2stack, &nodeConfig.Caching) + l2blockchain, err := execution.WriteOrTestBlockChain(l2chainDb, coreCacheConfig, initReader, chainConfig, initMessage, arbnode.ConfigDefaultL2Test().TxLookupLimit, 0) Require(t, err) AddDefaultValNode(t, ctx, nodeConfig, true) diff --git a/system_tests/debugapi_test.go b/system_tests/debugapi_test.go index ff28e2350c..03e3dfd405 100644 --- a/system_tests/debugapi_test.go +++ b/system_tests/debugapi_test.go @@ -14,7 +14,7 @@ import ( func TestDebugAPI(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - _, _, _, l2stack, _, _, _, l1stack := createTestNodeOnL1WithConfigImpl(t, ctx, true, nil, nil, nil, nil, nil) + _, _, _, l2stack, _, _, _, l1stack := createTestNodeOnL1WithConfigImpl(t, ctx, true, nil, nil, nil, nil) defer requireClose(t, l1stack) defer requireClose(t, l2stack) diff --git a/system_tests/recreatestate_rpc_test.go b/system_tests/recreatestate_rpc_test.go index 561085c3f3..dbf68c8479 100644 --- a/system_tests/recreatestate_rpc_test.go +++ b/system_tests/recreatestate_rpc_test.go @@ -6,7 +6,6 @@ import ( "math/big" "strings" "testing" - "time" "github.com/ethereum/go-ethereum/arbitrum" "github.com/ethereum/go-ethereum/common" @@ -18,32 +17,13 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" "github.com/offchainlabs/nitro/arbnode" + "github.com/offchainlabs/nitro/arbnode/execution" "github.com/offchainlabs/nitro/util" - "github.com/offchainlabs/nitro/util/testhelpers" ) -func prepareNodeWithHistory(t *testing.T, ctx context.Context, maxRecreateStateDepth int64, txCount uint64) (node *arbnode.Node, bc *core.BlockChain, db ethdb.Database, l2client *ethclient.Client, l2info info, cancel func()) { +func prepareNodeWithHistory(t *testing.T, ctx context.Context, nodeConfig *arbnode.Config, txCount uint64) (node *arbnode.Node, executionNode *execution.ExecutionNode, l2client *ethclient.Client, cancel func()) { t.Helper() - nodeConfig := arbnode.ConfigDefaultL1Test() - nodeConfig.RPC.MaxRecreateStateDepth = maxRecreateStateDepth - nodeConfig.Sequencer.MaxBlockSpeed = 0 - nodeConfig.Sequencer.MaxTxDataSize = 150 // 1 test tx ~= 110 - cacheConfig := &core.CacheConfig{ - // Arbitrum Config Options - TriesInMemory: 128, - TrieRetention: 30 * time.Minute, - - // disable caching of states in BlockChain.stateCache - TrieCleanLimit: 0, - TrieDirtyLimit: 0, - - TrieDirtyDisabled: true, - - TrieTimeLimit: 5 * time.Minute, - SnapshotLimit: 256, - SnapshotWait: true, - } - l2info, node, l2client, _, _, _, _, l1stack := createTestNodeOnL1WithConfigImpl(t, ctx, true, nodeConfig, nil, nil, cacheConfig, nil) + l2info, node, l2client, _, _, _, l1stack := createTestNodeOnL1WithConfig(t, ctx, true, nodeConfig, nil, nil) cancel = func() { defer requireClose(t, l1stack) defer node.StopAndWait() @@ -54,16 +34,13 @@ func prepareNodeWithHistory(t *testing.T, ctx context.Context, maxRecreateStateD tx := l2info.PrepareTx("Owner", "User2", l2info.TransferGas, common.Big1, nil) txs = append(txs, tx) err := l2client.SendTransaction(ctx, tx) - testhelpers.RequireImpl(t, err) + Require(t, err) } for _, tx := range txs { _, err := EnsureTxSucceeded(ctx, l2client, tx) - testhelpers.RequireImpl(t, err) + Require(t, err) } - bc = node.Execution.Backend.ArbInterface().BlockChain() - db = node.Execution.Backend.ChainDb() - - return + return node, node.Execution, l2client, cancel } func fillHeaderCache(t *testing.T, bc *core.BlockChain, from, to uint64) { @@ -71,7 +48,7 @@ func fillHeaderCache(t *testing.T, bc *core.BlockChain, from, to uint64) { for i := from; i <= to; i++ { header := bc.GetHeaderByNumber(i) if header == nil { - testhelpers.FailImpl(t, "internal test error - failed to get header while trying to fill headerCache, header:", i) + Fatal(t, "internal test error - failed to get header while trying to fill headerCache, header:", i) } } } @@ -81,7 +58,7 @@ func fillBlockCache(t *testing.T, bc *core.BlockChain, from, to uint64) { for i := from; i <= to; i++ { block := bc.GetBlockByNumber(i) if block == nil { - testhelpers.FailImpl(t, "internal test error - failed to get block while trying to fill blockCache, block:", i) + Fatal(t, "internal test error - failed to get block while trying to fill blockCache, block:", i) } } } @@ -91,21 +68,21 @@ func removeStatesFromDb(t *testing.T, bc *core.BlockChain, db ethdb.Database, fr for i := from; i <= to; i++ { header := bc.GetHeaderByNumber(i) if header == nil { - testhelpers.FailImpl(t, "failed to get last block header") + Fatal(t, "failed to get last block header") } hash := header.Root err := db.Delete(hash.Bytes()) - testhelpers.RequireImpl(t, err) + Require(t, err) } for i := from; i <= to; i++ { header := bc.GetHeaderByNumber(i) _, err := bc.StateAt(header.Root) if err == nil { - testhelpers.FailImpl(t, "internal test error - failed to remove state from db") + Fatal(t, "internal test error - failed to remove state from db") } expectedErr := &trie.MissingNodeError{} if !errors.As(err, &expectedErr) { - testhelpers.FailImpl(t, "internal test error - failed to remove state from db, err: ", err) + Fatal(t, "internal test error - failed to remove state from db, err: ", err) } } } @@ -113,22 +90,34 @@ func removeStatesFromDb(t *testing.T, bc *core.BlockChain, db ethdb.Database, fr func TestRecreateStateForRPCNoDepthLimit(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - _, bc, db, l2client, _, cancelNode := prepareNodeWithHistory(t, ctx, arbitrum.InfiniteMaxRecreateStateDepth, 32) + nodeConfig := arbnode.ConfigDefaultL1Test() + nodeConfig.RPC.MaxRecreateStateDepth = arbitrum.InfiniteMaxRecreateStateDepth + nodeConfig.Sequencer.MaxBlockSpeed = 0 + nodeConfig.Sequencer.MaxTxDataSize = 150 // 1 test tx ~= 110 + nodeConfig.Caching.Archive = true + // disable caching of states in BlockChain.stateCache + nodeConfig.Caching.TrieCleanCache = 0 + nodeConfig.Caching.TrieDirtyCache = 0 + nodeConfig.Caching.MaxNumberOfBlocksToSkipStateSaving = 0 + nodeConfig.Caching.MaxAmountOfGasToSkipStateSaving = 0 + _, execNode, l2client, cancelNode := prepareNodeWithHistory(t, ctx, nodeConfig, 32) defer cancelNode() + bc := execNode.Backend.ArbInterface().BlockChain() + db := execNode.Backend.ChainDb() lastBlock, err := l2client.BlockNumber(ctx) - testhelpers.RequireImpl(t, err) + Require(t, err) middleBlock := lastBlock / 2 expectedBalance, err := l2client.BalanceAt(ctx, GetTestAddressForAccountName(t, "User2"), new(big.Int).SetUint64(lastBlock)) - testhelpers.RequireImpl(t, err) + Require(t, err) removeStatesFromDb(t, bc, db, middleBlock, lastBlock) balance, err := l2client.BalanceAt(ctx, GetTestAddressForAccountName(t, "User2"), new(big.Int).SetUint64(lastBlock)) - testhelpers.RequireImpl(t, err) + Require(t, err) if balance.Cmp(expectedBalance) != 0 { - testhelpers.FailImpl(t, "unexpected balance result for last block, want: ", expectedBalance, " have: ", balance) + Fatal(t, "unexpected balance result for last block, want: ", expectedBalance, " have: ", balance) } } @@ -137,22 +126,34 @@ func TestRecreateStateForRPCBigEnoughDepthLimit(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() depthGasLimit := int64(256 * util.NormalizeL2GasForL1GasInitial(800_000, params.GWei)) - _, bc, db, l2client, _, cancelNode := prepareNodeWithHistory(t, ctx, depthGasLimit, 32) + nodeConfig := arbnode.ConfigDefaultL1Test() + nodeConfig.RPC.MaxRecreateStateDepth = depthGasLimit + nodeConfig.Sequencer.MaxBlockSpeed = 0 + nodeConfig.Sequencer.MaxTxDataSize = 150 // 1 test tx ~= 110 + nodeConfig.Caching.Archive = true + // disable caching of states in BlockChain.stateCache + nodeConfig.Caching.TrieCleanCache = 0 + nodeConfig.Caching.TrieDirtyCache = 0 + nodeConfig.Caching.MaxNumberOfBlocksToSkipStateSaving = 0 + nodeConfig.Caching.MaxAmountOfGasToSkipStateSaving = 0 + _, execNode, l2client, cancelNode := prepareNodeWithHistory(t, ctx, nodeConfig, 32) defer cancelNode() + bc := execNode.Backend.ArbInterface().BlockChain() + db := execNode.Backend.ChainDb() lastBlock, err := l2client.BlockNumber(ctx) - testhelpers.RequireImpl(t, err) + Require(t, err) middleBlock := lastBlock / 2 expectedBalance, err := l2client.BalanceAt(ctx, GetTestAddressForAccountName(t, "User2"), new(big.Int).SetUint64(lastBlock)) - testhelpers.RequireImpl(t, err) + Require(t, err) removeStatesFromDb(t, bc, db, middleBlock, lastBlock) balance, err := l2client.BalanceAt(ctx, GetTestAddressForAccountName(t, "User2"), new(big.Int).SetUint64(lastBlock)) - testhelpers.RequireImpl(t, err) + Require(t, err) if balance.Cmp(expectedBalance) != 0 { - testhelpers.FailImpl(t, "unexpected balance result for last block, want: ", expectedBalance, " have: ", balance) + Fatal(t, "unexpected balance result for last block, want: ", expectedBalance, " have: ", balance) } } @@ -160,22 +161,33 @@ func TestRecreateStateForRPCBigEnoughDepthLimit(t *testing.T) { func TestRecreateStateForRPCDepthLimitExceeded(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - depthGasLimit := int64(200) - _, bc, db, l2client, _, cancelNode := prepareNodeWithHistory(t, ctx, depthGasLimit, 32) + nodeConfig := arbnode.ConfigDefaultL1Test() + nodeConfig.RPC.MaxRecreateStateDepth = int64(200) + nodeConfig.Sequencer.MaxBlockSpeed = 0 + nodeConfig.Sequencer.MaxTxDataSize = 150 // 1 test tx ~= 110 + nodeConfig.Caching.Archive = true + // disable caching of states in BlockChain.stateCache + nodeConfig.Caching.TrieCleanCache = 0 + nodeConfig.Caching.TrieDirtyCache = 0 + nodeConfig.Caching.MaxNumberOfBlocksToSkipStateSaving = 0 + nodeConfig.Caching.MaxAmountOfGasToSkipStateSaving = 0 + _, execNode, l2client, cancelNode := prepareNodeWithHistory(t, ctx, nodeConfig, 32) defer cancelNode() + bc := execNode.Backend.ArbInterface().BlockChain() + db := execNode.Backend.ChainDb() lastBlock, err := l2client.BlockNumber(ctx) - testhelpers.RequireImpl(t, err) + Require(t, err) middleBlock := lastBlock / 2 removeStatesFromDb(t, bc, db, middleBlock, lastBlock) _, err = l2client.BalanceAt(ctx, GetTestAddressForAccountName(t, "User2"), new(big.Int).SetUint64(lastBlock)) if err == nil { - testhelpers.FailImpl(t, "Didn't fail as expected") + Fatal(t, "Didn't fail as expected") } if err.Error() != arbitrum.ErrDepthLimitExceeded.Error() { - testhelpers.FailImpl(t, "Failed with unexpected error:", err) + Fatal(t, "Failed with unexpected error:", err) } } @@ -184,13 +196,25 @@ func TestRecreateStateForRPCMissingBlockParent(t *testing.T) { var headerCacheLimit uint64 = 512 ctx, cancel := context.WithCancel(context.Background()) defer cancel() - _, bc, db, l2client, _, cancelNode := prepareNodeWithHistory(t, ctx, arbitrum.InfiniteMaxRecreateStateDepth, headerCacheLimit+5) + nodeConfig := arbnode.ConfigDefaultL1Test() + nodeConfig.RPC.MaxRecreateStateDepth = arbitrum.InfiniteMaxRecreateStateDepth + nodeConfig.Sequencer.MaxBlockSpeed = 0 + nodeConfig.Sequencer.MaxTxDataSize = 150 // 1 test tx ~= 110 + nodeConfig.Caching.Archive = true + // disable caching of states in BlockChain.stateCache + nodeConfig.Caching.TrieCleanCache = 0 + nodeConfig.Caching.TrieDirtyCache = 0 + nodeConfig.Caching.MaxNumberOfBlocksToSkipStateSaving = 0 + nodeConfig.Caching.MaxAmountOfGasToSkipStateSaving = 0 + _, execNode, l2client, cancelNode := prepareNodeWithHistory(t, ctx, nodeConfig, headerCacheLimit+5) defer cancelNode() + bc := execNode.Backend.ArbInterface().BlockChain() + db := execNode.Backend.ChainDb() lastBlock, err := l2client.BlockNumber(ctx) - testhelpers.RequireImpl(t, err) + Require(t, err) if lastBlock < headerCacheLimit+4 { - testhelpers.FailImpl(t, "Internal test error - not enough blocks produced during preparation, want:", headerCacheLimit, "have:", lastBlock) + Fatal(t, "Internal test error - not enough blocks produced during preparation, want:", headerCacheLimit, "have:", lastBlock) } removeStatesFromDb(t, bc, db, lastBlock-4, lastBlock) @@ -206,10 +230,10 @@ func TestRecreateStateForRPCMissingBlockParent(t *testing.T) { _, err = l2client.BalanceAt(ctx, GetTestAddressForAccountName(t, "User2"), new(big.Int).SetUint64(i)) if err == nil { hash := rawdb.ReadCanonicalHash(db, i) - testhelpers.FailImpl(t, "Didn't fail to get balance at block:", i, " with hash:", hash, ", lastBlock:", lastBlock) + Fatal(t, "Didn't fail to get balance at block:", i, " with hash:", hash, ", lastBlock:", lastBlock) } if !strings.Contains(err.Error(), "chain doesn't contain parent of block") { - testhelpers.FailImpl(t, "Failed with unexpected error: \"", err, "\", at block:", i, "lastBlock:", lastBlock) + Fatal(t, "Failed with unexpected error: \"", err, "\", at block:", i, "lastBlock:", lastBlock) } } } @@ -217,11 +241,24 @@ func TestRecreateStateForRPCMissingBlockParent(t *testing.T) { func TestRecreateStateForRPCBeyondGenesis(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - _, bc, db, l2client, _, cancelNode := prepareNodeWithHistory(t, ctx, arbitrum.InfiniteMaxRecreateStateDepth, 32) + + nodeConfig := arbnode.ConfigDefaultL1Test() + nodeConfig.RPC.MaxRecreateStateDepth = arbitrum.InfiniteMaxRecreateStateDepth + nodeConfig.Sequencer.MaxBlockSpeed = 0 + nodeConfig.Sequencer.MaxTxDataSize = 150 // 1 test tx ~= 110 + nodeConfig.Caching.Archive = true + // disable caching of states in BlockChain.stateCache + nodeConfig.Caching.TrieCleanCache = 0 + nodeConfig.Caching.TrieDirtyCache = 0 + nodeConfig.Caching.MaxNumberOfBlocksToSkipStateSaving = 0 + nodeConfig.Caching.MaxAmountOfGasToSkipStateSaving = 0 + _, execNode, l2client, cancelNode := prepareNodeWithHistory(t, ctx, nodeConfig, 32) defer cancelNode() + bc := execNode.Backend.ArbInterface().BlockChain() + db := execNode.Backend.ChainDb() lastBlock, err := l2client.BlockNumber(ctx) - testhelpers.RequireImpl(t, err) + Require(t, err) genesis := bc.Config().ArbitrumChainParams.GenesisBlockNum removeStatesFromDb(t, bc, db, genesis, lastBlock) @@ -229,10 +266,10 @@ func TestRecreateStateForRPCBeyondGenesis(t *testing.T) { _, err = l2client.BalanceAt(ctx, GetTestAddressForAccountName(t, "User2"), new(big.Int).SetUint64(lastBlock)) if err == nil { hash := rawdb.ReadCanonicalHash(db, lastBlock) - testhelpers.FailImpl(t, "Didn't fail to get balance at block:", lastBlock, " with hash:", hash, ", lastBlock:", lastBlock) + Fatal(t, "Didn't fail to get balance at block:", lastBlock, " with hash:", hash, ", lastBlock:", lastBlock) } if !strings.Contains(err.Error(), "moved beyond genesis") { - testhelpers.FailImpl(t, "Failed with unexpected error: \"", err, "\", at block:", lastBlock, "lastBlock:", lastBlock) + Fatal(t, "Failed with unexpected error: \"", err, "\", at block:", lastBlock, "lastBlock:", lastBlock) } } @@ -241,13 +278,25 @@ func TestRecreateStateForRPCBlockNotFoundWhileRecreating(t *testing.T) { var blockCacheLimit uint64 = 256 ctx, cancel := context.WithCancel(context.Background()) defer cancel() - _, bc, db, l2client, _, cancelNode := prepareNodeWithHistory(t, ctx, arbitrum.InfiniteMaxRecreateStateDepth, blockCacheLimit+4) + nodeConfig := arbnode.ConfigDefaultL1Test() + nodeConfig.RPC.MaxRecreateStateDepth = arbitrum.InfiniteMaxRecreateStateDepth + nodeConfig.Sequencer.MaxBlockSpeed = 0 + nodeConfig.Sequencer.MaxTxDataSize = 150 // 1 test tx ~= 110 + nodeConfig.Caching.Archive = true + // disable caching of states in BlockChain.stateCache + nodeConfig.Caching.TrieCleanCache = 0 + nodeConfig.Caching.TrieDirtyCache = 0 + nodeConfig.Caching.MaxNumberOfBlocksToSkipStateSaving = 0 + nodeConfig.Caching.MaxAmountOfGasToSkipStateSaving = 0 + _, execNode, l2client, cancelNode := prepareNodeWithHistory(t, ctx, nodeConfig, blockCacheLimit+4) defer cancelNode() + bc := execNode.Backend.ArbInterface().BlockChain() + db := execNode.Backend.ChainDb() lastBlock, err := l2client.BlockNumber(ctx) - testhelpers.RequireImpl(t, err) + Require(t, err) if lastBlock < blockCacheLimit+4 { - testhelpers.FailImpl(t, "Internal test error - not enough blocks produced during preparation, want:", blockCacheLimit, "have:", lastBlock) + Fatal(t, "Internal test error - not enough blocks produced during preparation, want:", blockCacheLimit, "have:", lastBlock) } removeStatesFromDb(t, bc, db, lastBlock-4, lastBlock) @@ -262,9 +311,150 @@ func TestRecreateStateForRPCBlockNotFoundWhileRecreating(t *testing.T) { _, err = l2client.BalanceAt(ctx, GetTestAddressForAccountName(t, "User2"), new(big.Int).SetUint64(lastBlock)) if err == nil { hash := rawdb.ReadCanonicalHash(db, lastBlock) - testhelpers.FailImpl(t, "Didn't fail to get balance at block:", lastBlock, " with hash:", hash, ", lastBlock:", lastBlock) + Fatal(t, "Didn't fail to get balance at block:", lastBlock, " with hash:", hash, ", lastBlock:", lastBlock) } if !strings.Contains(err.Error(), "block not found while recreating") { - testhelpers.FailImpl(t, "Failed with unexpected error: \"", err, "\", at block:", lastBlock, "lastBlock:", lastBlock) + Fatal(t, "Failed with unexpected error: \"", err, "\", at block:", lastBlock, "lastBlock:", lastBlock) + } +} + +func testSkippingSavingStateAndRecreatingAfterRestart(t *testing.T, cacheConfig *execution.CachingConfig, txCount int) { + maxRecreateStateDepth := int64(30 * 1000 * 1000) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ctx1, cancel1 := context.WithCancel(ctx) + nodeConfig := arbnode.ConfigDefaultL2Test() + nodeConfig.RPC.MaxRecreateStateDepth = maxRecreateStateDepth + nodeConfig.Sequencer.MaxBlockSpeed = 0 + nodeConfig.Sequencer.MaxTxDataSize = 150 // 1 test tx ~= 110 + nodeConfig.Caching = *cacheConfig + + skipBlocks := nodeConfig.Caching.MaxNumberOfBlocksToSkipStateSaving + skipGas := nodeConfig.Caching.MaxAmountOfGasToSkipStateSaving + + feedErrChan := make(chan error, 10) + AddDefaultValNode(t, ctx1, nodeConfig, true) + l2info, stack, chainDb, arbDb, blockchain := createL2BlockChain(t, nil, t.TempDir(), params.ArbitrumDevTestChainConfig(), &nodeConfig.Caching) + + node, err := arbnode.CreateNode(ctx1, stack, chainDb, arbDb, NewFetcherFromConfig(nodeConfig), blockchain, nil, nil, nil, nil, nil, feedErrChan) + Require(t, err) + err = node.TxStreamer.AddFakeInitMessage() + Require(t, err) + Require(t, node.Start(ctx1)) + client := ClientForStack(t, stack) + + StartWatchChanErr(t, ctx, feedErrChan, node) + dataDir := node.Stack.DataDir() + + l2info.GenerateAccount("User2") + var txs []*types.Transaction + for i := 0; i < txCount; i++ { + tx := l2info.PrepareTx("Owner", "User2", l2info.TransferGas, common.Big1, nil) + txs = append(txs, tx) + err := client.SendTransaction(ctx, tx) + Require(t, err) + receipt, err := EnsureTxSucceeded(ctx, client, tx) + Require(t, err) + if have, want := receipt.BlockNumber.Uint64(), uint64(i)+1; have != want { + Fatal(t, "internal test error - tx got included in unexpected block number, have:", have, "want:", want) + } + } + genesis := uint64(0) + lastBlock, err := client.BlockNumber(ctx) + Require(t, err) + if want := genesis + uint64(txCount); lastBlock < want { + Fatal(t, "internal test error - not enough blocks produced during preparation, want:", want, "have:", lastBlock) + } + expectedBalance, err := client.BalanceAt(ctx, GetTestAddressForAccountName(t, "User2"), new(big.Int).SetUint64(lastBlock)) + Require(t, err) + + node.StopAndWait() + cancel1() + t.Log("stopped first node") + + AddDefaultValNode(t, ctx, nodeConfig, true) + l2info, stack, chainDb, arbDb, blockchain = createL2BlockChain(t, l2info, dataDir, params.ArbitrumDevTestChainConfig(), &nodeConfig.Caching) + node, err = arbnode.CreateNode(ctx, stack, chainDb, arbDb, NewFetcherFromConfig(nodeConfig), blockchain, nil, node.DeployInfo, nil, nil, nil, feedErrChan) + Require(t, err) + Require(t, node.Start(ctx)) + client = ClientForStack(t, stack) + defer node.StopAndWait() + bc := node.Execution.Backend.ArbInterface().BlockChain() + gas := skipGas + blocks := skipBlocks + for i := genesis + 1; i <= genesis+uint64(txCount); i++ { + block := bc.GetBlockByNumber(i) + if block == nil { + Fatal(t, "header not found for block number:", i) + continue + } + gas += block.GasUsed() + blocks++ + _, err := bc.StateAt(block.Root()) + if (skipBlocks == 0 && skipGas == 0) || (skipBlocks != 0 && blocks > skipBlocks) || (skipGas != 0 && gas > skipGas) { + if err != nil { + t.Log("blocks:", blocks, "skipBlocks:", skipBlocks, "gas:", gas, "skipGas:", skipGas) + } + Require(t, err, "state not found, root:", block.Root(), "blockNumber:", i, "blockHash", block.Hash(), "err:", err) + gas = 0 + blocks = 0 + } else { + if err == nil { + t.Log("blocks:", blocks, "skipBlocks:", skipBlocks, "gas:", gas, "skipGas:", skipGas) + Fatal(t, "state shouldn't be available, root:", block.Root(), "blockNumber:", i, "blockHash", block.Hash()) + } + expectedErr := &trie.MissingNodeError{} + if !errors.As(err, &expectedErr) { + Fatal(t, "getting state failed with unexpected error, root:", block.Root(), "blockNumber:", i, "blockHash", block.Hash()) + } + } + } + for i := genesis + 1; i <= genesis+uint64(txCount); i += i % 10 { + _, err = client.BalanceAt(ctx, GetTestAddressForAccountName(t, "User2"), new(big.Int).SetUint64(i)) + Require(t, err) + } + + balance, err := client.BalanceAt(ctx, GetTestAddressForAccountName(t, "User2"), new(big.Int).SetUint64(lastBlock)) + Require(t, err) + if balance.Cmp(expectedBalance) != 0 { + Fatal(t, "unexpected balance result for last block, want: ", expectedBalance, " have: ", balance) + } +} + +func TestSkippingSavingStateAndRecreatingAfterRestart(t *testing.T) { + cacheConfig := execution.DefaultCachingConfig + cacheConfig.Archive = true + // disable caching of states in BlockChain.stateCache + cacheConfig.TrieCleanCache = 0 + cacheConfig.TrieDirtyCache = 0 + // test defaults + testSkippingSavingStateAndRecreatingAfterRestart(t, &cacheConfig, 512) + + cacheConfig.MaxNumberOfBlocksToSkipStateSaving = 127 + cacheConfig.MaxAmountOfGasToSkipStateSaving = 0 + testSkippingSavingStateAndRecreatingAfterRestart(t, &cacheConfig, 512) + + cacheConfig.MaxNumberOfBlocksToSkipStateSaving = 0 + cacheConfig.MaxAmountOfGasToSkipStateSaving = 15 * 1000 * 1000 + testSkippingSavingStateAndRecreatingAfterRestart(t, &cacheConfig, 512) + + cacheConfig.MaxNumberOfBlocksToSkipStateSaving = 127 + cacheConfig.MaxAmountOfGasToSkipStateSaving = 15 * 1000 * 1000 + testSkippingSavingStateAndRecreatingAfterRestart(t, &cacheConfig, 512) + + // one test block ~ 925000 gas + testBlockGas := uint64(925000) + skipBlockValues := []uint64{0, 1, 2, 3, 5, 21, 51, 100, 101} + var skipGasValues []uint64 + for _, i := range skipBlockValues { + skipGasValues = append(skipGasValues, i*testBlockGas) + } + for _, skipGas := range skipGasValues { + for _, skipBlocks := range skipBlockValues[:len(skipBlockValues)-2] { + cacheConfig.MaxAmountOfGasToSkipStateSaving = skipGas + cacheConfig.MaxNumberOfBlocksToSkipStateSaving = uint32(skipBlocks) + testSkippingSavingStateAndRecreatingAfterRestart(t, &cacheConfig, 100) + } } } diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index 96ea1ee2e7..15929067f4 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -64,7 +64,7 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) types.NewArbitrumSigner(types.NewLondonSigner(l2chainConfig.ChainID)), big.NewInt(l2pricing.InitialBaseFeeWei*2), transferGas, ) - _, l2nodeA, l2clientA, _, l1info, _, l1client, l1stack := createTestNodeOnL1WithConfigImpl(t, ctx, true, nil, l2chainConfig, nil, nil, l2info) + _, l2nodeA, l2clientA, _, l1info, _, l1client, l1stack := createTestNodeOnL1WithConfigImpl(t, ctx, true, nil, l2chainConfig, nil, l2info) defer requireClose(t, l1stack) defer l2nodeA.StopAndWait()