From 669dbfde25eebd296825a499afff21ebd017bd58 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Fri, 17 May 2024 15:40:23 -0500 Subject: [PATCH 1/9] Add rebuilding of wasmstore as a part of node initialization --- arbnode/dataposter/data_poster.go | 4 +- arbos/programs/programs.go | 68 +++++++++ arbos/programs/wasm.go | 3 + cmd/nitro/init.go | 37 ++++- system_tests/program_test.go | 136 ++++++++++++++++++ util/wasmstorerebuilder/wasmstorerebuilder.go | 106 ++++++++++++++ 6 files changed, 351 insertions(+), 3 deletions(-) create mode 100644 util/wasmstorerebuilder/wasmstorerebuilder.go diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index fb35ac3c8d..d7f8db7f42 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -220,8 +220,8 @@ func rpcClient(ctx context.Context, opts *ExternalSignerCfg) (*rpc.Client, error // Dataposter verifies that signed transaction was signed by the account // that it expects to be signed with. So signer is already authenticated // on application level and does not need to rely on TLS for authentication. - InsecureSkipVerify: opts.InsecureSkipVerify, // #nosec G402 - } + InsecureSkipVerify: opts.InsecureSkipVerify, + } // #nosec G402 if opts.ClientCert != "" && opts.ClientPrivateKey != "" { log.Info("Client certificate for external signer is enabled") diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go index 6f73e16b85..1557aaa53b 100644 --- a/arbos/programs/programs.go +++ b/arbos/programs/programs.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/log" @@ -242,6 +243,9 @@ func (p Programs) CallProgram( func getWasm(statedb vm.StateDB, program common.Address) ([]byte, error) { prefixedWasm := statedb.GetCode(program) + return getWasmInternal(prefixedWasm) +} +func getWasmInternal(prefixedWasm []byte) ([]byte, error) { if prefixedWasm == nil { return nil, ProgramNotWasmError() } @@ -278,6 +282,70 @@ func (p Programs) getProgram(codeHash common.Hash, time uint64) (Program, error) return program, err } +// SaveActiveProgramToWasmStore is used to save active stylus programs to wasm store during rebuilding +func (p Programs) SaveActiveProgramToWasmStore(statedb *state.StateDB, codeHash common.Hash, code []byte, time uint64, debugMode bool, rebuildingStartBlockTime uint64) error { + params, err := p.Params() + if err != nil { + return err + } + + program, err := p.getActiveProgram(codeHash, time, params) + if err != nil { + // The program is not active so return early + log.Info("program is not active, getActiveProgram returned error, hence do not include in rebuilding", "err", err) + return nil + } + + // It might happen that node crashed some time after rebuilding commenced and before it completed, hence when rebuilding + // resumes after node is restarted the latest diskdb derived from statedb might now have codehashes that were activated + // during the last rebuilding session. In such cases we don't need to fetch moduleshashes but instead return early + // since they would already be added to the wasm store + currentHoursSince := hoursSinceArbitrum(rebuildingStartBlockTime) + if currentHoursSince < program.activatedAt { + return nil + } + + moduleHash, err := p.moduleHashes.Get(codeHash) + if err != nil { + return err + } + + // If already in wasm store then return early + localAsm, err := statedb.TryGetActivatedAsm(moduleHash) + if err == nil && len(localAsm) > 0 { + return nil + } + + wasm, err := getWasmInternal(code) + if err != nil { + log.Error("Failed to reactivate program while rebuilding wasm store: getWasmInternal", "expected moduleHash", moduleHash, "err", err) + return fmt.Errorf("failed to reactivate program while rebuilding wasm store: %w", err) + } + + unlimitedGas := uint64(0xffffffffffff) + // We know program is activated, so it must be in correct version and not use too much memory + // Empty program address is supplied because we dont have access to this during rebuilding of wasm store + info, asm, module, err := activateProgramInternal(statedb, common.Address{}, codeHash, wasm, params.PageLimit, program.version, debugMode, &unlimitedGas) + if err != nil { + log.Error("failed to reactivate program while rebuilding wasm store", "expected moduleHash", moduleHash, "err", err) + return fmt.Errorf("failed to reactivate program while rebuilding wasm store: %w", err) + } + + if info.moduleHash != moduleHash { + log.Error("failed to reactivate program while rebuilding wasm store", "expected moduleHash", moduleHash, "got", info.moduleHash) + return fmt.Errorf("failed to reactivate program while rebuilding wasm store, expected ModuleHash: %v", moduleHash) + } + + batch := statedb.Database().WasmStore().NewBatch() + rawdb.WriteActivation(batch, moduleHash, asm, module) + if err := batch.Write(); err != nil { + log.Error("failed writing re-activation to state while rebuilding wasm store", "err", err) + return err + } + + return nil +} + // Gets a program entry. Errors if not active. func (p Programs) getActiveProgram(codeHash common.Hash, time uint64, params *StylusParams) (Program, error) { program, err := p.getProgram(codeHash, time) diff --git a/arbos/programs/wasm.go b/arbos/programs/wasm.go index 95f30e83b6..c4fe81b3a3 100644 --- a/arbos/programs/wasm.go +++ b/arbos/programs/wasm.go @@ -99,6 +99,9 @@ func cacheProgram(db vm.StateDB, module common.Hash, program Program, params *St } func evictProgram(db vm.StateDB, module common.Hash, version uint16, debug bool, mode core.MessageRunMode, forever bool) { } +func activateProgramInternal(db vm.StateDB, program common.Address, codehash common.Hash, wasm []byte, page_limit uint16, version uint16, debug bool, gasLeft *uint64) (*activationInfo, []byte, []byte, error) { + return nil, nil, nil, nil +} //go:wasmimport programs new_program func newProgram( diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index c52c87732c..bd10cddec5 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -38,6 +38,7 @@ import ( "github.com/offchainlabs/nitro/execution/gethexec" "github.com/offchainlabs/nitro/statetransfer" "github.com/offchainlabs/nitro/util/arbmath" + "github.com/offchainlabs/nitro/util/wasmstorerebuilder" ) func downloadInit(ctx context.Context, initConfig *conf.InitConfig) (string, error) { @@ -205,7 +206,34 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo return chainDb, l2BlockChain, fmt.Errorf("failed to recreate missing states: %w", err) } } - + latestBlock := l2BlockChain.CurrentBlock() + if latestBlock.Number.Uint64() <= chainConfig.ArbitrumChainParams.GenesisBlockNum { + // If there is only genesis block or no blocks in the blockchain, set Rebuilding of wasmdb to Done + log.Info("setting rebuilding of wasmdb to done") + if err = wasmstorerebuilder.SetRebuildingParam(wasmDb, wasmstorerebuilder.RebuildingPositionKey, wasmstorerebuilder.RebuildingDone); err != nil { + return nil, nil, fmt.Errorf("unable to set rebuilding status of wasmdb to done: %w", err) + } + } else { + key, err := wasmstorerebuilder.GetRebuildingParam[common.Hash](wasmDb, wasmstorerebuilder.RebuildingPositionKey) + if err != nil { + log.Info("unable to get codehash position in rebuilding of wasmdb, its possible it isnt initialized yet, so initializing it and starting rebuilding", "err", err) + if err := wasmstorerebuilder.SetRebuildingParam(wasmDb, wasmstorerebuilder.RebuildingPositionKey, common.Hash{}); err != nil { + return nil, nil, fmt.Errorf("unable to set rebuilding status of wasmdb to beginning: %w", err) + } + } + startBlockTime, err := wasmstorerebuilder.GetRebuildingParam[uint64](wasmDb, wasmstorerebuilder.RebuildingStartBlockTimeKey) + if err != nil { + log.Info("unable to get rebuilding start time of wasmdb so initializing it to current block time", "err", err) + if err := wasmstorerebuilder.SetRebuildingParam(wasmDb, wasmstorerebuilder.RebuildingStartBlockTimeKey, latestBlock.Time); err != nil { + return nil, nil, fmt.Errorf("unable to set rebuilding status of wasmdb to beginning: %w", err) + } + startBlockTime = latestBlock.Time + } + if key != wasmstorerebuilder.RebuildingDone { + log.Info("starting or continuing rebuilding of wasm store", "codeHash", key, "startBlockTime", startBlockTime) + go wasmstorerebuilder.RebuildWasmStore(ctx, wasmDb, l2BlockChain, key, startBlockTime) + } + } return chainDb, l2BlockChain, nil } readOnlyDb.Close() @@ -245,6 +273,13 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo } chainDb := rawdb.WrapDatabaseWithWasm(chainData, wasmDb) + // Rebuilding wasmdb is not required when just starting out + err = wasmstorerebuilder.SetRebuildingParam(wasmDb, wasmstorerebuilder.RebuildingPositionKey, wasmstorerebuilder.RebuildingDone) + log.Info("setting rebuilding of wasmdb to done") + if err != nil { + return nil, nil, fmt.Errorf("unable to set rebuilding of wasmdb to done: %w", err) + } + if config.Init.ImportFile != "" { initDataReader, err = statetransfer.NewJsonInitDataReader(config.Init.ImportFile) if err != nil { diff --git a/system_tests/program_test.go b/system_tests/program_test.go index 079b6c0818..6ab040a7a6 100644 --- a/system_tests/program_test.go +++ b/system_tests/program_test.go @@ -18,12 +18,15 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" _ "github.com/ethereum/go-ethereum/eth/tracers/js" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/arbcompress" "github.com/offchainlabs/nitro/arbos/programs" "github.com/offchainlabs/nitro/arbos/util" @@ -33,6 +36,7 @@ import ( "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/colors" "github.com/offchainlabs/nitro/util/testhelpers" + "github.com/offchainlabs/nitro/util/wasmstorerebuilder" "github.com/offchainlabs/nitro/validator/valnode" "github.com/wasmerio/wasmer-go/wasmer" ) @@ -1536,3 +1540,135 @@ func TestWasmRecreate(t *testing.T) { os.RemoveAll(wasmPath) } + +// createMapFromDb is used in verifying if wasm store rebuilding works +func createMapFromDb(db ethdb.KeyValueStore) (map[string][]byte, error) { + iter := db.NewIterator(nil, nil) + defer iter.Release() + + dataMap := make(map[string][]byte) + + for iter.Next() { + key := iter.Key() + value := iter.Value() + + dataMap[string(key)] = value + } + + if err := iter.Error(); err != nil { + return nil, fmt.Errorf("iterator error: %w", err) + } + + return dataMap, nil +} + +func TestWasmStoreRebuilding(t *testing.T) { + builder, auth, cleanup := setupProgramTest(t, true) + ctx := builder.ctx + l2info := builder.L2Info + l2client := builder.L2.Client + defer cleanup() + + storage := deployWasm(t, ctx, auth, l2client, rustFile("storage")) + + zero := common.Hash{} + val := common.HexToHash("0x121233445566") + + // do an onchain call - store value + storeTx := l2info.PrepareTxTo("Owner", &storage, l2info.TransferGas, nil, argsForStorageWrite(zero, val)) + Require(t, l2client.SendTransaction(ctx, storeTx)) + _, err := EnsureTxSucceeded(ctx, l2client, storeTx) + Require(t, err) + + testDir := t.TempDir() + nodeBStack := createStackConfigForTest(testDir) + nodeB, cleanupB := builder.Build2ndNode(t, &SecondNodeParams{stackConfig: nodeBStack}) + + _, err = EnsureTxSucceeded(ctx, nodeB.Client, storeTx) + Require(t, err) + + // make sure reading 2nd value succeeds from 2nd node + loadTx := l2info.PrepareTxTo("Owner", &storage, l2info.TransferGas, nil, argsForStorageRead(zero)) + result, err := arbutil.SendTxAsCall(ctx, nodeB.Client, loadTx, l2info.GetAddress("Owner"), nil, true) + Require(t, err) + if common.BytesToHash(result) != val { + Fatal(t, "got wrong value") + } + + getLatestStateWasmStore := func(b *core.BlockChain) ethdb.KeyValueStore { + latestHeader := b.CurrentBlock() + latestState, err := b.StateAt(latestHeader.Root) + if err != nil { + Require(t, err) + } + return latestState.Database().WasmStore() + } + + wasmDb := getLatestStateWasmStore(nodeB.ExecNode.Backend.ArbInterface().BlockChain()) + + storeMap, err := createMapFromDb(wasmDb) + Require(t, err) + + // close nodeB + cleanupB() + + // delete wasm dir of nodeB + wasmPath := filepath.Join(testDir, "system_tests.test", "wasm") + dirContents, err := os.ReadDir(wasmPath) + Require(t, err) + if len(dirContents) == 0 { + Fatal(t, "not contents found before delete") + } + os.RemoveAll(wasmPath) + + // recreate nodeB - using same source dir (wasm deleted) + nodeB, cleanupB = builder.Build2ndNode(t, &SecondNodeParams{stackConfig: nodeBStack}) + bc := nodeB.ExecNode.Backend.ArbInterface().BlockChain() + + wasmDbAfterDelete := getLatestStateWasmStore(bc) + storeMapAfterDelete, err := createMapFromDb(wasmDbAfterDelete) + Require(t, err) + if len(storeMapAfterDelete) != 0 { + Fatal(t, "non-empty wasm store after it was previously deleted") + } + + // Start rebuilding and wait for it to finish + log.Info("starting rebuilding wasm store") + wasmstorerebuilder.RebuildWasmStore(ctx, wasmDbAfterDelete, bc, common.Hash{}, bc.CurrentBlock().Time) + + wasmDbAfterRebuild := getLatestStateWasmStore(bc) + + // Before comparing, check if rebuilding was set to done and then delete the keys that are used to track rebuilding status + status, err := wasmstorerebuilder.GetRebuildingParam[common.Hash](wasmDbAfterRebuild, wasmstorerebuilder.RebuildingPositionKey) + Require(t, err) + if status != wasmstorerebuilder.RebuildingDone { + Fatal(t, "rebuilding was not set to done after successful completion") + } + Require(t, wasmDbAfterRebuild.Delete(wasmstorerebuilder.RebuildingPositionKey)) + Require(t, wasmDbAfterRebuild.Delete(wasmstorerebuilder.RebuildingStartBlockTimeKey)) + + rebuiltStoreMap, err := createMapFromDb(wasmDbAfterRebuild) + Require(t, err) + + // Check if rebuilding worked + if len(storeMap) != len(rebuiltStoreMap) { + Fatal(t, "size mismatch while rebuilding wasm store:", "want", len(storeMap), "got", len(rebuiltStoreMap)) + } + for key, value1 := range storeMap { + value2, exists := rebuiltStoreMap[key] + if !exists { + Fatal(t, "rebuilt wasm store doesn't have key from original") + } + if !bytes.Equal(value1, value2) { + Fatal(t, "rebuilt wasm store has incorrect value from original") + } + } + + cleanupB() + dirContents, err = os.ReadDir(wasmPath) + Require(t, err) + if len(dirContents) == 0 { + Fatal(t, "not contents found before delete") + } + os.RemoveAll(wasmPath) +} diff --git a/util/wasmstorerebuilder/wasmstorerebuilder.go b/util/wasmstorerebuilder/wasmstorerebuilder.go new file mode 100644 index 0000000000..97db9ab25c --- /dev/null +++ b/util/wasmstorerebuilder/wasmstorerebuilder.go @@ -0,0 +1,106 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package wasmstorerebuilder + +import ( + "bytes" + "context" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" + "github.com/offchainlabs/nitro/arbos/arbosState" +) + +var RebuildingPositionKey []byte = []byte("_rebuildingPosition") // contains the codehash upto where rebuilding of wasm store was last completed +var RebuildingStartBlockTimeKey []byte = []byte("_rebuildingStartBlockTime") // contains the block time when rebuilding of wasm store first began +var RebuildingDone common.Hash = common.BytesToHash([]byte("_done")) // indicates that the rebuilding is done, if RebuildingPositionKey holds this value it implies rebuilding was completed + +func GetRebuildingParam[T any](wasmStore ethdb.KeyValueStore, key []byte) (T, error) { + var empty T + posBytes, err := wasmStore.Get(key) + if err != nil { + return empty, err + } + var val T + err = rlp.DecodeBytes(posBytes, &val) + if err != nil { + return empty, fmt.Errorf("invalid value stored in rebuilding key or error decoding rebuildingBytes: %w", err) + } + return val, nil +} + +func SetRebuildingParam[T any](wasmStore ethdb.KeyValueStore, key []byte, val T) error { + valBytes, err := rlp.EncodeToBytes(val) + if err != nil { + return err + } + err = wasmStore.Put(key, valBytes) + if err != nil { + return err + } + return nil +} + +// RebuildWasmStore function runs a loop looking at every codehash in diskDb, checking if its an activated stylus contract and +// saving it to wasm store if it doesnt already exists. When errored it logs them and silently returns +// +// It stores the status of rebuilding to wasm store by updating the codehash (of the latest sucessfully checked contract) in +// RebuildingPositionKey every 50 checks. +// +// It also stores a special value that is only set once when rebuilding commenced in RebuildingStartBlockTimeKey as the block +// time of the latest block when rebuilding was first called, this is used to avoid recomputing of assembly and module of +// contracts that were created after rebuilding commenced since they would anyway already be added during sync. +func RebuildWasmStore(ctx context.Context, wasmStore ethdb.KeyValueStore, l2Blockchain *core.BlockChain, key common.Hash, rebuildingStartBlockTime uint64) { + latestHeader := l2Blockchain.CurrentBlock() + latestState, err := l2Blockchain.StateAt(latestHeader.Root) + if err != nil { + log.Error("error getting state at latest block, aborting rebuilding", "err", err) + return + } + diskDb := latestState.Database().DiskDB() + arbState, err := arbosState.OpenSystemArbosState(latestState, nil, true) + if err != nil { + log.Error("error getting arbos state, aborting rebuilding", "err", err) + return + } + programs := arbState.Programs() + iter := diskDb.NewIterator(rawdb.CodePrefix, key[:]) + for count := 1; iter.Next(); count++ { + // If outer context is cancelled we should terminate rebuilding. We probably wont be able to save codehash to wasm store (it might be closed) + if ctx.Err() != nil { + return + } + codeHashBytes := bytes.TrimPrefix(iter.Key(), rawdb.CodePrefix) + codeHash := common.BytesToHash(codeHashBytes) + code := iter.Value() + if state.IsStylusProgram(code) { + if err := programs.SaveActiveProgramToWasmStore(latestState, codeHash, code, latestHeader.Time, l2Blockchain.Config().DebugMode(), rebuildingStartBlockTime); err != nil { + log.Error("error while rebuilding wasm store, aborting rebuilding", "err", err) + return + } + } + // After every fifty codeHash checks, update the rebuilding position + // This also notifies user that we are working on rebuilding + if count%50 == 0 { + log.Info("Storing rebuilding status to disk", "codeHash", codeHash) + if err := SetRebuildingParam(wasmStore, RebuildingPositionKey, codeHash); err != nil { + log.Error("error updating position to wasm store mid way though rebuilding, aborting", "err", err) + return + } + } + } + iter.Release() + // Set rebuilding position to done indicating completion + if err := SetRebuildingParam(wasmStore, RebuildingPositionKey, RebuildingDone); err != nil { + log.Error("error updating rebuilding position to done", "err", err) + return + } + log.Info("Rebuilding of wasm store was successful") +} From ea77a3f77053b2a02df89297dbbe664dc6a8bde0 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Fri, 17 May 2024 15:52:10 -0500 Subject: [PATCH 2/9] undo nosec annotation change --- arbnode/dataposter/data_poster.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index d7f8db7f42..fb35ac3c8d 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -220,8 +220,8 @@ func rpcClient(ctx context.Context, opts *ExternalSignerCfg) (*rpc.Client, error // Dataposter verifies that signed transaction was signed by the account // that it expects to be signed with. So signer is already authenticated // on application level and does not need to rely on TLS for authentication. - InsecureSkipVerify: opts.InsecureSkipVerify, - } // #nosec G402 + InsecureSkipVerify: opts.InsecureSkipVerify, // #nosec G402 + } if opts.ClientCert != "" && opts.ClientPrivateKey != "" { log.Info("Client certificate for external signer is enabled") From 7aa13172c819a3aeb611bc395052b9cfc7077e04 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Thu, 23 May 2024 12:45:36 -0700 Subject: [PATCH 3/9] address PR comments --- arbnode/dataposter/data_poster.go | 2 +- arbos/programs/programs.go | 9 +-- cmd/nitro/init.go | 29 +++++----- .../gethexec}/wasmstorerebuilder.go | 55 +++++++++++-------- system_tests/program_test.go | 14 ++--- 5 files changed, 59 insertions(+), 50 deletions(-) rename {util/wasmstorerebuilder => execution/gethexec}/wasmstorerebuilder.go (59%) diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index 5aaef959d8..271aca7dac 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -221,7 +221,7 @@ func rpcClient(ctx context.Context, opts *ExternalSignerCfg) (*rpc.Client, error // that it expects to be signed with. So signer is already authenticated // on application level and does not need to rely on TLS for authentication. InsecureSkipVerify: opts.InsecureSkipVerify, // #nosec G402 - } + } // #nosec G402 if opts.ClientCert != "" && opts.ClientPrivateKey != "" { log.Info("Client certificate for external signer is enabled") diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go index 4891b606b7..dd882f213b 100644 --- a/arbos/programs/programs.go +++ b/arbos/programs/programs.go @@ -248,9 +248,10 @@ func (p Programs) CallProgram( func getWasm(statedb vm.StateDB, program common.Address) ([]byte, error) { prefixedWasm := statedb.GetCode(program) - return getWasmInternal(prefixedWasm) + return getWasmFromContractCode(prefixedWasm) } -func getWasmInternal(prefixedWasm []byte) ([]byte, error) { + +func getWasmFromContractCode(prefixedWasm []byte) ([]byte, error) { if prefixedWasm == nil { return nil, ProgramNotWasmError() } @@ -321,9 +322,9 @@ func (p Programs) SaveActiveProgramToWasmStore(statedb *state.StateDB, codeHash return nil } - wasm, err := getWasmInternal(code) + wasm, err := getWasmFromContractCode(code) if err != nil { - log.Error("Failed to reactivate program while rebuilding wasm store: getWasmInternal", "expected moduleHash", moduleHash, "err", err) + log.Error("Failed to reactivate program while rebuilding wasm store: getWasmFromContractCode", "expected moduleHash", moduleHash, "err", err) return fmt.Errorf("failed to reactivate program while rebuilding wasm store: %w", err) } diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index 529f4d6008..1f00e14b46 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -38,7 +38,6 @@ import ( "github.com/offchainlabs/nitro/execution/gethexec" "github.com/offchainlabs/nitro/statetransfer" "github.com/offchainlabs/nitro/util/arbmath" - "github.com/offchainlabs/nitro/util/wasmstorerebuilder" ) func downloadInit(ctx context.Context, initConfig *conf.InitConfig) (string, error) { @@ -210,28 +209,28 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo if latestBlock.Number.Uint64() <= chainConfig.ArbitrumChainParams.GenesisBlockNum { // If there is only genesis block or no blocks in the blockchain, set Rebuilding of wasmdb to Done log.Info("setting rebuilding of wasmdb to done") - if err = wasmstorerebuilder.SetRebuildingParam(wasmDb, wasmstorerebuilder.RebuildingPositionKey, wasmstorerebuilder.RebuildingDone); err != nil { + if err = gethexec.WriteToKeyValueStore(wasmDb, gethexec.RebuildingPositionKey, gethexec.RebuildingDone); err != nil { return nil, nil, fmt.Errorf("unable to set rebuilding status of wasmdb to done: %w", err) } } else { - key, err := wasmstorerebuilder.GetRebuildingParam[common.Hash](wasmDb, wasmstorerebuilder.RebuildingPositionKey) + position, err := gethexec.ReadFromKeyValueStore[common.Hash](wasmDb, gethexec.RebuildingPositionKey) if err != nil { log.Info("unable to get codehash position in rebuilding of wasmdb, its possible it isnt initialized yet, so initializing it and starting rebuilding", "err", err) - if err := wasmstorerebuilder.SetRebuildingParam(wasmDb, wasmstorerebuilder.RebuildingPositionKey, common.Hash{}); err != nil { + if err := gethexec.WriteToKeyValueStore(wasmDb, gethexec.RebuildingPositionKey, common.Hash{}); err != nil { return nil, nil, fmt.Errorf("unable to set rebuilding status of wasmdb to beginning: %w", err) } } - startBlockTime, err := wasmstorerebuilder.GetRebuildingParam[uint64](wasmDb, wasmstorerebuilder.RebuildingStartBlockTimeKey) - if err != nil { - log.Info("unable to get rebuilding start time of wasmdb so initializing it to current block time", "err", err) - if err := wasmstorerebuilder.SetRebuildingParam(wasmDb, wasmstorerebuilder.RebuildingStartBlockTimeKey, latestBlock.Time); err != nil { - return nil, nil, fmt.Errorf("unable to set rebuilding status of wasmdb to beginning: %w", err) + if position != gethexec.RebuildingDone { + startBlockHash, err := gethexec.ReadFromKeyValueStore[common.Hash](wasmDb, gethexec.RebuildingStartBlockHashKey) + if err != nil { + log.Info("unable to get rebuilding start time of wasmdb so initializing it to current block time", "err", err) + if err := gethexec.WriteToKeyValueStore(wasmDb, gethexec.RebuildingStartBlockHashKey, latestBlock.Hash()); err != nil { + return nil, nil, fmt.Errorf("unable to set rebuilding status of wasmdb to beginning: %w", err) + } + startBlockHash = latestBlock.Hash() } - startBlockTime = latestBlock.Time - } - if key != wasmstorerebuilder.RebuildingDone { - log.Info("starting or continuing rebuilding of wasm store", "codeHash", key, "startBlockTime", startBlockTime) - go wasmstorerebuilder.RebuildWasmStore(ctx, wasmDb, l2BlockChain, key, startBlockTime) + log.Info("starting or continuing rebuilding of wasm store", "codeHash", position, "startBlockHash", startBlockHash) + gethexec.RebuildWasmStore(ctx, wasmDb, l2BlockChain, position, startBlockHash) } } return chainDb, l2BlockChain, nil @@ -274,7 +273,7 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo chainDb := rawdb.WrapDatabaseWithWasm(chainData, wasmDb, 1) // Rebuilding wasmdb is not required when just starting out - err = wasmstorerebuilder.SetRebuildingParam(wasmDb, wasmstorerebuilder.RebuildingPositionKey, wasmstorerebuilder.RebuildingDone) + err = gethexec.WriteToKeyValueStore(wasmDb, gethexec.RebuildingPositionKey, gethexec.RebuildingDone) log.Info("setting rebuilding of wasmdb to done") if err != nil { return nil, nil, fmt.Errorf("unable to set rebuilding of wasmdb to done: %w", err) diff --git a/util/wasmstorerebuilder/wasmstorerebuilder.go b/execution/gethexec/wasmstorerebuilder.go similarity index 59% rename from util/wasmstorerebuilder/wasmstorerebuilder.go rename to execution/gethexec/wasmstorerebuilder.go index 97db9ab25c..eca901ffd6 100644 --- a/util/wasmstorerebuilder/wasmstorerebuilder.go +++ b/execution/gethexec/wasmstorerebuilder.go @@ -1,7 +1,7 @@ // Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE -package wasmstorerebuilder +package gethexec import ( "bytes" @@ -19,29 +19,29 @@ import ( ) var RebuildingPositionKey []byte = []byte("_rebuildingPosition") // contains the codehash upto where rebuilding of wasm store was last completed -var RebuildingStartBlockTimeKey []byte = []byte("_rebuildingStartBlockTime") // contains the block time when rebuilding of wasm store first began +var RebuildingStartBlockHashKey []byte = []byte("_rebuildingStartBlockHash") // contains the block hash of starting block when rebuilding of wasm store first began var RebuildingDone common.Hash = common.BytesToHash([]byte("_done")) // indicates that the rebuilding is done, if RebuildingPositionKey holds this value it implies rebuilding was completed -func GetRebuildingParam[T any](wasmStore ethdb.KeyValueStore, key []byte) (T, error) { +func ReadFromKeyValueStore[T any](store ethdb.KeyValueStore, key []byte) (T, error) { var empty T - posBytes, err := wasmStore.Get(key) + posBytes, err := store.Get(key) if err != nil { return empty, err } var val T err = rlp.DecodeBytes(posBytes, &val) if err != nil { - return empty, fmt.Errorf("invalid value stored in rebuilding key or error decoding rebuildingBytes: %w", err) + return empty, fmt.Errorf("error decoding value stored for key in the KeyValueStore: %w", err) } return val, nil } -func SetRebuildingParam[T any](wasmStore ethdb.KeyValueStore, key []byte, val T) error { +func WriteToKeyValueStore[T any](store ethdb.KeyValueStore, key []byte, val T) error { valBytes, err := rlp.EncodeToBytes(val) if err != nil { return err } - err = wasmStore.Put(key, valBytes) + err = store.Put(key, valBytes) if err != nil { return err } @@ -54,51 +54,60 @@ func SetRebuildingParam[T any](wasmStore ethdb.KeyValueStore, key []byte, val T) // It stores the status of rebuilding to wasm store by updating the codehash (of the latest sucessfully checked contract) in // RebuildingPositionKey every 50 checks. // -// It also stores a special value that is only set once when rebuilding commenced in RebuildingStartBlockTimeKey as the block +// It also stores a special value that is only set once when rebuilding commenced in RebuildingStartBlockHashKey as the block // time of the latest block when rebuilding was first called, this is used to avoid recomputing of assembly and module of // contracts that were created after rebuilding commenced since they would anyway already be added during sync. -func RebuildWasmStore(ctx context.Context, wasmStore ethdb.KeyValueStore, l2Blockchain *core.BlockChain, key common.Hash, rebuildingStartBlockTime uint64) { +func RebuildWasmStore(ctx context.Context, wasmStore ethdb.KeyValueStore, l2Blockchain *core.BlockChain, position, rebuildingStartBlockHash common.Hash) { + var err error + var stateDb *state.StateDB latestHeader := l2Blockchain.CurrentBlock() - latestState, err := l2Blockchain.StateAt(latestHeader.Root) + // Attempt to get state at the start block when rebuilding commenced, if not available (in case of non-archival nodes) use latest state + rebuildingStartHeader := l2Blockchain.GetHeaderByHash(rebuildingStartBlockHash) + stateDb, err = l2Blockchain.StateAt(rebuildingStartHeader.Root) if err != nil { - log.Error("error getting state at latest block, aborting rebuilding", "err", err) - return + log.Info("error getting state at start block of rebuilding wasm store, attempting rebuilding with latest state", "err", err) + stateDb, err = l2Blockchain.StateAt(latestHeader.Root) + if err != nil { + log.Error("error getting state at latest block, aborting rebuilding", "err", err) + return + } } - diskDb := latestState.Database().DiskDB() - arbState, err := arbosState.OpenSystemArbosState(latestState, nil, true) + diskDb := stateDb.Database().DiskDB() + arbState, err := arbosState.OpenSystemArbosState(stateDb, nil, true) if err != nil { log.Error("error getting arbos state, aborting rebuilding", "err", err) return } programs := arbState.Programs() - iter := diskDb.NewIterator(rawdb.CodePrefix, key[:]) + iter := diskDb.NewIterator(rawdb.CodePrefix, position[:]) for count := 1; iter.Next(); count++ { - // If outer context is cancelled we should terminate rebuilding. We probably wont be able to save codehash to wasm store (it might be closed) - if ctx.Err() != nil { - return - } codeHashBytes := bytes.TrimPrefix(iter.Key(), rawdb.CodePrefix) codeHash := common.BytesToHash(codeHashBytes) code := iter.Value() if state.IsStylusProgram(code) { - if err := programs.SaveActiveProgramToWasmStore(latestState, codeHash, code, latestHeader.Time, l2Blockchain.Config().DebugMode(), rebuildingStartBlockTime); err != nil { + if err := programs.SaveActiveProgramToWasmStore(stateDb, codeHash, code, latestHeader.Time, l2Blockchain.Config().DebugMode(), rebuildingStartHeader.Time); err != nil { log.Error("error while rebuilding wasm store, aborting rebuilding", "err", err) return } } // After every fifty codeHash checks, update the rebuilding position // This also notifies user that we are working on rebuilding - if count%50 == 0 { + if count%50 == 0 || ctx.Err() != nil { log.Info("Storing rebuilding status to disk", "codeHash", codeHash) - if err := SetRebuildingParam(wasmStore, RebuildingPositionKey, codeHash); err != nil { + if err := WriteToKeyValueStore(wasmStore, RebuildingPositionKey, codeHash); err != nil { log.Error("error updating position to wasm store mid way though rebuilding, aborting", "err", err) return } + // If outer context is cancelled we should terminate rebuilding + // We attempted to write the latest checked codeHash to wasm store + if ctx.Err() != nil { + return + } } } iter.Release() // Set rebuilding position to done indicating completion - if err := SetRebuildingParam(wasmStore, RebuildingPositionKey, RebuildingDone); err != nil { + if err := WriteToKeyValueStore(wasmStore, RebuildingPositionKey, RebuildingDone); err != nil { log.Error("error updating rebuilding position to done", "err", err) return } diff --git a/system_tests/program_test.go b/system_tests/program_test.go index 6ab040a7a6..76c0b58ff4 100644 --- a/system_tests/program_test.go +++ b/system_tests/program_test.go @@ -31,12 +31,12 @@ import ( "github.com/offchainlabs/nitro/arbos/programs" "github.com/offchainlabs/nitro/arbos/util" "github.com/offchainlabs/nitro/arbutil" + "github.com/offchainlabs/nitro/execution/gethexec" "github.com/offchainlabs/nitro/solgen/go/mocksgen" pgen "github.com/offchainlabs/nitro/solgen/go/precompilesgen" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/colors" "github.com/offchainlabs/nitro/util/testhelpers" - "github.com/offchainlabs/nitro/util/wasmstorerebuilder" "github.com/offchainlabs/nitro/validator/valnode" "github.com/wasmerio/wasmer-go/wasmer" ) @@ -1633,19 +1633,19 @@ func TestWasmStoreRebuilding(t *testing.T) { } // Start rebuilding and wait for it to finish - log.Info("starting rebuilding wasm store") - wasmstorerebuilder.RebuildWasmStore(ctx, wasmDbAfterDelete, bc, common.Hash{}, bc.CurrentBlock().Time) + log.Info("starting rebuilding of wasm store") + gethexec.RebuildWasmStore(ctx, wasmDbAfterDelete, bc, common.Hash{}, bc.CurrentBlock().Hash()) wasmDbAfterRebuild := getLatestStateWasmStore(bc) // Before comparing, check if rebuilding was set to done and then delete the keys that are used to track rebuilding status - status, err := wasmstorerebuilder.GetRebuildingParam[common.Hash](wasmDbAfterRebuild, wasmstorerebuilder.RebuildingPositionKey) + status, err := gethexec.ReadFromKeyValueStore[common.Hash](wasmDbAfterRebuild, gethexec.RebuildingPositionKey) Require(t, err) - if status != wasmstorerebuilder.RebuildingDone { + if status != gethexec.RebuildingDone { Fatal(t, "rebuilding was not set to done after successful completion") } - Require(t, wasmDbAfterRebuild.Delete(wasmstorerebuilder.RebuildingPositionKey)) - Require(t, wasmDbAfterRebuild.Delete(wasmstorerebuilder.RebuildingStartBlockTimeKey)) + Require(t, wasmDbAfterRebuild.Delete(gethexec.RebuildingPositionKey)) + Require(t, wasmDbAfterRebuild.Delete(gethexec.RebuildingStartBlockHashKey)) rebuiltStoreMap, err := createMapFromDb(wasmDbAfterRebuild) Require(t, err) From 7ab266a9703551d25d08d7816fa4d4540cfa7c95 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Thu, 23 May 2024 13:14:21 -0700 Subject: [PATCH 4/9] lint fix --- arbnode/dataposter/data_poster.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index 271aca7dac..5aaef959d8 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -221,7 +221,7 @@ func rpcClient(ctx context.Context, opts *ExternalSignerCfg) (*rpc.Client, error // that it expects to be signed with. So signer is already authenticated // on application level and does not need to rely on TLS for authentication. InsecureSkipVerify: opts.InsecureSkipVerify, // #nosec G402 - } // #nosec G402 + } if opts.ClientCert != "" && opts.ClientPrivateKey != "" { log.Info("Client certificate for external signer is enabled") From 67cfcc6b62cf49d3902581eb937e5a395d48979d Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Thu, 23 May 2024 14:40:12 -0700 Subject: [PATCH 5/9] fix typos and make logs consistent --- cmd/nitro/init.go | 20 ++++++++++---------- execution/gethexec/wasmstorerebuilder.go | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index 1f00e14b46..ea908394e2 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -207,25 +207,25 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo } latestBlock := l2BlockChain.CurrentBlock() if latestBlock.Number.Uint64() <= chainConfig.ArbitrumChainParams.GenesisBlockNum { - // If there is only genesis block or no blocks in the blockchain, set Rebuilding of wasmdb to Done - log.Info("setting rebuilding of wasmdb to done") + // If there is only genesis block or no blocks in the blockchain, set Rebuilding of wasm store to Done + log.Info("setting rebuilding of wasm store to done") if err = gethexec.WriteToKeyValueStore(wasmDb, gethexec.RebuildingPositionKey, gethexec.RebuildingDone); err != nil { - return nil, nil, fmt.Errorf("unable to set rebuilding status of wasmdb to done: %w", err) + return nil, nil, fmt.Errorf("unable to set rebuilding status of wasm store to done: %w", err) } } else { position, err := gethexec.ReadFromKeyValueStore[common.Hash](wasmDb, gethexec.RebuildingPositionKey) if err != nil { - log.Info("unable to get codehash position in rebuilding of wasmdb, its possible it isnt initialized yet, so initializing it and starting rebuilding", "err", err) + log.Info("unable to get codehash position in rebuilding of wasm store, its possible it isnt initialized yet, so initializing it and starting rebuilding", "err", err) if err := gethexec.WriteToKeyValueStore(wasmDb, gethexec.RebuildingPositionKey, common.Hash{}); err != nil { - return nil, nil, fmt.Errorf("unable to set rebuilding status of wasmdb to beginning: %w", err) + return nil, nil, fmt.Errorf("unable to initialize codehash position in rebuilding of wasm store to beginning: %w", err) } } if position != gethexec.RebuildingDone { startBlockHash, err := gethexec.ReadFromKeyValueStore[common.Hash](wasmDb, gethexec.RebuildingStartBlockHashKey) if err != nil { - log.Info("unable to get rebuilding start time of wasmdb so initializing it to current block time", "err", err) + log.Info("unable to get start block hash in rebuilding of wasm store, its possible it isnt initialized yet, so initializing it to latest block hash", "err", err) if err := gethexec.WriteToKeyValueStore(wasmDb, gethexec.RebuildingStartBlockHashKey, latestBlock.Hash()); err != nil { - return nil, nil, fmt.Errorf("unable to set rebuilding status of wasmdb to beginning: %w", err) + return nil, nil, fmt.Errorf("unable to initialize start block hash in rebuilding of wasm store to latest block hash: %w", err) } startBlockHash = latestBlock.Hash() } @@ -272,11 +272,11 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo } chainDb := rawdb.WrapDatabaseWithWasm(chainData, wasmDb, 1) - // Rebuilding wasmdb is not required when just starting out + // Rebuilding wasm store is not required when just starting out err = gethexec.WriteToKeyValueStore(wasmDb, gethexec.RebuildingPositionKey, gethexec.RebuildingDone) - log.Info("setting rebuilding of wasmdb to done") + log.Info("setting codehash position in rebuilding of wasm store to done") if err != nil { - return nil, nil, fmt.Errorf("unable to set rebuilding of wasmdb to done: %w", err) + return nil, nil, fmt.Errorf("unable to set codehash position in rebuilding of wasm store to done: %w", err) } if config.Init.ImportFile != "" { diff --git a/execution/gethexec/wasmstorerebuilder.go b/execution/gethexec/wasmstorerebuilder.go index eca901ffd6..b4affa8bbc 100644 --- a/execution/gethexec/wasmstorerebuilder.go +++ b/execution/gethexec/wasmstorerebuilder.go @@ -18,7 +18,7 @@ import ( "github.com/offchainlabs/nitro/arbos/arbosState" ) -var RebuildingPositionKey []byte = []byte("_rebuildingPosition") // contains the codehash upto where rebuilding of wasm store was last completed +var RebuildingPositionKey []byte = []byte("_rebuildingPosition") // contains the codehash upto which rebuilding of wasm store was last completed. Initialized to common.Hash{} at the start var RebuildingStartBlockHashKey []byte = []byte("_rebuildingStartBlockHash") // contains the block hash of starting block when rebuilding of wasm store first began var RebuildingDone common.Hash = common.BytesToHash([]byte("_done")) // indicates that the rebuilding is done, if RebuildingPositionKey holds this value it implies rebuilding was completed @@ -86,7 +86,7 @@ func RebuildWasmStore(ctx context.Context, wasmStore ethdb.KeyValueStore, l2Bloc code := iter.Value() if state.IsStylusProgram(code) { if err := programs.SaveActiveProgramToWasmStore(stateDb, codeHash, code, latestHeader.Time, l2Blockchain.Config().DebugMode(), rebuildingStartHeader.Time); err != nil { - log.Error("error while rebuilding wasm store, aborting rebuilding", "err", err) + log.Error("error while rebuilding of wasm store, aborting rebuilding", "err", err) return } } @@ -95,7 +95,7 @@ func RebuildWasmStore(ctx context.Context, wasmStore ethdb.KeyValueStore, l2Bloc if count%50 == 0 || ctx.Err() != nil { log.Info("Storing rebuilding status to disk", "codeHash", codeHash) if err := WriteToKeyValueStore(wasmStore, RebuildingPositionKey, codeHash); err != nil { - log.Error("error updating position to wasm store mid way though rebuilding, aborting", "err", err) + log.Error("error updating codehash position in rebuilding of wasm store", "err", err) return } // If outer context is cancelled we should terminate rebuilding @@ -108,7 +108,7 @@ func RebuildWasmStore(ctx context.Context, wasmStore ethdb.KeyValueStore, l2Bloc iter.Release() // Set rebuilding position to done indicating completion if err := WriteToKeyValueStore(wasmStore, RebuildingPositionKey, RebuildingDone); err != nil { - log.Error("error updating rebuilding position to done", "err", err) + log.Error("error updating codehash position in rebuilding of wasm store to done", "err", err) return } log.Info("Rebuilding of wasm store was successful") From 69a9280e2d6504d3ba11d1dd43731be3098a4cf9 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Tue, 4 Jun 2024 11:52:48 -0500 Subject: [PATCH 6/9] address PR comments --- arbos/programs/programs.go | 65 ------------------- arbos/programs/wasm.go | 3 - arbos/programs/wasmstorehelper.go | 80 ++++++++++++++++++++++++ cmd/nitro/init.go | 8 ++- execution/gethexec/wasmstorerebuilder.go | 31 +++++---- system_tests/program_test.go | 2 +- 6 files changed, 102 insertions(+), 87 deletions(-) create mode 100644 arbos/programs/wasmstorehelper.go diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go index dd882f213b..7d873526e0 100644 --- a/arbos/programs/programs.go +++ b/arbos/programs/programs.go @@ -10,7 +10,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/log" @@ -288,70 +287,6 @@ func (p Programs) getProgram(codeHash common.Hash, time uint64) (Program, error) return program, err } -// SaveActiveProgramToWasmStore is used to save active stylus programs to wasm store during rebuilding -func (p Programs) SaveActiveProgramToWasmStore(statedb *state.StateDB, codeHash common.Hash, code []byte, time uint64, debugMode bool, rebuildingStartBlockTime uint64) error { - params, err := p.Params() - if err != nil { - return err - } - - program, err := p.getActiveProgram(codeHash, time, params) - if err != nil { - // The program is not active so return early - log.Info("program is not active, getActiveProgram returned error, hence do not include in rebuilding", "err", err) - return nil - } - - // It might happen that node crashed some time after rebuilding commenced and before it completed, hence when rebuilding - // resumes after node is restarted the latest diskdb derived from statedb might now have codehashes that were activated - // during the last rebuilding session. In such cases we don't need to fetch moduleshashes but instead return early - // since they would already be added to the wasm store - currentHoursSince := hoursSinceArbitrum(rebuildingStartBlockTime) - if currentHoursSince < program.activatedAt { - return nil - } - - moduleHash, err := p.moduleHashes.Get(codeHash) - if err != nil { - return err - } - - // If already in wasm store then return early - localAsm, err := statedb.TryGetActivatedAsm(moduleHash) - if err == nil && len(localAsm) > 0 { - return nil - } - - wasm, err := getWasmFromContractCode(code) - if err != nil { - log.Error("Failed to reactivate program while rebuilding wasm store: getWasmFromContractCode", "expected moduleHash", moduleHash, "err", err) - return fmt.Errorf("failed to reactivate program while rebuilding wasm store: %w", err) - } - - unlimitedGas := uint64(0xffffffffffff) - // We know program is activated, so it must be in correct version and not use too much memory - // Empty program address is supplied because we dont have access to this during rebuilding of wasm store - info, asm, module, err := activateProgramInternal(statedb, common.Address{}, codeHash, wasm, params.PageLimit, program.version, debugMode, &unlimitedGas) - if err != nil { - log.Error("failed to reactivate program while rebuilding wasm store", "expected moduleHash", moduleHash, "err", err) - return fmt.Errorf("failed to reactivate program while rebuilding wasm store: %w", err) - } - - if info.moduleHash != moduleHash { - log.Error("failed to reactivate program while rebuilding wasm store", "expected moduleHash", moduleHash, "got", info.moduleHash) - return fmt.Errorf("failed to reactivate program while rebuilding wasm store, expected ModuleHash: %v", moduleHash) - } - - batch := statedb.Database().WasmStore().NewBatch() - rawdb.WriteActivation(batch, moduleHash, asm, module) - if err := batch.Write(); err != nil { - log.Error("failed writing re-activation to state while rebuilding wasm store", "err", err) - return err - } - - return nil -} - // Gets a program entry. Errors if not active. func (p Programs) getActiveProgram(codeHash common.Hash, time uint64, params *StylusParams) (Program, error) { program, err := p.getProgram(codeHash, time) diff --git a/arbos/programs/wasm.go b/arbos/programs/wasm.go index 6b4b2c72b0..4bc978a2b6 100644 --- a/arbos/programs/wasm.go +++ b/arbos/programs/wasm.go @@ -99,9 +99,6 @@ func cacheProgram(db vm.StateDB, module common.Hash, program Program, params *St } func evictProgram(db vm.StateDB, module common.Hash, version uint16, debug bool, mode core.MessageRunMode, forever bool) { } -func activateProgramInternal(db vm.StateDB, program common.Address, codehash common.Hash, wasm []byte, page_limit uint16, version uint16, debug bool, gasLeft *uint64) (*activationInfo, []byte, []byte, error) { - return nil, nil, nil, nil -} //go:wasmimport programs new_program func newProgram( diff --git a/arbos/programs/wasmstorehelper.go b/arbos/programs/wasmstorehelper.go new file mode 100644 index 0000000000..9e69178694 --- /dev/null +++ b/arbos/programs/wasmstorehelper.go @@ -0,0 +1,80 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +//go:build !wasm +// +build !wasm + +package programs + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/log" +) + +// SaveActiveProgramToWasmStore is used to save active stylus programs to wasm store during rebuilding +func (p Programs) SaveActiveProgramToWasmStore(statedb *state.StateDB, codeHash common.Hash, code []byte, time uint64, debugMode bool, rebuildingStartBlockTime uint64) error { + params, err := p.Params() + if err != nil { + return err + } + + program, err := p.getActiveProgram(codeHash, time, params) + if err != nil { + // The program is not active so return early + log.Info("program is not active, getActiveProgram returned error, hence do not include in rebuilding", "err", err) + return nil + } + + // It might happen that node crashed some time after rebuilding commenced and before it completed, hence when rebuilding + // resumes after node is restarted the latest diskdb derived from statedb might now have codehashes that were activated + // during the last rebuilding session. In such cases we don't need to fetch moduleshashes but instead return early + // since they would already be added to the wasm store + currentHoursSince := hoursSinceArbitrum(rebuildingStartBlockTime) + if currentHoursSince < program.activatedAt { + return nil + } + + moduleHash, err := p.moduleHashes.Get(codeHash) + if err != nil { + return err + } + + // If already in wasm store then return early + localAsm, err := statedb.TryGetActivatedAsm(moduleHash) + if err == nil && len(localAsm) > 0 { + return nil + } + + wasm, err := getWasmFromContractCode(code) + if err != nil { + log.Error("Failed to reactivate program while rebuilding wasm store: getWasmFromContractCode", "expected moduleHash", moduleHash, "err", err) + return fmt.Errorf("failed to reactivate program while rebuilding wasm store: %w", err) + } + + unlimitedGas := uint64(0xffffffffffff) + // We know program is activated, so it must be in correct version and not use too much memory + // Empty program address is supplied because we dont have access to this during rebuilding of wasm store + info, asm, module, err := activateProgramInternal(statedb, common.Address{}, codeHash, wasm, params.PageLimit, program.version, debugMode, &unlimitedGas) + if err != nil { + log.Error("failed to reactivate program while rebuilding wasm store", "expected moduleHash", moduleHash, "err", err) + return fmt.Errorf("failed to reactivate program while rebuilding wasm store: %w", err) + } + + if info.moduleHash != moduleHash { + log.Error("failed to reactivate program while rebuilding wasm store", "expected moduleHash", moduleHash, "got", info.moduleHash) + return fmt.Errorf("failed to reactivate program while rebuilding wasm store, expected ModuleHash: %v", moduleHash) + } + + batch := statedb.Database().WasmStore().NewBatch() + rawdb.WriteActivation(batch, moduleHash, asm, module) + if err := batch.Write(); err != nil { + log.Error("failed writing re-activation to state while rebuilding wasm store", "err", err) + return err + } + + return nil +} diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index 50486a691f..a1679edd52 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -206,8 +206,10 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo } } latestBlock := l2BlockChain.CurrentBlock() - if latestBlock.Number.Uint64() <= chainConfig.ArbitrumChainParams.GenesisBlockNum { + if latestBlock.Number.Uint64() <= chainConfig.ArbitrumChainParams.GenesisBlockNum || + types.DeserializeHeaderExtraInformation(latestBlock).ArbOSFormatVersion < params.ArbosVersion_Stylus { // If there is only genesis block or no blocks in the blockchain, set Rebuilding of wasm store to Done + // If Stylus upgrade hasn't yet happened, skipping rebuilding of wasm store log.Info("setting rebuilding of wasm store to done") if err = gethexec.WriteToKeyValueStore(wasmDb, gethexec.RebuildingPositionKey, gethexec.RebuildingDone); err != nil { return nil, nil, fmt.Errorf("unable to set rebuilding status of wasm store to done: %w", err) @@ -230,7 +232,9 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo startBlockHash = latestBlock.Hash() } log.Info("starting or continuing rebuilding of wasm store", "codeHash", position, "startBlockHash", startBlockHash) - gethexec.RebuildWasmStore(ctx, wasmDb, l2BlockChain, position, startBlockHash) + if err := gethexec.RebuildWasmStore(ctx, wasmDb, l2BlockChain, position, startBlockHash); err != nil { + return nil, nil, fmt.Errorf("error rebuilding of wasm store: %w", err) + } } } return chainDb, l2BlockChain, nil diff --git a/execution/gethexec/wasmstorerebuilder.go b/execution/gethexec/wasmstorerebuilder.go index b4affa8bbc..e8ed78e22c 100644 --- a/execution/gethexec/wasmstorerebuilder.go +++ b/execution/gethexec/wasmstorerebuilder.go @@ -7,6 +7,7 @@ import ( "bytes" "context" "fmt" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" @@ -57,7 +58,7 @@ func WriteToKeyValueStore[T any](store ethdb.KeyValueStore, key []byte, val T) e // It also stores a special value that is only set once when rebuilding commenced in RebuildingStartBlockHashKey as the block // time of the latest block when rebuilding was first called, this is used to avoid recomputing of assembly and module of // contracts that were created after rebuilding commenced since they would anyway already be added during sync. -func RebuildWasmStore(ctx context.Context, wasmStore ethdb.KeyValueStore, l2Blockchain *core.BlockChain, position, rebuildingStartBlockHash common.Hash) { +func RebuildWasmStore(ctx context.Context, wasmStore ethdb.KeyValueStore, l2Blockchain *core.BlockChain, position, rebuildingStartBlockHash common.Hash) error { var err error var stateDb *state.StateDB latestHeader := l2Blockchain.CurrentBlock() @@ -68,48 +69,46 @@ func RebuildWasmStore(ctx context.Context, wasmStore ethdb.KeyValueStore, l2Bloc log.Info("error getting state at start block of rebuilding wasm store, attempting rebuilding with latest state", "err", err) stateDb, err = l2Blockchain.StateAt(latestHeader.Root) if err != nil { - log.Error("error getting state at latest block, aborting rebuilding", "err", err) - return + return fmt.Errorf("error getting state at latest block, aborting rebuilding: %w", err) } } diskDb := stateDb.Database().DiskDB() arbState, err := arbosState.OpenSystemArbosState(stateDb, nil, true) if err != nil { - log.Error("error getting arbos state, aborting rebuilding", "err", err) - return + return fmt.Errorf("error getting arbos state, aborting rebuilding: %w", err) } programs := arbState.Programs() iter := diskDb.NewIterator(rawdb.CodePrefix, position[:]) - for count := 1; iter.Next(); count++ { + defer iter.Release() + lastStatusUpdate := time.Now() + for iter.Next() { codeHashBytes := bytes.TrimPrefix(iter.Key(), rawdb.CodePrefix) codeHash := common.BytesToHash(codeHashBytes) code := iter.Value() if state.IsStylusProgram(code) { if err := programs.SaveActiveProgramToWasmStore(stateDb, codeHash, code, latestHeader.Time, l2Blockchain.Config().DebugMode(), rebuildingStartHeader.Time); err != nil { - log.Error("error while rebuilding of wasm store, aborting rebuilding", "err", err) - return + return fmt.Errorf("error while rebuilding of wasm store, aborting rebuilding: %w", err) } } - // After every fifty codeHash checks, update the rebuilding position + // After every one second of work, update the rebuilding position // This also notifies user that we are working on rebuilding - if count%50 == 0 || ctx.Err() != nil { + if time.Since(lastStatusUpdate) >= time.Second || ctx.Err() != nil { log.Info("Storing rebuilding status to disk", "codeHash", codeHash) if err := WriteToKeyValueStore(wasmStore, RebuildingPositionKey, codeHash); err != nil { - log.Error("error updating codehash position in rebuilding of wasm store", "err", err) - return + return fmt.Errorf("error updating codehash position in rebuilding of wasm store: %w", err) } // If outer context is cancelled we should terminate rebuilding // We attempted to write the latest checked codeHash to wasm store if ctx.Err() != nil { - return + return ctx.Err() } + lastStatusUpdate = time.Now() } } - iter.Release() // Set rebuilding position to done indicating completion if err := WriteToKeyValueStore(wasmStore, RebuildingPositionKey, RebuildingDone); err != nil { - log.Error("error updating codehash position in rebuilding of wasm store to done", "err", err) - return + return fmt.Errorf("error updating codehash position in rebuilding of wasm store to done: %w", err) } log.Info("Rebuilding of wasm store was successful") + return nil } diff --git a/system_tests/program_test.go b/system_tests/program_test.go index 76c0b58ff4..1cee3db298 100644 --- a/system_tests/program_test.go +++ b/system_tests/program_test.go @@ -1634,7 +1634,7 @@ func TestWasmStoreRebuilding(t *testing.T) { // Start rebuilding and wait for it to finish log.Info("starting rebuilding of wasm store") - gethexec.RebuildWasmStore(ctx, wasmDbAfterDelete, bc, common.Hash{}, bc.CurrentBlock().Hash()) + Require(t, gethexec.RebuildWasmStore(ctx, wasmDbAfterDelete, bc, common.Hash{}, bc.CurrentBlock().Hash())) wasmDbAfterRebuild := getLatestStateWasmStore(bc) From 441c8a988235256e37be27446356b73a1ef9813c Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Fri, 7 Jun 2024 10:58:30 -0500 Subject: [PATCH 7/9] try to get live state while rebuilding. fix logs --- cmd/nitro/init.go | 12 ++++++------ execution/gethexec/wasmstorerebuilder.go | 24 +++++++++++++++++++++--- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index e8889427e5..402027c67b 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -320,18 +320,18 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo } } latestBlock := l2BlockChain.CurrentBlock() - if latestBlock.Number.Uint64() <= chainConfig.ArbitrumChainParams.GenesisBlockNum || + if latestBlock == nil || latestBlock.Number.Uint64() <= chainConfig.ArbitrumChainParams.GenesisBlockNum || types.DeserializeHeaderExtraInformation(latestBlock).ArbOSFormatVersion < params.ArbosVersion_Stylus { // If there is only genesis block or no blocks in the blockchain, set Rebuilding of wasm store to Done // If Stylus upgrade hasn't yet happened, skipping rebuilding of wasm store - log.Info("setting rebuilding of wasm store to done") + log.Info("Setting rebuilding of wasm store to done") if err = gethexec.WriteToKeyValueStore(wasmDb, gethexec.RebuildingPositionKey, gethexec.RebuildingDone); err != nil { return nil, nil, fmt.Errorf("unable to set rebuilding status of wasm store to done: %w", err) } } else { position, err := gethexec.ReadFromKeyValueStore[common.Hash](wasmDb, gethexec.RebuildingPositionKey) if err != nil { - log.Info("unable to get codehash position in rebuilding of wasm store, its possible it isnt initialized yet, so initializing it and starting rebuilding", "err", err) + log.Info("Unable to get codehash position in rebuilding of wasm store, its possible it isnt initialized yet, so initializing it and starting rebuilding", "err", err) if err := gethexec.WriteToKeyValueStore(wasmDb, gethexec.RebuildingPositionKey, common.Hash{}); err != nil { return nil, nil, fmt.Errorf("unable to initialize codehash position in rebuilding of wasm store to beginning: %w", err) } @@ -339,13 +339,13 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo if position != gethexec.RebuildingDone { startBlockHash, err := gethexec.ReadFromKeyValueStore[common.Hash](wasmDb, gethexec.RebuildingStartBlockHashKey) if err != nil { - log.Info("unable to get start block hash in rebuilding of wasm store, its possible it isnt initialized yet, so initializing it to latest block hash", "err", err) + log.Info("Unable to get start block hash in rebuilding of wasm store, its possible it isnt initialized yet, so initializing it to latest block hash", "err", err) if err := gethexec.WriteToKeyValueStore(wasmDb, gethexec.RebuildingStartBlockHashKey, latestBlock.Hash()); err != nil { return nil, nil, fmt.Errorf("unable to initialize start block hash in rebuilding of wasm store to latest block hash: %w", err) } startBlockHash = latestBlock.Hash() } - log.Info("starting or continuing rebuilding of wasm store", "codeHash", position, "startBlockHash", startBlockHash) + log.Info("Starting or continuing rebuilding of wasm store", "codeHash", position, "startBlockHash", startBlockHash) if err := gethexec.RebuildWasmStore(ctx, wasmDb, l2BlockChain, position, startBlockHash); err != nil { return nil, nil, fmt.Errorf("error rebuilding of wasm store: %w", err) } @@ -392,7 +392,7 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo // Rebuilding wasm store is not required when just starting out err = gethexec.WriteToKeyValueStore(wasmDb, gethexec.RebuildingPositionKey, gethexec.RebuildingDone) - log.Info("setting codehash position in rebuilding of wasm store to done") + log.Info("Setting codehash position in rebuilding of wasm store to done") if err != nil { return nil, nil, fmt.Errorf("unable to set codehash position in rebuilding of wasm store to done: %w", err) } diff --git a/execution/gethexec/wasmstorerebuilder.go b/execution/gethexec/wasmstorerebuilder.go index e8ed78e22c..18e76f2b29 100644 --- a/execution/gethexec/wasmstorerebuilder.go +++ b/execution/gethexec/wasmstorerebuilder.go @@ -6,6 +6,7 @@ package gethexec import ( "bytes" "context" + "errors" "fmt" "time" @@ -13,6 +14,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" @@ -61,13 +63,29 @@ func WriteToKeyValueStore[T any](store ethdb.KeyValueStore, key []byte, val T) e func RebuildWasmStore(ctx context.Context, wasmStore ethdb.KeyValueStore, l2Blockchain *core.BlockChain, position, rebuildingStartBlockHash common.Hash) error { var err error var stateDb *state.StateDB + tryLiveStateFallBackStateAt := func(header *types.Header) (*state.StateDB, error) { + if header == nil { + return nil, errors.New("trying to get state for a nil header") + } + if header.Root != (common.Hash{}) { + if err := l2Blockchain.TrieDB().Reference(header.Root, common.Hash{}); err != nil { + return l2Blockchain.StateAt(header.Root) + } + } + liveState, err := state.New(header.Root, l2Blockchain.StateCache(), nil) + if err != nil { + log.Info("Failed to get live state during rebuilding wasm store, falling back to StateAt", "err", err) + return l2Blockchain.StateAt(header.Root) + } + return liveState, nil + } latestHeader := l2Blockchain.CurrentBlock() // Attempt to get state at the start block when rebuilding commenced, if not available (in case of non-archival nodes) use latest state rebuildingStartHeader := l2Blockchain.GetHeaderByHash(rebuildingStartBlockHash) - stateDb, err = l2Blockchain.StateAt(rebuildingStartHeader.Root) + stateDb, err = tryLiveStateFallBackStateAt(rebuildingStartHeader) if err != nil { - log.Info("error getting state at start block of rebuilding wasm store, attempting rebuilding with latest state", "err", err) - stateDb, err = l2Blockchain.StateAt(latestHeader.Root) + log.Info("Error getting state at start block of rebuilding wasm store, attempting rebuilding with latest state", "err", err) + stateDb, err = tryLiveStateFallBackStateAt(latestHeader) if err != nil { return fmt.Errorf("error getting state at latest block, aborting rebuilding: %w", err) } From 008d6059aa75470ff30e6dfbf076b515d9a46de7 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Mon, 10 Jun 2024 11:39:02 -0500 Subject: [PATCH 8/9] simplify fetching of live state. code refactor --- cmd/nitro/init.go | 2 +- execution/gethexec/wasmstorerebuilder.go | 27 +++++------------------- go-ethereum | 2 +- system_tests/program_test.go | 24 ++++----------------- 4 files changed, 11 insertions(+), 44 deletions(-) diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index 402027c67b..632e9a6478 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -346,7 +346,7 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo startBlockHash = latestBlock.Hash() } log.Info("Starting or continuing rebuilding of wasm store", "codeHash", position, "startBlockHash", startBlockHash) - if err := gethexec.RebuildWasmStore(ctx, wasmDb, l2BlockChain, position, startBlockHash); err != nil { + if err := gethexec.RebuildWasmStore(ctx, wasmDb, chainDb, config.Execution.RPC.MaxRecreateStateDepth, l2BlockChain, position, startBlockHash); err != nil { return nil, nil, fmt.Errorf("error rebuilding of wasm store: %w", err) } } diff --git a/execution/gethexec/wasmstorerebuilder.go b/execution/gethexec/wasmstorerebuilder.go index 18e76f2b29..dcbee45a3f 100644 --- a/execution/gethexec/wasmstorerebuilder.go +++ b/execution/gethexec/wasmstorerebuilder.go @@ -6,15 +6,14 @@ package gethexec import ( "bytes" "context" - "errors" "fmt" "time" + "github.com/ethereum/go-ethereum/arbitrum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" @@ -55,37 +54,21 @@ func WriteToKeyValueStore[T any](store ethdb.KeyValueStore, key []byte, val T) e // saving it to wasm store if it doesnt already exists. When errored it logs them and silently returns // // It stores the status of rebuilding to wasm store by updating the codehash (of the latest sucessfully checked contract) in -// RebuildingPositionKey every 50 checks. +// RebuildingPositionKey after every second of work. // // It also stores a special value that is only set once when rebuilding commenced in RebuildingStartBlockHashKey as the block // time of the latest block when rebuilding was first called, this is used to avoid recomputing of assembly and module of // contracts that were created after rebuilding commenced since they would anyway already be added during sync. -func RebuildWasmStore(ctx context.Context, wasmStore ethdb.KeyValueStore, l2Blockchain *core.BlockChain, position, rebuildingStartBlockHash common.Hash) error { +func RebuildWasmStore(ctx context.Context, wasmStore ethdb.KeyValueStore, chainDb ethdb.Database, maxRecreateStateDepth int64, l2Blockchain *core.BlockChain, position, rebuildingStartBlockHash common.Hash) error { var err error var stateDb *state.StateDB - tryLiveStateFallBackStateAt := func(header *types.Header) (*state.StateDB, error) { - if header == nil { - return nil, errors.New("trying to get state for a nil header") - } - if header.Root != (common.Hash{}) { - if err := l2Blockchain.TrieDB().Reference(header.Root, common.Hash{}); err != nil { - return l2Blockchain.StateAt(header.Root) - } - } - liveState, err := state.New(header.Root, l2Blockchain.StateCache(), nil) - if err != nil { - log.Info("Failed to get live state during rebuilding wasm store, falling back to StateAt", "err", err) - return l2Blockchain.StateAt(header.Root) - } - return liveState, nil - } latestHeader := l2Blockchain.CurrentBlock() // Attempt to get state at the start block when rebuilding commenced, if not available (in case of non-archival nodes) use latest state rebuildingStartHeader := l2Blockchain.GetHeaderByHash(rebuildingStartBlockHash) - stateDb, err = tryLiveStateFallBackStateAt(rebuildingStartHeader) + stateDb, _, err = arbitrum.StateAndHeaderFromHeader(ctx, chainDb, l2Blockchain, maxRecreateStateDepth, rebuildingStartHeader, nil) if err != nil { log.Info("Error getting state at start block of rebuilding wasm store, attempting rebuilding with latest state", "err", err) - stateDb, err = tryLiveStateFallBackStateAt(latestHeader) + stateDb, _, err = arbitrum.StateAndHeaderFromHeader(ctx, chainDb, l2Blockchain, maxRecreateStateDepth, latestHeader, nil) if err != nil { return fmt.Errorf("error getting state at latest block, aborting rebuilding: %w", err) } diff --git a/go-ethereum b/go-ethereum index de513a2b2c..9c4f48cb5d 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit de513a2b2c8e9e1239190992fcdaccef81cd387c +Subproject commit 9c4f48cb5d569ac94cd8c4104b111bb2d4d780ae diff --git a/system_tests/program_test.go b/system_tests/program_test.go index 1cee3db298..d8d9e05aa1 100644 --- a/system_tests/program_test.go +++ b/system_tests/program_test.go @@ -18,7 +18,6 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -1595,16 +1594,7 @@ func TestWasmStoreRebuilding(t *testing.T) { Fatal(t, "got wrong value") } - getLatestStateWasmStore := func(b *core.BlockChain) ethdb.KeyValueStore { - latestHeader := b.CurrentBlock() - latestState, err := b.StateAt(latestHeader.Root) - if err != nil { - Require(t, err) - } - return latestState.Database().WasmStore() - } - - wasmDb := getLatestStateWasmStore(nodeB.ExecNode.Backend.ArbInterface().BlockChain()) + wasmDb := nodeB.ExecNode.Backend.ArbInterface().BlockChain().StateCache().WasmStore() storeMap, err := createMapFromDb(wasmDb) Require(t, err) @@ -1625,7 +1615,7 @@ func TestWasmStoreRebuilding(t *testing.T) { nodeB, cleanupB = builder.Build2ndNode(t, &SecondNodeParams{stackConfig: nodeBStack}) bc := nodeB.ExecNode.Backend.ArbInterface().BlockChain() - wasmDbAfterDelete := getLatestStateWasmStore(bc) + wasmDbAfterDelete := nodeB.ExecNode.Backend.ArbInterface().BlockChain().StateCache().WasmStore() storeMapAfterDelete, err := createMapFromDb(wasmDbAfterDelete) Require(t, err) if len(storeMapAfterDelete) != 0 { @@ -1634,9 +1624,9 @@ func TestWasmStoreRebuilding(t *testing.T) { // Start rebuilding and wait for it to finish log.Info("starting rebuilding of wasm store") - Require(t, gethexec.RebuildWasmStore(ctx, wasmDbAfterDelete, bc, common.Hash{}, bc.CurrentBlock().Hash())) + Require(t, gethexec.RebuildWasmStore(ctx, wasmDbAfterDelete, nodeB.ExecNode.ChainDB, nodeB.ExecNode.ConfigFetcher().RPC.MaxRecreateStateDepth, bc, common.Hash{}, bc.CurrentBlock().Hash())) - wasmDbAfterRebuild := getLatestStateWasmStore(bc) + wasmDbAfterRebuild := nodeB.ExecNode.Backend.ArbInterface().BlockChain().StateCache().WasmStore() // Before comparing, check if rebuilding was set to done and then delete the keys that are used to track rebuilding status status, err := gethexec.ReadFromKeyValueStore[common.Hash](wasmDbAfterRebuild, gethexec.RebuildingPositionKey) @@ -1665,10 +1655,4 @@ func TestWasmStoreRebuilding(t *testing.T) { } cleanupB() - dirContents, err = os.ReadDir(wasmPath) - Require(t, err) - if len(dirContents) == 0 { - Fatal(t, "not contents found before delete") - } - os.RemoveAll(wasmPath) } From ae4740696c5f808526295811d2215f858c24f73a Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Tue, 11 Jun 2024 10:23:55 -0500 Subject: [PATCH 9/9] update geth pin --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index 9c4f48cb5d..0a55348db8 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 9c4f48cb5d569ac94cd8c4104b111bb2d4d780ae +Subproject commit 0a55348db87087016359e375de7db8b4cd3e445d