From 535662154acab724a1f33d010d4f65d3da456f22 Mon Sep 17 00:00:00 2001 From: Pepper Lebeck-Jobe Date: Fri, 31 Jan 2025 13:03:20 +0100 Subject: [PATCH] Add a test for storage trie determinism This includes a new BigMap contract in the nitro-contracts repo in the mocks package. The test attempts to set a lot of storage slots to some value in one transaction, and then to clear 75% of them and add 10% more values in a second transaction. Finally, validation is run to ensure that the validator is behaving correctly. The idea is that this test will pass validation with the state db's deterministic flag set, but fail without it set. Related to: NIT-3068 --- contracts | 2 +- system_tests/common_test.go | 13 ++++++ system_tests/storage_trie_test.go | 74 +++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 system_tests/storage_trie_test.go diff --git a/contracts b/contracts index 763bd77906..24c42972bf 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 763bd77906b7677da691eaa31c6e195d455197a4 +Subproject commit 24c42972bf5ff9718218a17e5d3024c8b7643476 diff --git a/system_tests/common_test.go b/system_tests/common_test.go index 94218c7fa9..09bc20d948 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -149,6 +149,10 @@ func (tc *TestClient) SendWaitTestTransactions(t *testing.T, txs []*types.Transa return SendWaitTestTransactions(t, tc.ctx, tc.Client, txs) } +func (tc *TestClient) DeployBigMap(t *testing.T, auth bind.TransactOpts) (common.Address, *mocksgen.BigMap) { + return deployBigMap(t, tc.ctx, auth, tc.Client) +} + func (tc *TestClient) DeploySimple(t *testing.T, auth bind.TransactOpts) (common.Address, *mocksgen.Simple) { return deploySimple(t, tc.ctx, auth, tc.Client) } @@ -1713,6 +1717,15 @@ func getDeadlineTimeout(t *testing.T, defaultTimeout time.Duration) time.Duratio return timeout } +func deployBigMap(t *testing.T, ctx context.Context, auth bind.TransactOpts, client *ethclient.Client, +) (common.Address, *mocksgen.BigMap) { + addr, tx, bigMap, err := mocksgen.DeployBigMap(&auth, client) + Require(t, err, "could not deploy BigMap.sol contract") + _, err = EnsureTxSucceeded(ctx, client, tx) + Require(t, err) + return addr, bigMap +} + func deploySimple( t *testing.T, ctx context.Context, auth bind.TransactOpts, client *ethclient.Client, ) (common.Address, *mocksgen.Simple) { diff --git a/system_tests/storage_trie_test.go b/system_tests/storage_trie_test.go new file mode 100644 index 0000000000..0208c11900 --- /dev/null +++ b/system_tests/storage_trie_test.go @@ -0,0 +1,74 @@ +// Copyright 2025, Offchain Labs, Inc. +// For license information, see: +// https://github.com/OffchainLabs/nitro/blob/master/LICENSE.md + +package arbtest + +import ( + "context" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/core/rawdb" + + "github.com/offchainlabs/nitro/util/arbmath" + "github.com/offchainlabs/nitro/validator/valnode" +) + +var withL1 = true + +func TestStorageTrie(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + builder := NewNodeBuilder(ctx).DefaultConfig(t, withL1) + + // retryableSetup is being called by tests that validate blocks. + // For now validation only works with HashScheme set. + builder.execConfig.Caching.StateScheme = rawdb.HashScheme + builder.nodeConfig.BlockValidator.Enable = false + builder.nodeConfig.Staker.Enable = true + builder.nodeConfig.BatchPoster.Enable = true + builder.nodeConfig.ParentChainReader.Enable = true + builder.nodeConfig.ParentChainReader.OldHeaderTimeout = 10 * time.Minute + + valConf := valnode.TestValidationConfig + valConf.UseJit = true + _, valStack := createTestValidationNode(t, ctx, &valConf) + configByValidationNode(builder.nodeConfig, valStack) + + cleanup := builder.Build(t) + defer cleanup() + + ownerTxOpts := builder.L2Info.GetDefaultTransactOpts("Owner", ctx) + _, bigMap := builder.L2.DeployBigMap(t, ownerTxOpts) + + // Store enough values to use just over 32M gas + values := big.NewInt(1431) + + userTxOpts := builder.L2Info.GetDefaultTransactOpts("Faucet", ctx) + tx, err := bigMap.StoreValues(&userTxOpts, values) + Require(t, err) + + receipt, err := builder.L2.EnsureTxSucceeded(tx) + Require(t, err) + + if receipt.GasUsed != 32_002_907 { + t.Errorf("Want GasUsed: 32002907: got: %d", receipt.GasUsed) + } + + // Clear about 75% of them, and add another 10% + toClear := arbmath.BigDiv(arbmath.BigMul(values, big.NewInt(75)), big.NewInt(100)) + toAdd := arbmath.BigDiv(arbmath.BigMul(values, big.NewInt(10)), big.NewInt(100)) + + tx, err = bigMap.ClearAndAddValues(&userTxOpts, toClear, toAdd) + Require(t, err) + + receipt, err = builder.L2.EnsureTxSucceeded(tx) + Require(t, err) + + // Ensures that the validator gets the same results as the executor + validateBlockRange(t, []uint64{receipt.BlockNumber.Uint64()}, true, builder) +}