diff --git a/.github/workflows/stable-spec-tests.yml b/.github/workflows/stable-spec-tests.yml new file mode 100644 index 00000000000..3c5477150eb --- /dev/null +++ b/.github/workflows/stable-spec-tests.yml @@ -0,0 +1,77 @@ +name: Execution Spec Tests - Consume (stable) + +on: + push: + branches: [master] + pull_request: + branches: [master, kaustinen-with-shapella] + workflow_dispatch: + +env: + FIXTURES_TAG: "verkle@v0.0.9-alpha-1" + +jobs: + setup: + runs-on: ubuntu-latest + steps: + - name: Checkout go-ethereum + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.12.4" + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.22.4 + + - name: Build geth evm + run: | + go build -v ./cmd/evm + mkdir -p ${{ github.workspace }}/bin + mv evm ${{ github.workspace }}/bin/evm + chmod +x ${{ github.workspace }}/bin/evm + + - name: Archive built evm + uses: actions/upload-artifact@v4 + with: + name: evm + path: ${{ github.workspace }}/bin/evm + + consume: + runs-on: ubuntu-latest + needs: setup + strategy: + matrix: + filename: + [ + fixtures_verkle-genesis.tar.gz, + ] + steps: + - name: Download geth evm + uses: actions/download-artifact@v4 + with: + name: evm + path: ./bin + + - name: Make evm binary executable and add to PATH + run: | + chmod +x ./bin/evm + echo "${{ github.workspace }}/bin" >> $GITHUB_PATH + + - name: Download fixtures + uses: robinraju/release-downloader@v1 + with: + repository: "ethereum/execution-spec-tests" + tag: "${{ env.FIXTURES_TAG }}" + fileName: "${{ matrix.filename }}" + extract: true + - name: Clone execution-spec-tests and consume tests + run: | + curl -LsSf https://astral.sh/uv/install.sh | sh + git clone https://github.com/ethereum/execution-spec-tests -b ${{ env.FIXTURES_TAG }} --depth 1 + cd execution-spec-tests + uv run consume direct --evm-bin="${{ github.workspace }}/bin/evm" --input=../fixtures -n auto + shell: bash diff --git a/cmd/evm/blockrunner.go b/cmd/evm/blockrunner.go index 31d1ba5ba1c..f6538b13567 100644 --- a/cmd/evm/blockrunner.go +++ b/cmd/evm/blockrunner.go @@ -89,7 +89,7 @@ func runBlockTest(ctx *cli.Context, fname string) ([]testResult, error) { continue } result := &testResult{Name: name, Pass: true} - if err := tests[name].Run(false, rawdb.HashScheme, ctx.Bool(WitnessCrossCheckFlag.Name), tracer, func(res error, chain *core.BlockChain) { + if err := tests[name].Run(false, rawdb.PathScheme, ctx.Bool(WitnessCrossCheckFlag.Name), tracer, func(res error, chain *core.BlockChain) { if ctx.Bool(DumpFlag.Name) { if s, _ := chain.State(); s != nil { result.State = dump(s) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 7de1eb6949d..35993759e32 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -39,13 +39,15 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-verkle" "github.com/holiman/uint256" "golang.org/x/crypto/sha3" ) type Prestate struct { - Env stEnv `json:"env"` - Pre types.GenesisAlloc `json:"pre"` + Env stEnv `json:"env"` + Pre types.GenesisAlloc `json:"pre"` + VKT map[common.Hash]hexutil.Bytes `json:"vkt,omitempty"` } //go:generate go run github.com/fjl/gencodec -type ExecutionResult -field-override executionResultMarshaling -out gen_execresult.go @@ -68,6 +70,11 @@ type ExecutionResult struct { CurrentBlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed,omitempty"` RequestsHash *common.Hash `json:"requestsHash,omitempty"` Requests [][]byte `json:"requests"` + + // Verkle witness + VerkleProof *verkle.VerkleProof `json:"verkleProof,omitempty"` + StateDiff verkle.StateDiff `json:"stateDiff,omitempty"` + ParentRoot common.Hash `json:"parentStateRoot,omitempty"` } type executionResultMarshaling struct { @@ -101,6 +108,7 @@ type stEnv struct { ParentExcessBlobGas *uint64 `json:"parentExcessBlobGas,omitempty"` ParentBlobGasUsed *uint64 `json:"parentBlobGasUsed,omitempty"` ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"` + ParentHash *common.Hash `json:"parentHash,omitempty"` } type stEnvMarshaling struct { @@ -143,16 +151,17 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, return h } var ( - statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre) - signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) - gaspool = new(core.GasPool) - blockHash = common.Hash{0x13, 0x37} - rejectedTxs []*rejectedTx - includedTxs types.Transactions - gasUsed = uint64(0) - blobGasUsed = uint64(0) - receipts = make(types.Receipts, 0) - txIndex = 0 + parentStateRoot, statedb = MakePreState(rawdb.NewMemoryDatabase(), chainConfig, pre, chainConfig.IsVerkle(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp)) + signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) + gaspool = new(core.GasPool) + blockHash = common.Hash{0x13, 0x37} + rejectedTxs []*rejectedTx + includedTxs types.Transactions + gasUsed = uint64(0) + blobGasUsed = uint64(0) + receipts = make(types.Receipts, 0) + txIndex = 0 + vtrpre *trie.VerkleTrie ) gaspool.AddGas(pre.Env.GasLimit) vmContext := vm.BlockContext{ @@ -165,6 +174,14 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, GasLimit: pre.Env.GasLimit, GetHash: getHash, } + + // We save the current state of the Verkle Tree before applying the transactions. + // Note that if the Verkle fork isn't active, this will be a noop. + switch tr := statedb.GetTrie().(type) { + case *trie.VerkleTrie: + vtrpre = tr.Copy() + } + // If currentBaseFee is defined, add it to the vmContext. if pre.Env.BaseFee != nil { vmContext.BaseFee = new(big.Int).Set(pre.Env.BaseFee) @@ -251,6 +268,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, } } statedb.SetTxContext(tx.Hash(), txIndex) + evm.AccessEvents = state.NewAccessEvents(evm.StateDB.PointCache()) var ( snapshot = statedb.Snapshot() prevGas = gaspool.Gas() @@ -315,7 +333,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, evm.Config.Tracer.OnTxEnd(receipt, nil) } } - + statedb.AccessEvents().Merge(evm.AccessEvents) txIndex++ } statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)) @@ -348,6 +366,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, // Amount is in gwei, turn into wei amount := new(big.Int).Mul(new(big.Int).SetUint64(w.Amount), big.NewInt(params.GWei)) statedb.AddBalance(w.Address, uint256.MustFromBig(amount), tracing.BalanceIncreaseWithdrawal) + + statedb.AccessEvents().AddAccount(w.Address, true) } // Gather the execution-layer triggered requests. @@ -373,6 +393,25 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, if err != nil { return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not commit state: %v", err)) } + // Add the witness to the execution result + var vktProof *verkle.VerkleProof + var vktStateDiff verkle.StateDiff + if chainConfig.IsVerkle(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp) { + keys := statedb.AccessEvents().Keys() + if len(keys) > 0 && vtrpre != nil { + var proofTrie *trie.VerkleTrie + switch tr := statedb.GetTrie().(type) { + case *trie.VerkleTrie: + proofTrie = tr + default: + return nil, nil, nil, fmt.Errorf("invalid tree type in proof generation: %v", tr) + } + vktProof, vktStateDiff, err = vtrpre.Proof(proofTrie, keys) + if err != nil { + return nil, nil, nil, fmt.Errorf("error generating verkle proof for block %d: %w", pre.Env.Number, err) + } + } + } execRs := &ExecutionResult{ StateRoot: root, TxRoot: types.DeriveSha(includedTxs, trie.NewStackTrie(nil)), @@ -384,6 +423,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty), GasUsed: (math.HexOrDecimal64)(gasUsed), BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee), + VerkleProof: vktProof, + StateDiff: vktStateDiff, + ParentRoot: parentStateRoot, } if pre.Env.Withdrawals != nil { h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil)) @@ -414,11 +456,13 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, return statedb, execRs, body, nil } -func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB { +// XXX peut-etre pas besoin de changer les parametres tant qu'on n'a pas la conversion +func MakePreState(db ethdb.Database, chainConfig *params.ChainConfig, pre *Prestate, verkle bool) (common.Hash, *state.StateDB) { tdb := triedb.NewDatabase(db, &triedb.Config{Preimages: true}) sdb := state.NewDatabase(tdb, nil) + statedb, _ := state.New(types.EmptyRootHash, sdb) - for addr, a := range accounts { + for addr, a := range pre.Pre { statedb.SetCode(addr, a.Code) statedb.SetNonce(addr, a.Nonce, tracing.NonceChangeGenesis) statedb.SetBalance(addr, uint256.MustFromBig(a.Balance), tracing.BalanceIncreaseGenesisBalance) @@ -427,9 +471,19 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB } } // Commit and re-open to start with a clean state. - root, _ := statedb.Commit(0, false, false) - statedb, _ = state.New(root, sdb) - return statedb + mptRoot, err := statedb.Commit(0, false, false) + if err != nil { + panic(err) + } + parentRoot := mptRoot + // If verkle mode started, establish the conversion + if verkle { + if _, ok := statedb.GetTrie().(*trie.VerkleTrie); ok { + return parentRoot, statedb + } + } + statedb, _ = state.New(mptRoot, sdb) + return parentRoot, statedb } func rlpHash(x interface{}) (h common.Hash) { diff --git a/cmd/evm/internal/t8ntool/flags.go b/cmd/evm/internal/t8ntool/flags.go index f2606c86d18..52f7b05a20f 100644 --- a/cmd/evm/internal/t8ntool/flags.go +++ b/cmd/evm/internal/t8ntool/flags.go @@ -88,6 +88,22 @@ var ( "\t - into the file ", Value: "block.json", } + OutputVKTFlag = &cli.StringFlag{ + Name: "output.vkt", + Usage: "Determines where to put the `VKT` of the post-state.\n" + + "\t`stdout` - into the stdout output\n" + + "\t`stderr` - into the stderr output\n" + + "\t - into the file ", + Value: "vkt.json", + } + OutputWitnessFlag = &cli.StringFlag{ + Name: "output.witness", + Usage: "Determines where to put the `witness` of the post-state.\n" + + "\t`stdout` - into the stdout output\n" + + "\t`stderr` - into the stderr output\n" + + "\t - into the file ", + Value: "witness.json", + } InputAllocFlag = &cli.StringFlag{ Name: "input.alloc", Usage: "`stdin` or file name of where to find the prestate alloc to use.", @@ -123,6 +139,10 @@ var ( Usage: "`stdin` or file name of where to find the transactions list in RLP form.", Value: "txs.rlp", } + InputVKTFlag = &cli.StringFlag{ + Name: "input.vkt", + Usage: "`stdin` or file name of where to find the prestate VKT.", + } SealCliqueFlag = &cli.StringFlag{ Name: "seal.clique", Usage: "Seal block with Clique. `stdin` or file name of where to find the Clique sealing data.", diff --git a/cmd/evm/internal/t8ntool/gen_stenv.go b/cmd/evm/internal/t8ntool/gen_stenv.go index d47db4a8765..0c296395e2e 100644 --- a/cmd/evm/internal/t8ntool/gen_stenv.go +++ b/cmd/evm/internal/t8ntool/gen_stenv.go @@ -37,6 +37,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) { ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"` + ParentHash *common.Hash `json:"parentHash,omitempty"` } var enc stEnv enc.Coinbase = common.UnprefixedAddress(s.Coinbase) @@ -59,6 +60,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) { enc.ParentExcessBlobGas = (*math.HexOrDecimal64)(s.ParentExcessBlobGas) enc.ParentBlobGasUsed = (*math.HexOrDecimal64)(s.ParentBlobGasUsed) enc.ParentBeaconBlockRoot = s.ParentBeaconBlockRoot + enc.ParentHash = s.ParentHash return json.Marshal(&enc) } @@ -85,6 +87,7 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"` + ParentHash *common.Hash `json:"parentHash,omitempty"` } var dec stEnv if err := json.Unmarshal(input, &dec); err != nil { @@ -154,5 +157,8 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { if dec.ParentBeaconBlockRoot != nil { s.ParentBeaconBlockRoot = dec.ParentBeaconBlockRoot } + if dec.ParentHash != nil { + s.ParentHash = dec.ParentHash + } return nil } diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index e946ccddd56..6036f868b2a 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -75,10 +75,11 @@ var ( ) type input struct { - Alloc types.GenesisAlloc `json:"alloc,omitempty"` - Env *stEnv `json:"env,omitempty"` - Txs []*txWithKey `json:"txs,omitempty"` - TxRlp string `json:"txsRlp,omitempty"` + Alloc types.GenesisAlloc `json:"alloc,omitempty"` + Env *stEnv `json:"env,omitempty"` + VKT map[common.Hash]hexutil.Bytes `json:"vkt,omitempty"` + Txs []*txWithKey `json:"txs,omitempty"` + TxRlp string `json:"txsRlp,omitempty"` } func Transition(ctx *cli.Context) error { @@ -90,16 +91,16 @@ func Transition(ctx *cli.Context) error { // stdin input or in files. // Check if anything needs to be read from stdin var ( - prestate Prestate - txIt txIterator // txs to apply - allocStr = ctx.String(InputAllocFlag.Name) - + prestate Prestate + txIt txIterator // txs to apply + allocStr = ctx.String(InputAllocFlag.Name) + vktStr = ctx.String(InputVKTFlag.Name) envStr = ctx.String(InputEnvFlag.Name) txStr = ctx.String(InputTxsFlag.Name) inputData = &input{} ) // Figure out the prestate alloc - if allocStr == stdinSelector || envStr == stdinSelector || txStr == stdinSelector { + if allocStr == stdinSelector || vktStr == stdinSelector || envStr == stdinSelector || txStr == stdinSelector { decoder := json.NewDecoder(os.Stdin) if err := decoder.Decode(inputData); err != nil { return NewError(ErrorJson, fmt.Errorf("failed unmarshalling stdin: %v", err)) @@ -110,8 +111,18 @@ func Transition(ctx *cli.Context) error { return err } } + if vktStr != stdinSelector { + if err := readFile(vktStr, "VKT", &inputData.Alloc); err != nil { + return err + } + } prestate.Pre = inputData.Alloc - + if vktStr != stdinSelector && vktStr != "" { + if err := readFile(vktStr, "VKT", &inputData.VKT); err != nil { + return err + } + } + prestate.VKT = inputData.VKT // Set the block environment if envStr != stdinSelector { var env stEnv @@ -183,8 +194,14 @@ func Transition(ctx *cli.Context) error { } // Dump the execution result collector := make(Alloc) - s.DumpToCollector(collector, nil) - return dispatchOutput(ctx, baseDir, result, collector, body) + var vktleaves map[common.Hash]hexutil.Bytes + if !chainConfig.IsVerkle(big.NewInt(int64(prestate.Env.Number)), prestate.Env.Timestamp) { + s.DumpToCollector(collector, nil) + } else { + vktleaves = make(map[common.Hash]hexutil.Bytes) + s.DumpVKTLeaves(vktleaves) + } + return dispatchOutput(ctx, baseDir, result, collector, body, vktleaves) } func applyLondonChecks(env *stEnv, chainConfig *params.ChainConfig) error { @@ -306,7 +323,7 @@ func saveFile(baseDir, filename string, data interface{}) error { // dispatchOutput writes the output data to either stderr or stdout, or to the specified // files -func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, body hexutil.Bytes) error { +func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, body hexutil.Bytes, vkt map[common.Hash]hexutil.Bytes) error { stdOutObject := make(map[string]interface{}) stdErrObject := make(map[string]interface{}) dispatch := func(baseDir, fName, name string, obj interface{}) error { @@ -333,6 +350,11 @@ func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, a if err := dispatch(baseDir, ctx.String(OutputBodyFlag.Name), "body", body); err != nil { return err } + if vkt != nil { + if err := dispatch(baseDir, ctx.String(OutputVKTFlag.Name), "vkt", vkt); err != nil { + return err + } + } if len(stdOutObject) > 0 { b, err := json.MarshalIndent(stdOutObject, "", " ") if err != nil { diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 61e46aa50e1..7f405a7df16 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -146,10 +146,13 @@ var ( t8ntool.TraceEnableCallFramesFlag, t8ntool.OutputBasedir, t8ntool.OutputAllocFlag, + t8ntool.OutputVKTFlag, + t8ntool.OutputWitnessFlag, t8ntool.OutputResultFlag, t8ntool.OutputBodyFlag, t8ntool.InputAllocFlag, t8ntool.InputEnvFlag, + t8ntool.InputVKTFlag, t8ntool.InputTxsFlag, t8ntool.ForknameFlag, t8ntool.ChainIDFlag, diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index f0be52a0dff..cfe0a5e6890 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -98,6 +98,17 @@ data, and verifies that all snapshot storage data has a corresponding account. Description: ` geth snapshot inspect-account
checks all snapshot layers and prints out information about the specified address. +`, + }, + { + Name: "inspect-garys-account", + Usage: "Check all snapshot layers for the specific account", + ArgsUsage: "
", + Action: checkGarysAccounts, + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), + Description: ` +geth snapshot inspect-account
checks all snapshot layers and prints out +information about the specified address. `, }, { @@ -692,3 +703,72 @@ func checkAccount(ctx *cli.Context) error { log.Info("Checked the snapshot journalled storage", "time", common.PrettyDuration(time.Since(start))) return nil } + +func checkGarysAccounts(ctx *cli.Context) error { + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + chaindb := utils.MakeChainDatabase(ctx, stack, true) + defer chaindb.Close() + + triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false) + defer triedb.Close() + + var root common.Hash + if ctx.NArg() == 1 { + rootBytes := common.FromHex(ctx.Args().Get(1)) + if len(rootBytes) != common.HashLength { + return fmt.Errorf("invalid hash: %s", ctx.Args().Get(1)) + } + root = common.BytesToHash(rootBytes) + } else { + headBlock := rawdb.ReadHeadBlock(chaindb) + if headBlock == nil { + log.Error("Failed to load head block") + return errors.New("no head block") + } + root = headBlock.Root() + } + snapConfig := snapshot.Config{ + CacheSize: 256, + Recovery: false, + NoBuild: true, + AsyncBuild: false, + } + snaptree, err := snapshot.New(snapConfig, chaindb, triedb, root) + if err != nil { + return err + } + accIt, err := snaptree.AccountIterator(root, common.Hash{}) + if err != nil { + log.Error("Failed to create account iterator", "error", err) + return err + } + defer accIt.Release() + + // Open the file in append mode, create it if it doesn't exist + file, err := os.OpenFile("weird_contracts.csv", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return err + } + defer file.Close() + file.WriteString("account hash,nonce,code hash,root hash,balance\n") + + var weird int + for accIt.Next() { + acc, err := types.FullAccount(accIt.Account()) + if err != nil { + log.Error("Failed to get full account", "error", err) + return err + } + + if acc.Nonce == 0 && bytes.Equal(acc.CodeHash, types.EmptyCodeHash[:]) && acc.Root != types.EmptyRootHash { + weird++ + if _, err = file.WriteString(fmt.Sprintf("%x,%d,%x,%x,%s\n", accIt.Hash(), acc.Nonce, acc.CodeHash, acc.Root, acc.Balance.String())); err != nil { + return err + } + } + } + log.Info("sweep completed", "count", weird) + return nil +} diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index cc9f44e460a..5c943405790 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -354,9 +354,9 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types. // FinalizeAndAssemble implements consensus.Engine, setting the final state and // assembling the block. -func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) { +func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, statedb *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) { if !beacon.IsPoSHeader(header) { - return beacon.ethone.FinalizeAndAssemble(chain, header, state, body, receipts) + return beacon.ethone.FinalizeAndAssemble(chain, header, statedb, body, receipts) } shanghai := chain.Config().IsShanghai(header.Number, header.Time) if shanghai { @@ -370,10 +370,10 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea } } // Finalize and assemble the block. - beacon.Finalize(chain, header, state, body) + beacon.Finalize(chain, header, statedb, body) // Assign the final state root to header. - header.Root = state.IntermediateRoot(true) + header.Root = statedb.IntermediateRoot(true) // Assemble the final block. block := types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)) @@ -381,19 +381,21 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea // Create the block witness and attach to block. // This step needs to happen as late as possible to catch all access events. if chain.Config().IsVerkle(header.Number, header.Time) { - keys := state.AccessEvents().Keys() + // TODO(gballet) move this to the end of the overlay conversion function in a subsequent PR + statedb.Database().SaveTransitionState(header.Root, &state.TransitionState{Ended: true}) + keys := statedb.AccessEvents().Keys() // Open the pre-tree to prove the pre-state against parent := chain.GetHeaderByNumber(header.Number.Uint64() - 1) if parent == nil { return nil, fmt.Errorf("nil parent header for block %d", header.Number) } - preTrie, err := state.Database().OpenTrie(parent.Root) + preTrie, err := statedb.Database().OpenTrie(parent.Root) if err != nil { return nil, fmt.Errorf("error opening pre-state tree root: %w", err) } vktPreTrie, okpre := preTrie.(*trie.VerkleTrie) - vktPostTrie, okpost := state.GetTrie().(*trie.VerkleTrie) + vktPostTrie, okpost := statedb.GetTrie().(*trie.VerkleTrie) // The witness is only attached iff both parent and current block are // using verkle tree. diff --git a/core/blockchain.go b/core/blockchain.go index d56996dadbe..c08b05e7f47 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -283,12 +283,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis if cacheConfig == nil { cacheConfig = defaultCacheConfig } - // Open trie database with provided config - enableVerkle, err := EnableVerkleAtGenesis(db, genesis) - if err != nil { - return nil, err - } - triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(enableVerkle)) + triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(genesis.IsVerkle())) // Write the supplied genesis to the database if it has not been initialized // yet. The corresponding chain config will be returned, either from the diff --git a/core/chain_makers.go b/core/chain_makers.go index 7a258dc05f8..a3825137c6f 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -536,8 +536,10 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine return block, b.receipts } + sdb := state.NewDatabase(trdb, nil) + for i := 0; i < n; i++ { - statedb, err := state.New(parent.Root(), state.NewDatabase(trdb, nil)) + statedb, err := state.New(parent.Root(), sdb) if err != nil { panic(err) } @@ -575,6 +577,7 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine func GenerateVerkleChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (common.Hash, ethdb.Database, []*types.Block, []types.Receipts, []*verkle.VerkleProof, []verkle.StateDiff) { db := rawdb.NewMemoryDatabase() + saveVerkleTransitionStatusAtVerlkeGenesis(db) cacheConfig := DefaultCacheConfigWithScheme(rawdb.PathScheme) cacheConfig.SnapshotLimit = 0 triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true)) diff --git a/core/genesis.go b/core/genesis.go index 95782a827a4..da01a420c4a 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -18,6 +18,7 @@ package core import ( "bytes" + "encoding/gob" "encoding/json" "errors" "fmt" @@ -145,6 +146,9 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) { emptyRoot = types.EmptyVerkleHash } db := rawdb.NewMemoryDatabase() + if isVerkle { + saveVerkleTransitionStatusAtVerlkeGenesis(db) + } statedb, err := state.New(emptyRoot, state.NewDatabase(triedb.NewDatabase(db, config), nil)) if err != nil { return common.Hash{}, err @@ -276,6 +280,24 @@ func (o *ChainOverrides) apply(cfg *params.ChainConfig) error { return cfg.CheckConfigForkOrder() } +// saveVerkleTransitionStatusAtVerlkeGenesis saves a conversion marker +// representing a converted state, which is used in devnets that activate +// verkle at genesis. +func saveVerkleTransitionStatusAtVerlkeGenesis(db ethdb.Database) { + saveVerkleTransitionStatus(db, common.Hash{}, &state.TransitionState{Ended: true}) +} + +func saveVerkleTransitionStatus(db ethdb.Database, root common.Hash, ts *state.TransitionState) { + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + err := enc.Encode(ts) + if err != nil { + log.Error("failed to encode transition state", "err", err) + return + } + rawdb.WriteVerkleTransitionState(db, root, buf.Bytes()) +} + // SetupGenesisBlock writes or updates the genesis block in db. // The block that will be used is: // @@ -299,6 +321,11 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g if genesis != nil && genesis.Config == nil { return nil, common.Hash{}, nil, errGenesisNoConfig } + // In case of verkle-at-genesis, we need to ensure that the conversion + // markers are indicating that the conversion has completed. + if genesis != nil && genesis.Config.VerkleTime != nil && *genesis.Config.VerkleTime == genesis.Timestamp { + saveVerkleTransitionStatusAtVerlkeGenesis(db) + } // Commit the genesis if the database is empty ghash := rawdb.ReadCanonicalHash(db, 0) if (ghash == common.Hash{}) { @@ -443,7 +470,7 @@ func (g *Genesis) chainConfigOrDefault(ghash common.Hash, stored *params.ChainCo // IsVerkle indicates whether the state is already stored in a verkle // tree at genesis time. func (g *Genesis) IsVerkle() bool { - return g.Config.IsVerkleGenesis() + return g != nil && g.Config != nil && g.Config.VerkleTime != nil && *g.Config.VerkleTime == g.Timestamp } // ToBlock returns the genesis block according to genesis specification. @@ -547,6 +574,9 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Blo if err != nil { return nil, err } + if g.IsVerkle() { + saveVerkleTransitionStatus(db, block.Root(), &state.TransitionState{Ended: true}) + } batch := db.NewBatch() rawdb.WriteGenesisStateSpec(batch, block.Hash(), blob) rawdb.WriteBlock(batch, block) @@ -569,29 +599,6 @@ func (g *Genesis) MustCommit(db ethdb.Database, triedb *triedb.Database) *types. return block } -// EnableVerkleAtGenesis indicates whether the verkle fork should be activated -// at genesis. This is a temporary solution only for verkle devnet testing, where -// verkle fork is activated at genesis, and the configured activation date has -// already passed. -// -// In production networks (mainnet and public testnets), verkle activation always -// occurs after the genesis block, making this function irrelevant in those cases. -func EnableVerkleAtGenesis(db ethdb.Database, genesis *Genesis) (bool, error) { - if genesis != nil { - if genesis.Config == nil { - return false, errGenesisNoConfig - } - return genesis.Config.EnableVerkleAtGenesis, nil - } - if ghash := rawdb.ReadCanonicalHash(db, 0); ghash != (common.Hash{}) { - chainCfg := rawdb.ReadChainConfig(db, ghash) - if chainCfg != nil { - return chainCfg.EnableVerkleAtGenesis, nil - } - } - return false, nil -} - // DefaultGenesisBlock returns the Ethereum main net genesis block. func DefaultGenesisBlock() *Genesis { return &Genesis{ diff --git a/core/genesis_test.go b/core/genesis_test.go index 726bda86bb0..837ec7cdd83 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -286,7 +286,6 @@ func TestVerkleGenesisCommit(t *testing.T) { OsakaTime: &verkleTime, VerkleTime: &verkleTime, TerminalTotalDifficulty: big.NewInt(0), - EnableVerkleAtGenesis: true, Ethash: nil, Clique: nil, BlobScheduleConfig: ¶ms.BlobScheduleConfig{ @@ -314,6 +313,7 @@ func TestVerkleGenesisCommit(t *testing.T) { } db := rawdb.NewMemoryDatabase() + saveVerkleTransitionStatusAtVerlkeGenesis(db) triedb := triedb.NewDatabase(db, triedb.VerkleDefaults) block := genesis.MustCommit(db, triedb) if !bytes.Equal(block.Root().Bytes(), expected) { diff --git a/core/rawdb/accessors_overlay.go b/core/rawdb/accessors_overlay.go new file mode 100644 index 00000000000..364cc889d1e --- /dev/null +++ b/core/rawdb/accessors_overlay.go @@ -0,0 +1,30 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rawdb + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethdb" +) + +func ReadVerkleTransitionState(db ethdb.KeyValueReader, hash common.Hash) ([]byte, error) { + return db.Get(transitionStateKey(hash)) +} + +func WriteVerkleTransitionState(db ethdb.KeyValueWriter, hash common.Hash, state []byte) error { + return db.Put(transitionStateKey(hash), state) +} diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index fa125cecc05..a4af6557d9a 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -147,6 +147,9 @@ var ( preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil) preimageHitsCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil) preimageMissCounter = metrics.NewRegisteredCounter("db/preimage/miss", nil) + + // Verkle transition information + VerkleTransitionStatePrefix = []byte("verkle-transition-state-") ) // LegacyTxLookupEntry is the legacy TxLookupEntry definition with some unnecessary @@ -362,3 +365,8 @@ func filterMapBlockLVKey(number uint64) []byte { binary.BigEndian.PutUint64(key[l:], number) return key } + +// transitionStateKey = transitionStatusKey + hash +func transitionStateKey(hash common.Hash) []byte { + return append(VerkleTransitionStatePrefix, hash.Bytes()...) +} diff --git a/core/state/database.go b/core/state/database.go index faf4954650b..082dbd82575 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -17,7 +17,10 @@ package state import ( + "bytes" + "encoding/gob" "fmt" + "reflect" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/lru" @@ -26,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/utils" @@ -62,6 +66,12 @@ type Database interface { // Snapshot returns the underlying state snapshot. Snapshot() *snapshot.Tree + + // SaveTransitionState saves the tree transition progress markers to the database. + SaveTransitionState(common.Hash, *TransitionState) + + // LoadTransitionState loads the tree transition progress markers from the database. + LoadTransitionState(common.Hash) *TransitionState } // Trie is a Ethereum Merkle Patricia trie. @@ -141,6 +151,50 @@ type Trie interface { IsVerkle() bool } +// TransitionState is a structure that holds the progress markers of the +// translation process. +type TransitionState struct { + CurrentAccountAddress *common.Address // addresss of the last translated account + CurrentSlotHash common.Hash // hash of the last translated storage slot + CurrentPreimageOffset int64 // next byte to read from the preimage file + Started, Ended bool + + // Mark whether the storage for an account has been processed. This is useful if the + // maximum number of leaves of the conversion is reached before the whole storage is + // processed. + StorageProcessed bool + + BaseRoot common.Hash // hash of the last read-only MPT base tree +} + +// InTransition returns true if the translation process is in progress. +func (ts *TransitionState) InTransition() bool { + return ts != nil && ts.Started && !ts.Ended +} + +// Transitioned returns true if the translation process has been completed. +func (ts *TransitionState) Transitioned() bool { + return ts != nil && ts.Ended +} + +// Copy returns a deep copy of the TransitionState object. +func (ts *TransitionState) Copy() *TransitionState { + ret := &TransitionState{ + Started: ts.Started, + Ended: ts.Ended, + CurrentSlotHash: ts.CurrentSlotHash, + CurrentPreimageOffset: ts.CurrentPreimageOffset, + StorageProcessed: ts.StorageProcessed, + } + + if ts.CurrentAccountAddress != nil { + ret.CurrentAccountAddress = &common.Address{} + copy(ret.CurrentAccountAddress[:], ts.CurrentAccountAddress[:]) + } + + return ret +} + // CachingDB is an implementation of Database interface. It leverages both trie and // state snapshot to provide functionalities for state access. It's meant to be a // long-live object and has a few caches inside for sharing between blocks. @@ -151,17 +205,21 @@ type CachingDB struct { codeCache *lru.SizeConstrainedCache[common.Hash, []byte] codeSizeCache *lru.Cache[common.Hash, int] pointCache *utils.PointCache + + // Transition-specific fields + TransitionStatePerRoot lru.BasicLRU[common.Hash, *TransitionState] } // NewDatabase creates a state database with the provided data sources. func NewDatabase(triedb *triedb.Database, snap *snapshot.Tree) *CachingDB { return &CachingDB{ - disk: triedb.Disk(), - triedb: triedb, - snap: snap, - codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize), - codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize), - pointCache: utils.NewPointCache(pointCacheSize), + disk: triedb.Disk(), + triedb: triedb, + snap: snap, + codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize), + codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize), + pointCache: utils.NewPointCache(pointCacheSize), + TransitionStatePerRoot: lru.NewBasicLRU[common.Hash, *TransitionState](1000), } } @@ -193,9 +251,10 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) { readers = append(readers, newFlatReader(reader)) // state reader is optional } } + ts := db.LoadTransitionState(stateRoot) // Set up the trie reader, which is expected to always be available // as the gatekeeper unless the state is corrupted. - tr, err := newTrieReader(stateRoot, db.triedb, db.pointCache) + tr, err := newTrieReader(stateRoot, db.triedb, db.pointCache, ts) if err != nil { return nil, err } @@ -210,7 +269,11 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) { // OpenTrie opens the main account trie at a specific root hash. func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) { - if db.triedb.IsVerkle() { + ts := db.LoadTransitionState(root) + if ts.InTransition() { + panic("transition isn't supported yet") + } + if ts.Transitioned() { return trie.NewVerkleTrie(root, db.triedb, db.pointCache) } tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb) @@ -222,10 +285,11 @@ func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) { // OpenStorageTrie opens the storage trie of an account. func (db *CachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, self Trie) (Trie, error) { + ts := db.LoadTransitionState(stateRoot) // In the verkle case, there is only one tree. But the two-tree structure // is hardcoded in the codebase. So we need to return the same trie in this // case. - if db.triedb.IsVerkle() { + if ts.InTransition() || ts.Transitioned() { return self, nil } tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, crypto.Keccak256Hash(address.Bytes()), root), db.triedb) @@ -277,3 +341,86 @@ func mustCopyTrie(t Trie) Trie { panic(fmt.Errorf("unknown trie type %T", t)) } } + +// SaveTransitionState saves the transition state to the cache and commits +// it to the database if it's not already in the cache. +func (db *CachingDB) SaveTransitionState(root common.Hash, ts *TransitionState) { + if ts == nil { + panic("nil transition state") + } + + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + err := enc.Encode(ts) + if err != nil { + log.Error("failed to encode transition state", "err", err) + return + } + + if !db.TransitionStatePerRoot.Contains(root) { + // Copy so that the address pointer isn't updated after + // it has been saved. + db.TransitionStatePerRoot.Add(root, ts.Copy()) + rawdb.WriteVerkleTransitionState(db.TrieDB().Disk(), root, buf.Bytes()) + } else { + // Check that the state is consistent with what is in the cache, + // which is not strictly necessary but a good sanity check. Can + // be removed when the transition is stable. + cachedState, _ := db.TransitionStatePerRoot.Get(root) + if !reflect.DeepEqual(cachedState, ts) { + fmt.Println("transition state mismatch", "cached state", cachedState, "new state", ts) + panic("transition state mismatch") + } + } + + log.Debug("saving transition state", "storage processed", ts.StorageProcessed, "addr", ts.CurrentAccountAddress, "slot hash", ts.CurrentSlotHash, "root", root, "ended", ts.Ended, "started", ts.Started) +} + +func (db *CachingDB) LoadTransitionState(root common.Hash) *TransitionState { + // Try to get the transition state from the cache and + // the DB if it's not there. + ts, ok := db.TransitionStatePerRoot.Get(root) + if !ok { + // Not in the cache, try getting it from the DB + data, _ := rawdb.ReadVerkleTransitionState(db.TrieDB().Disk(), root) + // if err != nil && errors.Is(err, triedb.ErrNotFound) { + // log.Error("failed to read transition state", "err", err) + // return nil + // } + + // if a state could be read from the db, attempt to decode it + if len(data) > 0 { + var ( + newts TransitionState + buf = bytes.NewBuffer(data[:]) + dec = gob.NewDecoder(buf) + ) + // Decode transition state + err := dec.Decode(&newts) + if err != nil { + log.Error("failed to decode transition state", "err", err) + return nil + } + ts = &newts + } + + // Fallback that should only happen before the transition + if ts == nil { + // Initialize the first transition state, with the "ended" + // field set to true if the database was created + // as a verkle database. + log.Debug("no transition state found, starting fresh", "is verkle", db.triedb.IsVerkle()) + // Start with a fresh state + ts = &TransitionState{Ended: db.triedb.IsVerkle()} + } + + db.TransitionStatePerRoot.Add(root, ts) + } + + // Copy so that the CurrentAddress pointer in the map + // doesn't get overwritten. + // db.CurrentTransitionState = ts.Copy() + + log.Debug("loaded transition state", "storage processed", ts.StorageProcessed, "addr", ts.CurrentAccountAddress, "slot hash", ts.CurrentSlotHash, "root", root, "ended", ts.Ended, "started", ts.Started) + return ts +} diff --git a/core/state/dump.go b/core/state/dump.go index c9aad4f8e23..3005c0810b3 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -208,6 +208,18 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] return nextKey } +func (s *StateDB) DumpVKTLeaves(collector map[common.Hash]hexutil.Bytes) { + it, err := s.trie.(*trie.VerkleTrie).NodeIterator(nil) + if err != nil { + panic(err) + } + for it.Next(true) { + if it.Leaf() { + collector[common.BytesToHash(it.LeafKey())] = it.LeafBlob() + } + } +} + // RawDump returns the state. If the processing is aborted e.g. due to options // reaching Max, the `Next` key is set on the returned Dump. func (s *StateDB) RawDump(opts *DumpConfig) Dump { diff --git a/core/state/reader.go b/core/state/reader.go index a0f15dfcc8b..ae2bf0945d1 100644 --- a/core/state/reader.go +++ b/core/state/reader.go @@ -207,12 +207,12 @@ type trieReader struct { // trieReader constructs a trie reader of the specific state. An error will be // returned if the associated trie specified by root is not existent. -func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCache) (*trieReader, error) { +func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCache, ts *TransitionState) (*trieReader, error) { var ( tr Trie err error ) - if !db.IsVerkle() { + if !ts.Transitioned() && !ts.InTransition() { tr, err = trie.NewStateTrie(trie.StateTrieID(root), db) } else { tr, err = trie.NewVerkleTrie(root, db, cache) diff --git a/core/state/statedb.go b/core/state/statedb.go index e3f5b9e1a0a..6d1ef45a7bd 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -181,7 +181,7 @@ func New(root common.Hash, db Database) (*StateDB, error) { accessList: newAccessList(), transientStorage: newTransientStorage(), } - if db.TrieDB().IsVerkle() { + if tr.IsVerkle() { sdb.accessEvents = NewAccessEvents(db.PointCache()) } return sdb, nil diff --git a/core/verkle_witness_test.go b/core/verkle_witness_test.go index de2280ced1c..353c4310150 100644 --- a/core/verkle_witness_test.go +++ b/core/verkle_witness_test.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/binary" "encoding/hex" + "fmt" "math/big" "slices" "testing" @@ -58,7 +59,6 @@ var ( ShanghaiTime: u64(0), VerkleTime: u64(0), TerminalTotalDifficulty: common.Big0, - EnableVerkleAtGenesis: true, BlobScheduleConfig: ¶ms.BlobScheduleConfig{ Verkle: params.DefaultPragueBlobConfig, }, @@ -82,7 +82,6 @@ var ( ShanghaiTime: u64(0), VerkleTime: u64(0), TerminalTotalDifficulty: common.Big0, - EnableVerkleAtGenesis: true, BlobScheduleConfig: ¶ms.BlobScheduleConfig{ Verkle: params.DefaultPragueBlobConfig, }, @@ -202,6 +201,9 @@ func TestProcessVerkle(t *testing.T) { t.Log("verified verkle proof, inserting blocks into the chain") + for i, b := range chain { + fmt.Printf("%d %x\n", i, b.Root()) + } endnum, err := blockchain.InsertChain(chain) if err != nil { t.Fatalf("block %d imported with error: %v", endnum, err) diff --git a/params/config.go b/params/config.go index 8f9e02583bf..b7e839ff8af 100644 --- a/params/config.go +++ b/params/config.go @@ -415,19 +415,6 @@ type ChainConfig struct { DepositContractAddress common.Address `json:"depositContractAddress,omitempty"` - // EnableVerkleAtGenesis is a flag that specifies whether the network uses - // the Verkle tree starting from the genesis block. If set to true, the - // genesis state will be committed using the Verkle tree, eliminating the - // need for any Verkle transition later. - // - // This is a temporary flag only for verkle devnet testing, where verkle is - // activated at genesis, and the configured activation date has already passed. - // - // In production networks (mainnet and public testnets), verkle activation - // always occurs after the genesis block, making this flag irrelevant in - // those cases. - EnableVerkleAtGenesis bool `json:"enableVerkleAtGenesis,omitempty"` - // Various consensus engines Ethash *EthashConfig `json:"ethash,omitempty"` Clique *CliqueConfig `json:"clique,omitempty"` @@ -651,20 +638,6 @@ func (c *ChainConfig) IsVerkle(num *big.Int, time uint64) bool { return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time) } -// IsVerkleGenesis checks whether the verkle fork is activated at the genesis block. -// -// Verkle mode is considered enabled if the verkle fork time is configured, -// regardless of whether the local time has surpassed the fork activation time. -// This is a temporary workaround for verkle devnet testing, where verkle is -// activated at genesis, and the configured activation date has already passed. -// -// In production networks (mainnet and public testnets), verkle activation -// always occurs after the genesis block, making this function irrelevant in -// those cases. -func (c *ChainConfig) IsVerkleGenesis() bool { - return c.EnableVerkleAtGenesis -} - // IsEIP4762 returns whether eip 4762 has been activated at given block. func (c *ChainConfig) IsEIP4762(num *big.Int, time uint64) bool { return c.IsVerkle(num, time) diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 77bf945e40e..e42e69ad725 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -117,19 +117,20 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t return UnsupportedForkError{t.json.Network} } // import pre accounts & construct test genesis block & state root + // Commit genesis state + gspec := t.genesis(config) var ( db = rawdb.NewMemoryDatabase() tconf = &triedb.Config{ Preimages: true, + IsVerkle: gspec.Config.VerkleTime != nil && *gspec.Config.VerkleTime <= gspec.Timestamp, } ) - if scheme == rawdb.PathScheme { + if scheme == rawdb.PathScheme || tconf.IsVerkle { tconf.PathDB = pathdb.Defaults } else { tconf.HashDB = hashdb.Defaults } - // Commit genesis state - gspec := t.genesis(config) // if ttd is not specified, set an arbitrary huge value if gspec.Config.TerminalTotalDifficulty == nil { diff --git a/tests/init.go b/tests/init.go index a8bc424fa2c..2db7ca1b75c 100644 --- a/tests/init.go +++ b/tests/init.go @@ -464,6 +464,45 @@ var Forks = map[string]*params.ChainConfig{ Osaka: params.DefaultOsakaBlobConfig, }, }, + + "Verkle": { + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiTime: u64(0), + VerkleTime: u64(0), + }, + "ShanghaiToVerkleAtTime32": { + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiTime: u64(0), + VerkleTime: u64(32), + }, } // AvailableForks returns the set of defined fork names