diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 61542140b00..76d69a7108d 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -28,7 +28,7 @@ jobs: with: go-version: 1.21.1 - name: Download golangci-lint - run: wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s latest + run: wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.54.2 - name: Lint run: ./bin/golangci-lint run - name: Vet @@ -42,7 +42,5 @@ jobs: uses: actions/setup-go@v2 with: go-version: 1.21.1 - - name: Download precomputed points - run: wget -nv https://github.com/gballet/go-verkle/releases/download/banderwagonv3/precomp -Otrie/utils/precomp - name: Test - run: go test ./... + run: go test ./... -timeout=20m diff --git a/beacon/engine/gen_ed.go b/beacon/engine/gen_ed.go index 6893d64a162..2a92e742040 100644 --- a/beacon/engine/gen_ed.go +++ b/beacon/engine/gen_ed.go @@ -17,23 +17,24 @@ var _ = (*executableDataMarshaling)(nil) // MarshalJSON marshals as JSON. func (e ExecutableData) MarshalJSON() ([]byte, error) { type ExecutableData struct { - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` - StateRoot common.Hash `json:"stateRoot" gencodec:"required"` - ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"` - LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"` - Random common.Hash `json:"prevRandao" gencodec:"required"` - Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"` - GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` - ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"` - BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` - BlockHash common.Hash `json:"blockHash" gencodec:"required"` - Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` - Withdrawals []*types.Withdrawal `json:"withdrawals"` - BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"` - ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` + StateRoot common.Hash `json:"stateRoot" gencodec:"required"` + ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"` + LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"` + Random common.Hash `json:"prevRandao" gencodec:"required"` + Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` + ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"` + BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` + BlockHash common.Hash `json:"blockHash" gencodec:"required"` + Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` + Withdrawals []*types.Withdrawal `json:"withdrawals"` + BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"` + ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"` + ExecutionWitness *types.ExecutionWitness `json:"executionWitness"` } var enc ExecutableData enc.ParentHash = e.ParentHash @@ -58,29 +59,31 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { enc.Withdrawals = e.Withdrawals enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed) enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas) + enc.ExecutionWitness = e.ExecutionWitness return json.Marshal(&enc) } // UnmarshalJSON unmarshals from JSON. func (e *ExecutableData) UnmarshalJSON(input []byte) error { type ExecutableData struct { - ParentHash *common.Hash `json:"parentHash" gencodec:"required"` - FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"` - StateRoot *common.Hash `json:"stateRoot" gencodec:"required"` - ReceiptsRoot *common.Hash `json:"receiptsRoot" gencodec:"required"` - LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"` - Random *common.Hash `json:"prevRandao" gencodec:"required"` - Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"` - GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` - ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"` - BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` - BlockHash *common.Hash `json:"blockHash" gencodec:"required"` - Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` - Withdrawals []*types.Withdrawal `json:"withdrawals"` - BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"` - ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"` + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"` + StateRoot *common.Hash `json:"stateRoot" gencodec:"required"` + ReceiptsRoot *common.Hash `json:"receiptsRoot" gencodec:"required"` + LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"` + Random *common.Hash `json:"prevRandao" gencodec:"required"` + Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"` + BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` + BlockHash *common.Hash `json:"blockHash" gencodec:"required"` + Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` + Withdrawals []*types.Withdrawal `json:"withdrawals"` + BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"` + ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"` + ExecutionWitness *types.ExecutionWitness `json:"executionWitness"` } var dec ExecutableData if err := json.Unmarshal(input, &dec); err != nil { @@ -154,5 +157,8 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { if dec.ExcessBlobGas != nil { e.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas) } + if dec.ExecutionWitness != nil { + e.ExecutionWitness = dec.ExecutionWitness + } return nil } diff --git a/beacon/engine/types.go b/beacon/engine/types.go index f1801edd1a9..0066e918175 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -64,6 +64,8 @@ type ExecutableData struct { Withdrawals []*types.Withdrawal `json:"withdrawals"` BlobGasUsed *uint64 `json:"blobGasUsed"` ExcessBlobGas *uint64 `json:"excessBlobGas"` + + ExecutionWitness *types.ExecutionWitness `json:"executionWitness"` } // JSON type overrides for executableData. @@ -208,24 +210,25 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash) withdrawalsRoot = &h } header := &types.Header{ - ParentHash: params.ParentHash, - UncleHash: types.EmptyUncleHash, - Coinbase: params.FeeRecipient, - Root: params.StateRoot, - TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), - ReceiptHash: params.ReceiptsRoot, - Bloom: types.BytesToBloom(params.LogsBloom), - Difficulty: common.Big0, - Number: new(big.Int).SetUint64(params.Number), - GasLimit: params.GasLimit, - GasUsed: params.GasUsed, - Time: params.Timestamp, - BaseFee: params.BaseFeePerGas, - Extra: params.ExtraData, - MixDigest: params.Random, - WithdrawalsHash: withdrawalsRoot, - ExcessBlobGas: params.ExcessBlobGas, - BlobGasUsed: params.BlobGasUsed, + ParentHash: params.ParentHash, + UncleHash: types.EmptyUncleHash, + Coinbase: params.FeeRecipient, + Root: params.StateRoot, + TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), + ReceiptHash: params.ReceiptsRoot, + Bloom: types.BytesToBloom(params.LogsBloom), + Difficulty: common.Big0, + Number: new(big.Int).SetUint64(params.Number), + GasLimit: params.GasLimit, + GasUsed: params.GasUsed, + Time: params.Timestamp, + BaseFee: params.BaseFeePerGas, + Extra: params.ExtraData, + MixDigest: params.Random, + WithdrawalsHash: withdrawalsRoot, + ExcessBlobGas: params.ExcessBlobGas, + BlobGasUsed: params.BlobGasUsed, + ExecutionWitness: params.ExecutionWitness, } block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals) if block.Hash() != params.BlockHash { @@ -238,23 +241,24 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash) // fields from the given block. It assumes the given block is post-merge block. func BlockToExecutableData(block *types.Block, fees *big.Int, blobs []kzg4844.Blob, commitments []kzg4844.Commitment, proofs []kzg4844.Proof) *ExecutionPayloadEnvelope { data := &ExecutableData{ - BlockHash: block.Hash(), - ParentHash: block.ParentHash(), - FeeRecipient: block.Coinbase(), - StateRoot: block.Root(), - Number: block.NumberU64(), - GasLimit: block.GasLimit(), - GasUsed: block.GasUsed(), - BaseFeePerGas: block.BaseFee(), - Timestamp: block.Time(), - ReceiptsRoot: block.ReceiptHash(), - LogsBloom: block.Bloom().Bytes(), - Transactions: encodeTransactions(block.Transactions()), - Random: block.MixDigest(), - ExtraData: block.Extra(), - Withdrawals: block.Withdrawals(), - BlobGasUsed: block.BlobGasUsed(), - ExcessBlobGas: block.ExcessBlobGas(), + BlockHash: block.Hash(), + ParentHash: block.ParentHash(), + FeeRecipient: block.Coinbase(), + StateRoot: block.Root(), + Number: block.NumberU64(), + GasLimit: block.GasLimit(), + GasUsed: block.GasUsed(), + BaseFeePerGas: block.BaseFee(), + Timestamp: block.Time(), + ReceiptsRoot: block.ReceiptHash(), + LogsBloom: block.Bloom().Bytes(), + Transactions: encodeTransactions(block.Transactions()), + Random: block.MixDigest(), + ExtraData: block.Extra(), + Withdrawals: block.Withdrawals(), + BlobGasUsed: block.BlobGasUsed(), + ExcessBlobGas: block.ExcessBlobGas(), + ExecutionWitness: block.ExecutionWitness(), } blobsBundle := BlobsBundleV1{ Commitments: make([]hexutil.Bytes, 0), diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index c408623fe32..035b367d34e 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -88,6 +88,7 @@ type stEnv struct { ExcessBlobGas *uint64 `json:"excessBlobGas,omitempty"` ParentExcessBlobGas *uint64 `json:"parentExcessBlobGas,omitempty"` ParentBlobGasUsed *uint64 `json:"parentBlobGasUsed,omitempty"` + ParentHash *common.Hash `json:"parentHash,omitempty"` } type stEnvMarshaling struct { @@ -182,6 +183,12 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 { misc.ApplyDAOHardFork(statedb) } + if chainConfig.IsPrague(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp) { + // insert all parent hashes in the contract + for i := pre.Env.Number - 1; i > 0 && i >= pre.Env.Number-257; i-- { + core.ProcessParentBlockHash(statedb, i, pre.Env.BlockHashes[math.HexOrDecimal64(i)]) + } + } var blobGasUsed uint64 for i, tx := range txs { if tx.Type() == types.BlobTxType && vmContext.ExcessBlobGas == nil { diff --git a/cmd/evm/internal/t8ntool/gen_stenv.go b/cmd/evm/internal/t8ntool/gen_stenv.go index f50fee5e31c..5436407f07b 100644 --- a/cmd/evm/internal/t8ntool/gen_stenv.go +++ b/cmd/evm/internal/t8ntool/gen_stenv.go @@ -36,6 +36,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) { ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas,omitempty"` ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` + ParentHash *common.Hash `json:"parentHash,omitempty"` } var enc stEnv enc.Coinbase = common.UnprefixedAddress(s.Coinbase) @@ -57,6 +58,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) { enc.ExcessBlobGas = (*math.HexOrDecimal64)(s.ExcessBlobGas) enc.ParentExcessBlobGas = (*math.HexOrDecimal64)(s.ParentExcessBlobGas) enc.ParentBlobGasUsed = (*math.HexOrDecimal64)(s.ParentBlobGasUsed) + enc.ParentHash = s.ParentHash return json.Marshal(&enc) } @@ -82,6 +84,7 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas,omitempty"` ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` + ParentHash *common.Hash `json:"parentHash,omitempty"` } var dec stEnv if err := json.Unmarshal(input, &dec); err != nil { @@ -148,5 +151,8 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { if dec.ParentBlobGasUsed != nil { s.ParentBlobGasUsed = (*uint64)(dec.ParentBlobGasUsed) } + if dec.ParentHash != nil { + s.ParentHash = dec.ParentHash + } return nil } diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 8e195bcf964..2179b61032b 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -49,7 +49,7 @@ var ( Name: "init", Usage: "Bootstrap and initialize a new genesis block", ArgsUsage: "", - Flags: flags.Merge([]cli.Flag{utils.CachePreimagesFlag}, utils.DatabasePathFlags), + Flags: flags.Merge([]cli.Flag{utils.CachePreimagesFlag, utils.OverridePrague}, utils.DatabasePathFlags), Description: ` The init command initializes a new genesis block and definition for the network. This is a destructive action and changes the network in which you will be @@ -201,6 +201,12 @@ func initGenesis(ctx *cli.Context) error { stack, _ := makeConfigNode(ctx) defer stack.Close() + var overrides core.ChainOverrides + if ctx.IsSet(utils.OverridePrague.Name) { + v := ctx.Uint64(utils.OverridePrague.Name) + overrides.OverridePrague = &v + } + for _, name := range []string{"chaindata", "lightchaindata"} { chaindb, err := stack.OpenDatabaseWithFreezer(name, 0, 0, ctx.String(utils.AncientFlag.Name), "", false) if err != nil { @@ -208,8 +214,9 @@ func initGenesis(ctx *cli.Context) error { } triedb := trie.NewDatabaseWithConfig(chaindb, &trie.Config{ Preimages: ctx.Bool(utils.CachePreimagesFlag.Name), + Verkle: genesis.IsVerkle(), }) - _, hash, err := core.SetupGenesisBlock(chaindb, triedb, genesis) + _, hash, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides) if err != nil { utils.Fatalf("Failed to write genesis block: %v", err) } diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 1a3de04bd4d..bf01c6f9185 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -171,9 +171,9 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { v := ctx.Uint64(utils.OverrideCancun.Name) cfg.Eth.OverrideCancun = &v } - if ctx.IsSet(utils.OverrideVerkle.Name) { - v := ctx.Uint64(utils.OverrideVerkle.Name) - cfg.Eth.OverrideVerkle = &v + if ctx.IsSet(utils.OverridePrague.Name) { + v := ctx.Uint64(utils.OverridePrague.Name) + cfg.Eth.OverridePrague = &v } backend, eth := utils.RegisterEthService(stack, &cfg.Eth) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index a239f88499a..38fb755b4b5 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -68,7 +68,7 @@ var ( utils.USBFlag, utils.SmartCardDaemonPathFlag, utils.OverrideCancun, - utils.OverrideVerkle, + utils.OverridePrague, utils.EnablePersonal, utils.TxPoolLocalsFlag, utils.TxPoolNoLocalsFlag, diff --git a/cmd/geth/verkle.go b/cmd/geth/verkle.go index d1953697b9b..ea424ebaf13 100644 --- a/cmd/geth/verkle.go +++ b/cmd/geth/verkle.go @@ -36,7 +36,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" tutils "github.com/ethereum/go-ethereum/trie/utils" - "github.com/gballet/go-verkle" + "github.com/ethereum/go-verkle" "github.com/holiman/uint256" cli "github.com/urfave/cli/v2" ) @@ -202,7 +202,7 @@ func convertToVerkle(ctx *cli.Context) error { // Otherwise, store the previous group in the tree with a // stem insertion. - vRoot.InsertStem(chunkkey[:31], values, chaindb.Get) + vRoot.InsertValuesAtStem(chunkkey[:31], values, chaindb.Get) } // Write the code size in the account header group @@ -267,7 +267,7 @@ func convertToVerkle(ctx *cli.Context) error { copy(k[:], []byte(s)) // reminder that InsertStem will merge leaves // if they exist. - vRoot.InsertStem(k[:31], vs, chaindb.Get) + vRoot.InsertValuesAtStem(k[:31], vs, chaindb.Get) } translatedStorage = make(map[string][][]byte) vRoot.FlushAtDepth(2, saveverkle) @@ -276,7 +276,7 @@ func convertToVerkle(ctx *cli.Context) error { for s, vs := range translatedStorage { var k [31]byte copy(k[:], []byte(s)) - vRoot.InsertStem(k[:31], vs, chaindb.Get) + vRoot.InsertValuesAtStem(k[:31], vs, chaindb.Get) } storageIt.Release() if storageIt.Error() != nil { @@ -285,7 +285,7 @@ func convertToVerkle(ctx *cli.Context) error { } } // Finish with storing the complete account header group inside the tree. - vRoot.InsertStem(stem[:31], newValues, chaindb.Get) + vRoot.InsertValuesAtStem(stem[:31], newValues, chaindb.Get) if time.Since(lastReport) > time.Second*8 { log.Info("Traversing state", "accounts", accounts, "elapsed", common.PrettyDuration(time.Since(start))) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index c92f49d432b..b927d0f94f8 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -268,8 +268,8 @@ var ( Usage: "Manually specify the Cancun fork timestamp, overriding the bundled setting", Category: flags.EthCategory, } - OverrideVerkle = &cli.Uint64Flag{ - Name: "override.verkle", + OverridePrague = &cli.Uint64Flag{ + Name: "override.prague", Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting", Category: flags.EthCategory, } diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 64be7b0005d..35a7ed2b56d 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/utils" + "github.com/ethereum/go-verkle" "github.com/holiman/uint256" ) @@ -356,9 +357,12 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types. state.AddBalance(w.Address, amount) // The returned gas is not charged + state.Witness().TouchAddressOnWriteAndComputeGas(w.Address[:], uint256.Int{}, utils.VersionLeafKey) state.Witness().TouchAddressOnWriteAndComputeGas(w.Address[:], uint256.Int{}, utils.BalanceLeafKey) + state.Witness().TouchAddressOnWriteAndComputeGas(w.Address[:], uint256.Int{}, utils.NonceLeafKey) + state.Witness().TouchAddressOnWriteAndComputeGas(w.Address[:], uint256.Int{}, utils.CodeKeccakLeafKey) + state.Witness().TouchAddressOnWriteAndComputeGas(w.Address[:], uint256.Int{}, utils.CodeSizeLeafKey) } - // No block reward which is issued by consensus layer instead. } // FinalizeAndAssemble implements consensus.Engine, setting the final state and @@ -384,8 +388,61 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea // Assign the final state root to header. header.Root = state.IntermediateRoot(true) + var ( + p *verkle.VerkleProof + k verkle.StateDiff + keys = state.Witness().Keys() + ) + if chain.Config().IsPrague(header.Number, header.Time) && chain.Config().ProofInBlocks { + // 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) + if err != nil { + return nil, fmt.Errorf("error opening pre-state tree root: %w", err) + } + + var okpre, okpost bool + var vtrpre, vtrpost *trie.VerkleTrie + switch pre := preTrie.(type) { + case *trie.VerkleTrie: + vtrpre, okpre = preTrie.(*trie.VerkleTrie) + vtrpost, okpost = state.GetTrie().(*trie.VerkleTrie) + case *trie.TransitionTrie: + vtrpre = pre.Overlay() + okpre = true + post, _ := state.GetTrie().(*trie.TransitionTrie) + vtrpost = post.Overlay() + okpost = true + default: + // This should only happen for the first block of the + // conversion, when the previous tree is a merkle tree. + // Logically, the "previous" verkle tree is an empty tree. + okpre = true + vtrpre = trie.NewVerkleTrie(verkle.New(), state.Database().TrieDB(), utils.NewPointCache(), false) + post := state.GetTrie().(*trie.TransitionTrie) + vtrpost = post.Overlay() + okpost = true + } + if okpre && okpost { + if len(keys) > 0 { + p, k, err = trie.ProveAndSerialize(vtrpre, vtrpost, keys, vtrpre.FlatdbNodeResolver) + if err != nil { + return nil, fmt.Errorf("error generating verkle proof for block %d: %w", header.Number, err) + } + } + } + } + // Assemble and return the final block. - return types.NewBlockWithWithdrawals(header, txs, uncles, receipts, withdrawals, trie.NewStackTrie(nil)), nil + block := types.NewBlockWithWithdrawals(header, txs, uncles, receipts, withdrawals, trie.NewStackTrie(nil)) + if chain.Config().IsPrague(header.Number, header.Time) && chain.Config().ProofInBlocks { + block.SetVerkleProof(p, k) + } + return block, nil } // Seal generates a new sealing request for the given input block and pushes diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 92f8100f6e6..44aec25c121 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -568,7 +568,7 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header r.Div(r, big8) // This should not happen, but it's useful for replay tests - if config.IsVerkle(header.Number, header.Time) { + if config.IsPrague(header.Number, header.Time) { state.Witness().TouchAddressOnReadAndComputeGas(uncle.Coinbase.Bytes(), uint256.Int{}, utils.BalanceLeafKey) } state.AddBalance(uncle.Coinbase, r) @@ -576,7 +576,7 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header r.Div(blockReward, big32) reward.Add(reward, r) } - if config.IsVerkle(header.Number, header.Time) { + if config.IsPrague(header.Number, header.Time) { state.Witness().TouchAddressOnReadAndComputeGas(header.Coinbase.Bytes(), uint256.Int{}, utils.BalanceLeafKey) state.Witness().TouchAddressOnReadAndComputeGas(header.Coinbase.Bytes(), uint256.Int{}, utils.VersionLeafKey) state.Witness().TouchAddressOnReadAndComputeGas(header.Coinbase.Bytes(), uint256.Int{}, utils.NonceLeafKey) diff --git a/core/blockchain.go b/core/blockchain.go index a1b3b6b6cb6..a97bfb20c1a 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -242,6 +242,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis triedb := trie.NewDatabaseWithConfig(db, &trie.Config{ Cache: cacheConfig.TrieCleanLimit, Preimages: cacheConfig.Preimages, + Verkle: true, }) // Setup the genesis block, commit the provided genesis specification // to database if the genesis block is not present yet, or load the @@ -310,8 +311,8 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis // Make sure the state associated with the block is available head := bc.CurrentBlock() - // Declare the end of the verkle transition is need be - if bc.chainConfig.Rules(head.Number, false /* XXX */, head.Time).IsVerkle { + // Declare the end of the verkle transition if need be + if bc.chainConfig.Rules(head.Number, false /* XXX */, head.Time).IsPrague { bc.stateCache.EndVerkleTransition() } @@ -411,7 +412,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis Recovery: recover, NoBuild: bc.cacheConfig.SnapshotNoBuild, AsyncBuild: !bc.cacheConfig.SnapshotWait, - Verkle: chainConfig.IsVerkle(head.Number, head.Time), + Verkle: chainConfig.IsPrague(head.Number, head.Time), } bc.snaps, _ = snapshot.New(snapconfig, bc.db, bc.triedb, head.Root) } @@ -1347,6 +1348,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. if err := blockBatch.Write(); err != nil { log.Crit("Failed to write block into disk", "err", err) } + state.Database().TrieDB().WritePreimages() // Commit all cached state changes into underlying memory database. root, err := state.Commit(block.NumberU64(), bc.chainConfig.IsEIP158(block.Number())) if err != nil { @@ -2531,12 +2533,11 @@ func (bc *BlockChain) GetTrieFlushInterval() time.Duration { return time.Duration(bc.flushInterval.Load()) } -func (bc *BlockChain) StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, cancunTime *uint64) { - bc.stateCache.StartVerkleTransition(originalRoot, translatedRoot, chainConfig, cancunTime) +func (bc *BlockChain) StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, pragueTime *uint64) { + bc.stateCache.StartVerkleTransition(originalRoot, translatedRoot, chainConfig, pragueTime) } - -func (bc *BlockChain) EndVerkleTransition() { - bc.stateCache.EndVerkleTransition() +func (bc *BlockChain) ReorgThroughVerkleTransition() { + bc.stateCache.ReorgThroughVerkleTransition() } func (bc *BlockChain) AddRootTranslation(originalRoot, translatedRoot common.Hash) { diff --git a/core/chain_makers.go b/core/chain_makers.go index d2aaef26097..5b9dc0c6ff0 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -32,7 +32,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" - "github.com/gballet/go-verkle" + "github.com/ethereum/go-verkle" ) // BlockGen creates blocks for testing. @@ -357,7 +357,7 @@ func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, if err != nil { panic(err) } - if genesis.Config != nil && genesis.Config.IsVerkle(genesis.ToBlock().Number(), genesis.ToBlock().Time()) { + if genesis.Config != nil && genesis.Config.IsPrague(genesis.ToBlock().Number(), genesis.ToBlock().Time()) { blocks, receipts, _, _ := GenerateVerkleChain(genesis.Config, genesis.ToBlock(), engine, db, n, gen) return db, blocks, receipts } @@ -372,14 +372,28 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine proofs := make([]*verkle.VerkleProof, 0, n) keyvals := make([]verkle.StateDiff, 0, n) blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n) - chainreader := &fakeChainReader{config: config} - var preStateTrie *trie.VerkleTrie + chainreader := &generatedLinearChainReader{ + config: config, + // GenerateVerkleChain should only be called with the genesis block + // as parent. + genesis: parent, + chain: blocks, + } genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) { b := &BlockGen{i: i, chain: blocks, parent: parent, statedb: statedb, config: config, engine: engine} b.header = makeHeader(chainreader, parent, statedb, b.engine) preState := statedb.Copy() fmt.Println("prestate", preState.GetTrie().(*trie.VerkleTrie).ToDot()) + if config.IsPrague(b.header.Number, b.header.Time) { + if !config.IsPrague(b.parent.Number(), b.parent.Time()) { + // Transition case: insert all 256 ancestors + InsertBlockHashHistoryAtEip2935Fork(statedb, b.header.Number.Uint64()-1, b.header.ParentHash, chainreader) + } else { + ProcessParentBlockHash(statedb, b.header.Number.Uint64()-1, b.header.ParentHash) + } + } + // Mutate the state and block according to any hard-fork specs if daoBlock := config.DAOForkBlock; daoBlock != nil { limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange) @@ -412,57 +426,17 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine panic(fmt.Sprintf("trie write error: %v", err)) } - // Generate an associated verkle proof - tr := preState.GetTrie() - if !tr.IsVerkle() { - panic("tree should be verkle") - } - - vtr := tr.(*trie.VerkleTrie) - // Make sure all keys are resolved before - // building the proof. Ultimately, node - // resolution can be done with a prefetcher - // or from GetCommitmentsAlongPath. - kvs := make(map[string][]byte) - keys := statedb.Witness().Keys() - for _, key := range keys { - v, err := vtr.GetWithHashedKey(key) - if err != nil { - panic(err) - } - kvs[string(key)] = v - } - - // Initialize the preStateTrie if it is nil, this should - // correspond to the genesis block. This is a workaround - // needed until the main verkle PR is rebased on top of - // PBSS. - if preStateTrie == nil { - preStateTrie = vtr - } - - vtr.Hash() - p, k, err := preStateTrie.ProveAndSerialize(statedb.Witness().Keys()) - if err != nil { - panic(err) - } - proofs = append(proofs, p) - keyvals = append(keyvals, k) - - // save the current state of the trie for producing the proof for the next block, - // since reading it from disk is broken with the intermediate PBSS-like system we - // have: it will read the post-state as this is the only state present on disk. - // This is a workaround needed until the main verkle PR is rebased on top of PBSS. - preStateTrie = statedb.GetTrie().(*trie.VerkleTrie) + proofs = append(proofs, block.ExecutionWitness().VerkleProof) + keyvals = append(keyvals, block.ExecutionWitness().StateDiff) return block, b.receipts } return nil, nil } var snaps *snapshot.Tree + triedb := state.NewDatabaseWithConfig(db, nil) + triedb.EndVerkleTransition() for i := 0; i < n; i++ { - triedb := state.NewDatabaseWithConfig(db, nil) - triedb.EndVerkleTransition() statedb, err := state.New(parent.Root(), triedb, snaps) if err != nil { panic(fmt.Sprintf("could not find state for block %d: err=%v, parent root=%x", i, err, parent.Root())) @@ -558,3 +532,59 @@ func (cr *fakeChainReader) GetHeaderByHash(hash common.Hash) *types.Header func (cr *fakeChainReader) GetHeader(hash common.Hash, number uint64) *types.Header { return nil } func (cr *fakeChainReader) GetBlock(hash common.Hash, number uint64) *types.Block { return nil } func (cr *fakeChainReader) GetTd(hash common.Hash, number uint64) *big.Int { return nil } + +type generatedLinearChainReader struct { + config *params.ChainConfig + genesis *types.Block + chain []*types.Block +} + +func (v *generatedLinearChainReader) Config() *params.ChainConfig { + return v.config +} + +func (v *generatedLinearChainReader) CurrentHeader() *types.Header { + return nil +} + +func (v *generatedLinearChainReader) GetHeader(_ common.Hash, number uint64) *types.Header { + if number == 0 { + return v.genesis.Header() + } + return v.chain[number-1].Header() +} + +func (v *generatedLinearChainReader) GetHeaderByNumber(number uint64) *types.Header { + if number == 0 { + return v.genesis.Header() + } + return v.chain[number-1].Header() +} + +func (v *generatedLinearChainReader) GetHeaderByHash(hash common.Hash) *types.Header { + if hash == v.genesis.Hash() { + return v.genesis.Header() + } + + for _, block := range v.chain { + if block.Hash() == hash { + return block.Header() + } + } + + return nil +} + +func (v *generatedLinearChainReader) GetBlock(_ common.Hash, number uint64) *types.Block { + if number == 0 { + return v.genesis + } + return v.chain[number-1] +} + +func (v *generatedLinearChainReader) GetTd(_ common.Hash, number uint64) *big.Int { + if number == 0 { + return v.genesis.Difficulty() + } + return v.chain[number-1].Difficulty() +} diff --git a/core/genesis.go b/core/genesis.go index 6b521369bcb..c8a4bc5952d 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -125,7 +125,7 @@ func (ga *GenesisAlloc) deriveHash(cfg *params.ChainConfig, timestamp uint64) (c // Create an ephemeral in-memory database for computing hash, // all the derived states will be discarded to not pollute disk. db := state.NewDatabase(rawdb.NewMemoryDatabase()) - if cfg.IsVerkle(big.NewInt(int64(0)), timestamp) { + if cfg.IsPrague(big.NewInt(int64(0)), timestamp) { db.EndVerkleTransition() } statedb, err := state.New(types.EmptyRootHash, db, nil) @@ -289,7 +289,7 @@ func (e *GenesisMismatchError) Error() string { // ChainOverrides contains the changes to chain config. type ChainOverrides struct { OverrideCancun *uint64 - OverrideVerkle *uint64 + OverridePrague *uint64 } // SetupGenesisBlock writes or updates the genesis block in db. @@ -318,8 +318,8 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen if overrides != nil && overrides.OverrideCancun != nil { config.CancunTime = overrides.OverrideCancun } - if overrides != nil && overrides.OverrideVerkle != nil { - config.VerkleTime = overrides.OverrideVerkle + if overrides != nil && overrides.OverridePrague != nil { + config.PragueTime = overrides.OverridePrague } } } @@ -332,17 +332,17 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen } else { log.Info("Writing custom genesis block") } + applyOverrides(genesis.Config) block, err := genesis.Commit(db, triedb) if err != nil { return genesis.Config, common.Hash{}, err } - applyOverrides(genesis.Config) return genesis.Config, block.Hash(), nil } // We have the genesis block in database(perhaps in ancient database) // but the corresponding state is missing. header := rawdb.ReadHeader(db, stored, 0) - if header.Root != types.EmptyRootHash && !rawdb.HasLegacyTrieNode(db, header.Root) { + if !triedb.IsVerkle() && header.Root != types.EmptyRootHash && !rawdb.HasLegacyTrieNode(db, header.Root) { if genesis == nil { genesis = DefaultGenesisBlock() } @@ -360,6 +360,7 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen } // Check whether the genesis block is already written. if genesis != nil { + applyOverrides(genesis.Config) hash := genesis.ToBlock().Hash() if hash != stored { return genesis.Config, hash, &GenesisMismatchError{stored, hash} @@ -368,9 +369,12 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen // Get the existing chain configuration. newcfg := genesis.configOrDefault(stored) applyOverrides(newcfg) - if err := newcfg.CheckConfigForkOrder(); err != nil { - return newcfg, common.Hash{}, err - } + // WORKAROUND it looks like this is broken, because overriding + // pragueTime will cause an error here, claiming that shanghaiTime + // wasn't set (it is). + // if err := newcfg.CheckConfigForkOrder(); err != nil { + // return newcfg, common.Hash{}, err + // } storedcfg := rawdb.ReadChainConfig(db, stored) if storedcfg == nil { log.Warn("Found genesis block without chain config") @@ -452,6 +456,12 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig { } } +// IsVerkle indicates whether the state is already stored in a verkle +// tree at genesis time. +func (g *Genesis) IsVerkle() bool { + return g.Config.IsPrague(new(big.Int).SetUint64(g.Number), g.Timestamp) +} + // ToBlock returns the genesis block according to genesis specification. func (g *Genesis) ToBlock() *types.Block { root, err := g.Alloc.deriveHash(g.Config, g.Timestamp) @@ -545,7 +555,7 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database) (*types.Block // Note the state changes will be committed in hash-based scheme, use Commit // if path-scheme is preferred. func (g *Genesis) MustCommit(db ethdb.Database) *types.Block { - triedb := trie.NewDatabaseWithConfig(db, &trie.Config{Verkle: g.Config != nil && g.Config.IsVerkle(big.NewInt(int64(g.Number)), g.Timestamp)}) + triedb := trie.NewDatabaseWithConfig(db, &trie.Config{Verkle: g.Config != nil && g.Config.IsPrague(big.NewInt(int64(g.Number)), g.Timestamp)}) block, err := g.Commit(db, triedb) if err != nil { panic(err) diff --git a/core/state/access_witness.go b/core/state/access_witness.go index 8b03cf371a6..696da15fd1e 100644 --- a/core/state/access_witness.go +++ b/core/state/access_witness.go @@ -118,7 +118,6 @@ func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte, createSen var gas uint64 gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.VersionLeafKey) gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.NonceLeafKey) - gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey) if createSendsValue { gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.BalanceLeafKey) } @@ -139,27 +138,35 @@ func (aw *AccessWitness) TouchAndChargeContractCreateCompleted(addr []byte) uint } func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) uint64 { - var gas uint64 - gas += aw.TouchAddressOnReadAndComputeGas(originAddr, zeroTreeIndex, utils.VersionLeafKey) - gas += aw.TouchAddressOnReadAndComputeGas(originAddr, zeroTreeIndex, utils.CodeSizeLeafKey) - gas += aw.TouchAddressOnReadAndComputeGas(originAddr, zeroTreeIndex, utils.CodeKeccakLeafKey) - gas += aw.TouchAddressOnWriteAndComputeGas(originAddr, zeroTreeIndex, utils.NonceLeafKey) - gas += aw.TouchAddressOnWriteAndComputeGas(originAddr, zeroTreeIndex, utils.BalanceLeafKey) - return gas + aw.TouchAddressOnReadAndComputeGas(originAddr, zeroTreeIndex, utils.VersionLeafKey) + aw.TouchAddressOnReadAndComputeGas(originAddr, zeroTreeIndex, utils.CodeSizeLeafKey) + aw.TouchAddressOnReadAndComputeGas(originAddr, zeroTreeIndex, utils.CodeKeccakLeafKey) + aw.TouchAddressOnWriteAndComputeGas(originAddr, zeroTreeIndex, utils.NonceLeafKey) + aw.TouchAddressOnWriteAndComputeGas(originAddr, zeroTreeIndex, utils.BalanceLeafKey) + + // Kaustinen note: we're currently experimenting with stop chargin gas for the origin address + // so simple transfer still take 21000 gas. This is to potentially avoid breaking existing tooling. + // This is the reason why we return 0 instead of `gas`. + // Note that we still have to touch the addresses to make sure the witness is correct. + return 0 } func (aw *AccessWitness) TouchTxExistingAndComputeGas(targetAddr []byte, sendsValue bool) uint64 { - var gas uint64 - gas += aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.VersionLeafKey) - gas += aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.CodeSizeLeafKey) - gas += aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.CodeKeccakLeafKey) - gas += aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.NonceLeafKey) + aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.VersionLeafKey) + aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.CodeSizeLeafKey) + aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.CodeKeccakLeafKey) + aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.NonceLeafKey) if sendsValue { - gas += aw.TouchAddressOnWriteAndComputeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey) + aw.TouchAddressOnWriteAndComputeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey) } else { - gas += aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey) + aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey) } - return gas + + // Kaustinen note: we're currently experimenting with stop chargin gas for the origin address + // so simple transfer still take 21000 gas. This is to potentially avoid breaking existing tooling. + // This is the reason why we return 0 instead of `gas`. + // Note that we still have to touch the addresses to make sure the witness is correct. + return 0 } func (aw *AccessWitness) TouchAddressOnWriteAndComputeGas(addr []byte, treeIndex uint256.Int, subIndex byte) uint64 { @@ -236,7 +243,7 @@ type branchAccessKey struct { func newBranchAccessKey(addr []byte, treeIndex uint256.Int) branchAccessKey { var sk branchAccessKey - copy(sk.addr[:], addr) + copy(sk.addr[20-len(addr):], addr) sk.treeIndex = treeIndex return sk } diff --git a/core/state/database.go b/core/state/database.go index 7ee8489d3f6..5707e2c88b6 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -19,7 +19,6 @@ package state import ( "errors" "fmt" - "sync" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/lru" @@ -31,7 +30,7 @@ import ( "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/utils" - "github.com/gballet/go-verkle" + "github.com/ethereum/go-verkle" ) const ( @@ -67,6 +66,8 @@ type Database interface { StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, cancunTime *uint64) + ReorgThroughVerkleTransition() + EndVerkleTransition() InTransition() bool @@ -210,7 +211,7 @@ func (db *cachingDB) Transitioned() bool { } // Fork implements the fork -func (db *cachingDB) StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, cancunTime *uint64) { +func (db *cachingDB) StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, pragueTime *uint64) { fmt.Println(` __________.__ .__ .__ __ .__ .__ ____ \__ ___| |__ ____ ____ | | ____ ______ | |__ _____ _____/ |_ | |__ _____ ______ __ _ _|__| ____ / ___\ ______ @@ -219,11 +220,18 @@ func (db *cachingDB) StartVerkleTransition(originalRoot, translatedRoot common.H |____| |___| /\___ \___ |____/\___ | __/|___| (____ |___| |__| |___| (____ /_____/ \/\_/ |__|___| /_____//_____/ |__|`) db.started = true - db.AddTranslation(originalRoot, translatedRoot) + db.ended = false + // db.AddTranslation(originalRoot, translatedRoot) db.baseRoot = originalRoot // initialize so that the first storage-less accounts are processed db.StorageProcessed = true - chainConfig.CancunTime = cancunTime + if pragueTime != nil { + chainConfig.PragueTime = pragueTime + } +} + +func (db *cachingDB) ReorgThroughVerkleTransition() { + db.ended, db.started = false, false } func (db *cachingDB) EndVerkleTransition() { @@ -241,26 +249,6 @@ func (db *cachingDB) EndVerkleTransition() { db.ended = true } -func (db *cachingDB) AddTranslation(orig, trans common.Hash) { - // TODO make this persistent - db.translatedRootsLock.Lock() - defer db.translatedRootsLock.Unlock() - db.translatedRoots[db.translationIndex] = trans - db.origRoots[db.translationIndex] = orig - db.translationIndex = (db.translationIndex + 1) % len(db.translatedRoots) -} - -func (db *cachingDB) getTranslation(orig common.Hash) common.Hash { - db.translatedRootsLock.RLock() - defer db.translatedRootsLock.RUnlock() - for i, o := range db.origRoots { - if o == orig { - return db.translatedRoots[i] - } - } - return common.Hash{} -} - type cachingDB struct { disk ethdb.KeyValueStore codeSizeCache *lru.Cache[common.Hash, int] @@ -269,12 +257,8 @@ type cachingDB struct { // Verkle specific fields // TODO ensure that this info is in the DB - started, ended bool - translatedRoots [32]common.Hash // hash of the translated root, for opening - origRoots [32]common.Hash - translationIndex int - translatedRootsLock sync.RWMutex - LastMerkleRoot common.Hash // root hash of the read-only base tree + started, ended bool + LastMerkleRoot common.Hash // root hash of the read-only base tree addrToPoint *utils.PointCache @@ -320,13 +304,8 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) { // TODO separate both cases when I can be certain that it won't // find a Verkle trie where is expects a Transitoion trie. if db.started || db.ended { - var r common.Hash - if db.ended { - r = root - } else { - r = db.getTranslation(root) - } - vkt, err := db.openVKTrie(r) + // NOTE this is a kaustinen-only change, it will break replay + vkt, err := db.openVKTrie(root) if err != nil { return nil, err } @@ -365,8 +344,9 @@ func (db *cachingDB) openStorageMPTrie(stateRoot common.Hash, address common.Add // 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) { + // TODO this should only return a verkle tree if db.ended { - mpt, err := db.openStorageMPTrie(common.Hash{}, address, common.Hash{}, self) + mpt, err := db.openStorageMPTrie(types.EmptyRootHash, address, common.Hash{}, self) if err != nil { return nil, err } @@ -511,7 +491,6 @@ func (db *cachingDB) GetStorageProcessed() bool { } func (db *cachingDB) AddRootTranslation(originalRoot, translatedRoot common.Hash) { - db.AddTranslation(originalRoot, translatedRoot) } func (db *cachingDB) SetLastMerkleRoot(root common.Hash) { diff --git a/core/state/statedb.go b/core/state/statedb.go index 21010e32f5f..1b47746458b 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -175,30 +175,11 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) } if tr.IsVerkle() { sdb.witness = sdb.NewAccessWitness() - // if sdb.snaps == nil { - // snapconfig := snapshot.Config{ - // CacheSize: 256, - // Recovery: false, - // NoBuild: false, - // AsyncBuild: false, - // Verkle: true, - // } - // sdb.snaps, err = snapshot.New(snapconfig, db.DiskDB(), db.TrieDB(), root) - // if err != nil { - // return nil, err - // } - // } - } - if sdb.snaps != nil { - if sdb.snap = sdb.snaps.Snapshot(root); sdb.snap == nil { - if db, ok := db.(*cachingDB); ok { - trans := db.getTranslation(root) - if trans != (common.Hash{}) { - sdb.snap = sdb.snaps.Snapshot(trans) - } - } - } } + // if sdb.snaps != nil { + // if sdb.snap = sdb.snaps.Snapshot(root); sdb.snap == nil { + // } + // } return sdb, nil } @@ -549,6 +530,14 @@ func (s *StateDB) Selfdestruct6780(addr common.Address) { } } +func (s *StateDB) WasCreatedInCurrentTx(addr common.Address) bool { + stateObject := s.getStateObject(addr) + if stateObject == nil { + return false + } + return stateObject.created +} + // SetTransientState sets transient storage for a given account. It // adds the change to the journal so that it can be rolled back // to its previous value if there is a revert. @@ -956,7 +945,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { // Thus, we can safely ignore it here continue } - if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) { + if (obj.selfDestructed || (deleteEmptyObjects && obj.empty())) && addr != params.HistoryStorageAddress { obj.deleted = true // We need to maintain account deletions explicitly (will remain diff --git a/core/state_processor.go b/core/state_processor.go index 5d10bceb181..cdb4e4af19b 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -35,8 +35,9 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" + "github.com/ethereum/go-ethereum/trie/utils" tutils "github.com/ethereum/go-ethereum/trie/utils" - "github.com/gballet/go-verkle" + "github.com/ethereum/go-verkle" "github.com/holiman/uint256" ) @@ -85,6 +86,14 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vmenv = vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg) signer = types.MakeSigner(p.config, header.Number, header.Time) ) + if p.config.IsPrague(block.Number(), block.Time()) { + parent := p.bc.GetBlockByHash(block.ParentHash()) + if !p.config.IsPrague(parent.Number(), parent.Time()) { + InsertBlockHashHistoryAtEip2935Fork(statedb, block.NumberU64()-1, block.ParentHash(), p.bc) + } else { + ProcessParentBlockHash(statedb, block.NumberU64()-1, block.ParentHash()) + } + } // Iterate over and process the individual transactions for i, tx := range block.Transactions() { msg, err := TransactionToMessage(tx, signer, header.BaseFee) @@ -359,3 +368,19 @@ func (kvm *keyValueMigrator) migrateCollectedKeyValues(tree *trie.VerkleTrie) er return nil } + +func InsertBlockHashHistoryAtEip2935Fork(statedb *state.StateDB, prevNumber uint64, prevHash common.Hash, chain consensus.ChainHeaderReader) { + ancestor := chain.GetHeader(prevHash, prevNumber) + for i := prevNumber; i > 0 && i >= prevNumber-256; i-- { + ProcessParentBlockHash(statedb, i, ancestor.Hash()) + ancestor = chain.GetHeader(ancestor.ParentHash, ancestor.Number.Uint64()-1) + } +} + +func ProcessParentBlockHash(statedb *state.StateDB, prevNumber uint64, prevHash common.Hash) { + var key common.Hash + binary.BigEndian.PutUint64(key[24:], prevNumber) + statedb.SetState(params.HistoryStorageAddress, key, prevHash) + index, suffix := utils.GetTreeKeyStorageSlotTreeIndexes(key[:]) + statedb.Witness().TouchAddressOnWriteAndComputeGas(params.HistoryStorageAddress[:], *index, suffix) +} diff --git a/core/state_processor_test.go b/core/state_processor_test.go index ffb3285b8f9..6b05435cd9b 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -17,8 +17,9 @@ package core import ( - //"bytes" + "bytes" "crypto/ecdsa" + "encoding/binary" //"fmt" "math/big" @@ -37,6 +38,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/trie/utils" //"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -451,9 +453,10 @@ func TestProcessVerkle(t *testing.T) { LondonBlock: big.NewInt(0), Ethash: new(params.EthashConfig), ShanghaiTime: u64(0), - VerkleTime: u64(0), + PragueTime: u64(0), TerminalTotalDifficulty: common.Big0, TerminalTotalDifficultyPassed: true, + ProofInBlocks: true, } signer = types.LatestSigner(config) testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") @@ -480,10 +483,10 @@ func TestProcessVerkle(t *testing.T) { // is now independent of the blockchain database. gspec.MustCommit(gendb) - txCost1 := params.WitnessBranchWriteCost*2 + params.WitnessBranchReadCost*2 + params.WitnessChunkWriteCost*3 + params.WitnessChunkReadCost*10 + params.TxGas - txCost2 := params.WitnessBranchWriteCost + params.WitnessBranchReadCost*2 + params.WitnessChunkWriteCost*2 + params.WitnessChunkReadCost*10 + params.TxGas - contractCreationCost := intrinsicContractCreationGas + uint64(6900 /* from */ +7700 /* creation */ +2939 /* execution costs */) - codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(6900 /* from */ +7000 /* creation */ +315944 /* execution costs */) + txCost1 := params.TxGas + txCost2 := params.TxGas + contractCreationCost := intrinsicContractCreationGas + uint64(7700 /* creation */ +2939 /* execution costs */) + codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(7000 /* creation */ +299744 /* execution costs */) blockGasUsagesExpected := []uint64{ txCost1*2 + txCost2, txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas, @@ -491,6 +494,7 @@ func TestProcessVerkle(t *testing.T) { // TODO utiliser GenerateChainWithGenesis pour le rendre plus pratique chain, _, proofs, keyvals := GenerateVerkleChain(gspec.Config, genesis, beacon.New(ethash.NewFaker()), gendb, 2, func(i int, gen *BlockGen) { gen.SetPoS() + // TODO need to check that the tx cost provided is the exact amount used (no remaining left-over) tx, _ := types.SignTx(types.NewTransaction(uint64(i)*3, common.Address{byte(i), 2, 3}, big.NewInt(999), txCost1, big.NewInt(875000000), nil), signer, testKey) gen.AddTx(tx) @@ -517,7 +521,7 @@ func TestProcessVerkle(t *testing.T) { //f.Write(buf.Bytes()) //fmt.Printf("root= %x\n", chain[0].Root()) // check the proof for the last block - err := trie.DeserializeAndVerifyVerkleProof(proofs[1], chain[0].Root().Bytes(), keyvals[1]) + err := trie.DeserializeAndVerifyVerkleProof(proofs[1], chain[0].Root().Bytes(), chain[1].Root().Bytes(), keyvals[1]) if err != nil { t.Fatal(err) } @@ -541,3 +545,966 @@ func TestProcessVerkle(t *testing.T) { } } } + +func TestProcessVerkleInvalidContractCreation(t *testing.T) { + var ( + config = ¶ms.ChainConfig{ + ChainID: big.NewInt(69420), + 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), + Ethash: new(params.EthashConfig), + ShanghaiTime: u64(0), + PragueTime: u64(0), + TerminalTotalDifficulty: common.Big0, + TerminalTotalDifficultyPassed: true, + ProofInBlocks: true, + } + bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain + gendb = rawdb.NewMemoryDatabase() // Database for the block-generation code, they must be separate as they are path-based. + coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7") + account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + gspec = &Genesis{ + Config: config, + Alloc: GenesisAlloc{ + coinbase: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account1: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account2: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 1, + }, + }, + } + ) + // Verkle trees use the snapshot, which must be enabled before the + // data is saved into the tree+database. + genesis := gspec.MustCommit(bcdb) + + // Commit the genesis block to the block-generation database as it + // is now independent of the blockchain database. + gspec.MustCommit(gendb) + + // Create two blocks that reproduce what is happening on kaustinen. + // - The first block contains two failing contract creation transactions, that write to storage before they revert. + // - The second block contains a single failing contract creation transaction, that fails right off the bat. + _, _, _, statediff := GenerateVerkleChain(gspec.Config, genesis, beacon.New(ethash.NewFaker()), gendb, 2, func(i int, gen *BlockGen) { + gen.SetPoS() + + if i == 0 { + var tx1, tx2, tx3 types.Transaction + // SSTORE at slot 105 and reverts + tx1payload := common.Hex2Bytes("f8d48084479c2c18830186a08080b8806000602955bda3f9600060ca55600060695523b360006039551983576000601255b0620c2fde2c592ac2600060bc55e0ac6000606455a63e22600060e655eb607e605c5360a2605d5360c7605e53601d605f5360eb606053606b606153608e60625360816063536079606453601e60655360fc60665360b7606753608b60685383021e7ca0cc20c65a97d2e526b8ec0f4266e8b01bdcde43b9aeb59d8bfb44e8eb8119c109a07a8e751813ae1b2ce734960dbc39a4f954917d7822a2c5d1dca18b06c584131f") + if err := tx1.UnmarshalBinary(tx1payload); err != nil { + t.Fatal(err) + } + gen.AddTx(&tx1) + + // SSTORE at slot 133 and reverts + tx2payload := common.Hex2Bytes("02f8db83010f2c01843b9aca0084479c2c18830186a08080b88060006085553fad6000600a55600060565555600060b55506600060cf557f1b8b38183e7bd1bdfaa7123c5a4976e54cce0e42049d841411978fd3595e25c66019527f0538943712953cf08900aae40222a40b2d5a4ac8075ad8cf0870e2be307edbb96039527f9f3174ff85024747041ae7a611acffb987c513c088d90ab288aec080a0cd6ac65ce2cb0a912371f6b5a551ba8caffc22ec55ad4d3cb53de41d05eb77b6a02e0dfe8513dfa6ec7bfd7eda6f5c0dac21b39b982436045e128cec46cfd3f960") + if err := tx2.UnmarshalBinary(tx2payload); err != nil { + t.Fatal(err) + } + gen.AddTx(&tx2) + + // this one is a simple transfer that succeeds, necessary to get the correct nonce in the other block. + tx3payload := common.Hex2Bytes("f8e80184479c2c18830186a094bbbbde4ca27f83fc18aa108170547ff57675936a80b8807ff71f7c15faadb969a76a5f54a81a0117e1e743cb7f24e378eda28442ea4c6eb6604a527fb5409e5718d44e23bfffac926e5ea726067f772772e7e19446acba0c853f62f5606a526020608a536088608b536039608c536004608d5360af608e537f7f7675d9f210e0a61564e6d11e7cd75f5bc9009ac9f6b94a0fc63035441a83021e7ba04a4a172d81ebb02847829b76a387ac09749c8b65668083699abe20c887fb9efca07c5b1a990702ec7b31a5e8e3935cd9a77649f8c25a84131229e24ab61aec6093") + if err := tx3.UnmarshalBinary(tx3payload); err != nil { + t.Fatal(err) + } + gen.AddTx(&tx3) + } else { + var tx types.Transaction + // immediately reverts + txpayload := common.Hex2Bytes("01f8d683010f2c028443ad7d0e830186a08080b880b00e7fa3c849dce891cce5fae8a4c46cbb313d6aec0c0ffe7863e05fb7b22d4807674c6055527ffbfcb0938f3e18f7937aa8fa95d880afebd5c4cec0d85186095832d03c85cf8a60755260ab60955360cf6096536066609753606e60985360fa609953609e609a53608e609b536024609c5360f6609d536072609e5360a4609fc080a08fc6f7101f292ff1fb0de8ac69c2d320fbb23bfe61cf327173786ea5daee6e37a044c42d91838ef06646294bf4f9835588aee66243b16a66a2da37641fae4c045f") + if err := tx.UnmarshalBinary(txpayload); err != nil { + t.Fatal(err) + } + gen.AddTx(&tx) + } + }) + + // Check that values 0x29 and 0x05 are found in the storage (and that they lead + // to no update, since the contract creation code reverted) + for _, stemStateDiff := range statediff[0] { + // Check that the value 0x85, which is overflowing the account header, + // is present. + if bytes.Equal(stemStateDiff.Stem[:], common.Hex2Bytes("a10042195481d30478251625e1ccef0e2174dc4e083e81d2566d880373f791")) { + for _, suffixDiff := range stemStateDiff.SuffixDiffs { + if suffixDiff.Suffix != 133 { + t.Fatalf("invalid suffix diff found for %x in block #1: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) + } + } + } else if bytes.Equal(stemStateDiff.Stem[:], common.Hex2Bytes("b24fa84f214459af17d6e3f604811f252cac93146f02d67d7811bbcdfa448b")) { + for _, suffixDiff := range stemStateDiff.SuffixDiffs { + if suffixDiff.Suffix != 105 && suffixDiff.Suffix != 0 && suffixDiff.Suffix != 2 && suffixDiff.Suffix != 3 { + t.Fatalf("invalid suffix diff found for %x in block #1: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) + } + } + } else if bytes.Equal(stemStateDiff.Stem[:], common.Hex2Bytes("97f2911f5efe08b74c28727d004e36d260225e73525fe2a300c8f58c7ffd76")) { + // BLOCKHASH contract stem + if len(stemStateDiff.SuffixDiffs) > 1 { + t.Fatalf("invalid suffix diff count found for BLOCKHASH contract: %d != 1", len(stemStateDiff.SuffixDiffs)) + } + if stemStateDiff.SuffixDiffs[0].Suffix != 64 { + t.Fatalf("invalid suffix diff value found for BLOCKHASH contract: %d != 64", stemStateDiff.SuffixDiffs[0].Suffix) + } + // check that the "current value" is nil and that the new value isn't. + if stemStateDiff.SuffixDiffs[0].CurrentValue != nil { + t.Fatalf("non-nil current value in BLOCKHASH contract insert: %x", stemStateDiff.SuffixDiffs[0].CurrentValue) + } + if stemStateDiff.SuffixDiffs[0].NewValue == nil { + t.Fatalf("nil new value in BLOCKHASH contract insert") + } + } else { + for _, suffixDiff := range stemStateDiff.SuffixDiffs { + if suffixDiff.Suffix > 4 { + t.Fatalf("invalid suffix diff found for %x in block #1: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) + } + } + } + } + + // Check that no account has a value above 4 in the 2nd block as no storage nor + // code should make it to the witness. + for _, stemStateDiff := range statediff[1] { + for _, suffixDiff := range stemStateDiff.SuffixDiffs { + if bytes.Equal(stemStateDiff.Stem[:], common.Hex2Bytes("97f2911f5efe08b74c28727d004e36d260225e73525fe2a300c8f58c7ffd76")) { + // BLOCKHASH contract stem + if len(stemStateDiff.SuffixDiffs) > 1 { + t.Fatalf("invalid suffix diff count found for BLOCKHASH contract at block #2: %d != 1", len(stemStateDiff.SuffixDiffs)) + } + if stemStateDiff.SuffixDiffs[0].Suffix != 65 { + t.Fatalf("invalid suffix diff value found for BLOCKHASH contract at block #2: %d != 65", stemStateDiff.SuffixDiffs[0].Suffix) + } + if stemStateDiff.SuffixDiffs[0].NewValue == nil { + t.Fatalf("missing post state value for BLOCKHASH contract at block #2") + } + if *stemStateDiff.SuffixDiffs[0].NewValue != common.HexToHash("53abcdfb284720ea59efe923d3dc774bbb7e787d829599f8ec7a81d344dd3d17") { + t.Fatalf("invalid post state value for BLOCKHASH contract at block #2: %x != ", (*stemStateDiff.SuffixDiffs[0].NewValue)[:]) + } + } else if suffixDiff.Suffix > 4 { + t.Fatalf("invalid suffix diff found for %x in block #2: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) + } + } + } +} + +func TestProcessVerkleContractWithEmptyCode(t *testing.T) { + var ( + config = ¶ms.ChainConfig{ + ChainID: big.NewInt(69421), + 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), + Ethash: new(params.EthashConfig), + ShanghaiTime: u64(0), + PragueTime: u64(0), + TerminalTotalDifficulty: common.Big0, + TerminalTotalDifficultyPassed: true, + ProofInBlocks: true, + } + bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain + gendb = rawdb.NewMemoryDatabase() // Database for the block-generation code, they must be separate as they are path-based. + coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7") + account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + gspec = &Genesis{ + Config: config, + Alloc: GenesisAlloc{ + coinbase: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account1: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account2: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 3, + }, + }, + } + ) + // Verkle trees use the snapshot, which must be enabled before the + // data is saved into the tree+database. + genesis := gspec.MustCommit(bcdb) + + // Commit the genesis block to the block-generation database as it + // is now independent of the blockchain database. + gspec.MustCommit(gendb) + + _, _, _, statediff := GenerateVerkleChain(gspec.Config, genesis, beacon.New(ethash.NewFaker()), gendb, 1, func(i int, gen *BlockGen) { + gen.SetPoS() + var tx types.Transaction + // a transaction that does some PUSH1n but returns a 0-sized contract + txpayload := common.Hex2Bytes("02f8db83010f2d03843b9aca008444cf6a05830186a08080b8807fdfbbb59f2371a76485ce557fd0de00c298d3ede52a3eab56d35af674eb49ec5860335260826053536001605453604c60555360f3605653606060575360446058536096605953600c605a5360df605b5360f3605c5360fb605d53600c605e53609a605f53607f60605360fe606153603d60625360f4606353604b60645360cac001a0486b6dc55b8a311568b7239a2cae1d77e7446dba71df61eaafd53f73820a138fa010bd48a45e56133ac4c5645142c2ea48950d40eb35050e9510b6bad9e15c5865") + if err := tx.UnmarshalBinary(txpayload); err != nil { + t.Fatal(err) + } + gen.AddTx(&tx) + }) + + for _, stemStateDiff := range statediff[0] { + if bytes.Equal(stemStateDiff.Stem[:], common.Hex2Bytes("97f2911f5efe08b74c28727d004e36d260225e73525fe2a300c8f58c7ffd76")) { + // BLOCKHASH contract stem + if len(stemStateDiff.SuffixDiffs) > 1 { + t.Fatalf("invalid suffix diff count found for BLOCKHASH contract: %d != 1", len(stemStateDiff.SuffixDiffs)) + } + if stemStateDiff.SuffixDiffs[0].Suffix != 64 { + t.Fatalf("invalid suffix diff value found for BLOCKHASH contract: %d != 64", stemStateDiff.SuffixDiffs[0].Suffix) + } + // check that the "current value" is nil and that the new value isn't. + if stemStateDiff.SuffixDiffs[0].CurrentValue != nil { + t.Fatalf("non-nil current value in BLOCKHASH contract insert: %x", stemStateDiff.SuffixDiffs[0].CurrentValue) + } + if stemStateDiff.SuffixDiffs[0].NewValue == nil { + t.Fatalf("nil new value in BLOCKHASH contract insert") + } + } else { + for _, suffixDiff := range stemStateDiff.SuffixDiffs { + if suffixDiff.Suffix > 4 { + // if d8898012c484fb48610ecb7963886339207dab004bce968b007b616ffa18e0 shows up, it means that the PUSHn + // in the transaction above added entries into the witness, when they should not have since they are + // part of a contract deployment. + t.Fatalf("invalid suffix diff found for %x in block #1: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) + } + } + } + } +} + +func TestProcessVerklExtCodeHashOpcode(t *testing.T) { + var ( + config = ¶ms.ChainConfig{ + ChainID: big.NewInt(69421), + 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), + Ethash: new(params.EthashConfig), + ShanghaiTime: u64(0), + PragueTime: u64(0), + TerminalTotalDifficulty: common.Big0, + TerminalTotalDifficultyPassed: true, + ProofInBlocks: true, + } + signer = types.LatestSigner(config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain + gendb = rawdb.NewMemoryDatabase() // Database for the block-generation code, they must be separate as they are path-based. + coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7") + account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + gspec = &Genesis{ + Config: config, + Alloc: GenesisAlloc{ + coinbase: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account1: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account2: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 3, + }, + }, + } + ) + // Verkle trees use the snapshot, which must be enabled before the + // data is saved into the tree+database. + genesis := gspec.MustCommit(bcdb) + + // Commit the genesis block to the block-generation database as it + // is now independent of the blockchain database. + gspec.MustCommit(gendb) + + dummyContract := []byte{ + 0x60, 2, // PUSH1 2 + 0x60, 12, // PUSH1 12 + 0x60, 0x00, // PUSH1 0 + 0x39, // CODECOPY + + 0x60, 2, // PUSH1 2 + 0x60, 0x00, // PUSH1 0 + 0xF3, // RETURN + + // Contract that auto-calls EXTCODEHASH + 0x60, 42, // PUSH1 42 + } + dummyContractAddr := common.HexToAddress("3a220f351252089d385b29beca14e27f204c296a") + extCodeHashContract := []byte{ + 0x60, 22, // PUSH1 22 + 0x60, 12, // PUSH1 12 + 0x60, 0x00, // PUSH1 0 + 0x39, // CODECOPY + + 0x60, 22, // PUSH1 22 + 0x60, 0x00, // PUSH1 0 + 0xF3, // RETURN + + // Contract that auto-calls EXTCODEHASH + 0x73, // PUSH20 + 0x3a, 0x22, 0x0f, 0x35, 0x12, 0x52, 0x08, 0x9d, 0x38, 0x5b, 0x29, 0xbe, 0xca, 0x14, 0xe2, 0x7f, 0x20, 0x4c, 0x29, 0x6a, + 0x3F, // EXTCODEHASH + } + extCodeHashContractAddr := common.HexToAddress("db7d6ab1f17c6b31909ae466702703daef9269cf") + _, _, _, statediff := GenerateVerkleChain(gspec.Config, genesis, beacon.New(ethash.NewFaker()), gendb, 2, func(i int, gen *BlockGen) { + gen.SetPoS() + + if i == 0 { + // Create dummy contract. + tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(0), 100_000, big.NewInt(875000000), dummyContract), signer, testKey) + gen.AddTx(tx) + + // Create contract with EXTCODEHASH opcode. + tx, _ = types.SignTx(types.NewContractCreation(1, big.NewInt(0), 100_000, big.NewInt(875000000), extCodeHashContract), signer, testKey) + gen.AddTx(tx) + } else { + tx, _ := types.SignTx(types.NewTransaction(2, extCodeHashContractAddr, big.NewInt(0), 100_000, big.NewInt(875000000), nil), signer, testKey) + gen.AddTx(tx) + } + }) + + contractKeccakTreeKey := utils.GetTreeKeyCodeKeccak(dummyContractAddr[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediff[1] { + if bytes.Equal(stemStateDiff.Stem[:], contractKeccakTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + codeHashStateDiff := statediff[1][stateDiffIdx].SuffixDiffs[0] + if codeHashStateDiff.Suffix != utils.CodeKeccakLeafKey { + t.Fatalf("code hash invalid suffix") + } + if codeHashStateDiff.CurrentValue == nil { + t.Fatalf("codeHash.CurrentValue must not be empty") + } + expCodeHash := crypto.Keccak256Hash(dummyContract[12:]) + if *codeHashStateDiff.CurrentValue != expCodeHash { + t.Fatalf("codeHash.CurrentValue unexpected code hash") + } + if codeHashStateDiff.NewValue != nil { + t.Fatalf("codeHash.NewValue must be nil") + } +} + +func TestProcessVerkleBalanceOpcode(t *testing.T) { + var ( + config = ¶ms.ChainConfig{ + ChainID: big.NewInt(69421), + 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), + Ethash: new(params.EthashConfig), + ShanghaiTime: u64(0), + PragueTime: u64(0), + TerminalTotalDifficulty: common.Big0, + TerminalTotalDifficultyPassed: true, + ProofInBlocks: true, + } + signer = types.LatestSigner(config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain + gendb = rawdb.NewMemoryDatabase() // Database for the block-generation code, they must be separate as they are path-based. + coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7") + account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + gspec = &Genesis{ + Config: config, + Alloc: GenesisAlloc{ + coinbase: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account1: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account2: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 3, + }, + }, + } + ) + // Verkle trees use the snapshot, which must be enabled before the + // data is saved into the tree+database. + genesis := gspec.MustCommit(bcdb) + + // Commit the genesis block to the block-generation database as it + // is now independent of the blockchain database. + gspec.MustCommit(gendb) + + _, _, _, statediff := GenerateVerkleChain(gspec.Config, genesis, beacon.New(ethash.NewFaker()), gendb, 1, func(i int, gen *BlockGen) { + gen.SetPoS() + txData := []byte{ + 0x73, // PUSH20 + 0x61, 0x77, 0x84, 0x3d, 0xb3, 0x13, 0x8a, 0xe6, 0x96, 0x79, 0xA5, 0x4b, 0x95, 0xcf, 0x34, 0x5E, 0xD7, 0x59, 0x45, 0x0d, // 0x6177843db3138ae69679A54b95cf345ED759450d + 0x31, // BALANCE + } + tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(0), 100_000, big.NewInt(875000000), txData), signer, testKey) + gen.AddTx(tx) + }) + + account2BalanceTreeKey := utils.GetTreeKeyBalance(account2[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediff[0] { + if bytes.Equal(stemStateDiff.Stem[:], account2BalanceTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + var zero [32]byte + balanceStateDiff := statediff[0][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BalanceLeafKey { + t.Fatalf("invalid suffix diff") + } + if balanceStateDiff.CurrentValue == nil { + t.Fatalf("invalid current value") + } + if *balanceStateDiff.CurrentValue == zero { + t.Fatalf("invalid current value") + } + if balanceStateDiff.NewValue != nil { + t.Fatalf("invalid new value") + } +} + +func TestProcessVerkleSelfDestructInSeparateTx(t *testing.T) { + var ( + config = ¶ms.ChainConfig{ + ChainID: big.NewInt(69421), + 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), + Ethash: new(params.EthashConfig), + ShanghaiTime: u64(0), + PragueTime: u64(0), + TerminalTotalDifficulty: common.Big0, + TerminalTotalDifficultyPassed: true, + ProofInBlocks: true, + } + signer = types.LatestSigner(config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain + gendb = rawdb.NewMemoryDatabase() // Database for the block-generation code, they must be separate as they are path-based. + coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7") + account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + gspec = &Genesis{ + Config: config, + Alloc: GenesisAlloc{ + coinbase: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account1: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account2: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 3, + }, + }, + } + ) + genesis := gspec.MustCommit(bcdb) + gspec.MustCommit(gendb) + + // The goal of this test is to test SELFDESTRUCT that happens in a contract execution which is created + // in a previous transaction. + + selfDestructContract := []byte{ + 0x60, 22, // PUSH1 22 + 0x60, 12, // PUSH1 12 + 0x60, 0x00, // PUSH1 0 + 0x39, // CODECOPY + + 0x60, 22, // PUSH1 22 + 0x60, 0x00, // PUSH1 0 + 0xF3, // RETURN + + // Deployed code + 0x73, // PUSH20 + 0x61, 0x77, 0x84, 0x3d, 0xb3, 0x13, 0x8a, 0xe6, 0x96, 0x79, 0xA5, 0x4b, 0x95, 0xcf, 0x34, 0x5E, 0xD7, 0x59, 0x45, 0x0d, // 0x6177843db3138ae69679A54b95cf345ED759450d + 0xFF, // SELFDESTRUCT + } + selfDestructContractAddr := common.HexToAddress("3a220f351252089d385b29beca14e27f204c296a") + _, _, _, statediff := GenerateVerkleChain(gspec.Config, genesis, beacon.New(ethash.NewFaker()), gendb, 2, func(i int, gen *BlockGen) { + gen.SetPoS() + + if i == 0 { + // Create selfdestruct contract, sending 42 wei. + tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(42), 100_000, big.NewInt(875000000), selfDestructContract), signer, testKey) + gen.AddTx(tx) + } else { + // Call it. + tx, _ := types.SignTx(types.NewTransaction(1, selfDestructContractAddr, big.NewInt(0), 100_000, big.NewInt(875000000), nil), signer, testKey) + gen.AddTx(tx) + } + }) + + var zero [32]byte + { // Check self-destructed contract in the witness + selfDestructContractTreeKey := utils.GetTreeKeyCodeKeccak(selfDestructContractAddr[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediff[1] { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + balanceStateDiff := statediff[1][stateDiffIdx].SuffixDiffs[1] + if balanceStateDiff.Suffix != utils.BalanceLeafKey { + t.Fatalf("balance invalid suffix") + } + + // The original balance was 42. + var fourtyTwo [32]byte + fourtyTwo[0] = 42 + if *balanceStateDiff.CurrentValue != fourtyTwo { + t.Fatalf("the pre-state balance before self-destruct must be 42") + } + + // The new balance must be 0. + if *balanceStateDiff.NewValue != zero { + t.Fatalf("the post-state balance after self-destruct must be 0") + } + } + { // Check self-destructed target in the witness. + selfDestructTargetTreeKey := utils.GetTreeKeyCodeKeccak(account2[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediff[1] { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructTargetTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + balanceStateDiff := statediff[1][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BalanceLeafKey { + t.Fatalf("balance invalid suffix") + } + if balanceStateDiff.CurrentValue == nil { + t.Fatalf("codeHash.CurrentValue must not be empty") + } + if balanceStateDiff.NewValue == nil { + t.Fatalf("codeHash.NewValue must not be empty") + } + preStateBalance := binary.LittleEndian.Uint64(balanceStateDiff.CurrentValue[:]) + postStateBalance := binary.LittleEndian.Uint64(balanceStateDiff.NewValue[:]) + if postStateBalance-preStateBalance != 42 { + t.Fatalf("the post-state balance after self-destruct must be 42") + } + } +} + +func TestProcessVerkleSelfDestructInSameTx(t *testing.T) { + var ( + config = ¶ms.ChainConfig{ + ChainID: big.NewInt(69421), + 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), + Ethash: new(params.EthashConfig), + ShanghaiTime: u64(0), + PragueTime: u64(0), + TerminalTotalDifficulty: common.Big0, + TerminalTotalDifficultyPassed: true, + ProofInBlocks: true, + } + signer = types.LatestSigner(config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain + gendb = rawdb.NewMemoryDatabase() // Database for the block-generation code, they must be separate as they are path-based. + coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7") + account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + gspec = &Genesis{ + Config: config, + Alloc: GenesisAlloc{ + coinbase: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account1: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account2: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 3, + }, + }, + } + ) + genesis := gspec.MustCommit(bcdb) + gspec.MustCommit(gendb) + + // The goal of this test is to test SELFDESTRUCT that happens in a contract execution which is created + // in **the same** transaction sending the remaining balance to an external (i.e: not itself) account. + + selfDestructContract := []byte{ + 0x73, // PUSH20 + 0x61, 0x77, 0x84, 0x3d, 0xb3, 0x13, 0x8a, 0xe6, 0x96, 0x79, 0xA5, 0x4b, 0x95, 0xcf, 0x34, 0x5E, 0xD7, 0x59, 0x45, 0x0d, // 0x6177843db3138ae69679A54b95cf345ED759450d + 0xFF, // SELFDESTRUCT + } + selfDestructContractAddr := common.HexToAddress("3a220f351252089d385b29beca14e27f204c296a") + _, _, _, statediff := GenerateVerkleChain(gspec.Config, genesis, beacon.New(ethash.NewFaker()), gendb, 1, func(i int, gen *BlockGen) { + gen.SetPoS() + tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(42), 100_000, big.NewInt(875000000), selfDestructContract), signer, testKey) + gen.AddTx(tx) + }) + + { // Check self-destructed contract in the witness + selfDestructContractTreeKey := utils.GetTreeKeyCodeKeccak(selfDestructContractAddr[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediff[0] { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + balanceStateDiff := statediff[0][stateDiffIdx].SuffixDiffs[1] + if balanceStateDiff.Suffix != utils.BalanceLeafKey { + t.Fatalf("balance invalid suffix") + } + + if balanceStateDiff.CurrentValue != nil { + t.Fatalf("the pre-state balance before must be nil, since the contract didn't exist") + } + + if balanceStateDiff.NewValue != nil { + t.Fatalf("the post-state balance after self-destruct must be nil since the contract shouldn't be created at all") + } + } + { // Check self-destructed target in the witness. + selfDestructTargetTreeKey := utils.GetTreeKeyCodeKeccak(account2[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediff[0] { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructTargetTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + balanceStateDiff := statediff[0][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BalanceLeafKey { + t.Fatalf("balance invalid suffix") + } + if balanceStateDiff.CurrentValue == nil { + t.Fatalf("codeHash.CurrentValue must not be empty") + } + if balanceStateDiff.NewValue == nil { + t.Fatalf("codeHash.NewValue must not be empty") + } + preStateBalance := binary.LittleEndian.Uint64(balanceStateDiff.CurrentValue[:]) + postStateBalance := binary.LittleEndian.Uint64(balanceStateDiff.NewValue[:]) + if postStateBalance-preStateBalance != 42 { + t.Fatalf("the post-state balance after self-destruct must be 42") + } + } +} + +func TestProcessVerkleSelfDestructInSeparateTxWithSelfBeneficiary(t *testing.T) { + var ( + config = ¶ms.ChainConfig{ + ChainID: big.NewInt(69421), + 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), + Ethash: new(params.EthashConfig), + ShanghaiTime: u64(0), + PragueTime: u64(0), + TerminalTotalDifficulty: common.Big0, + TerminalTotalDifficultyPassed: true, + ProofInBlocks: true, + } + signer = types.LatestSigner(config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain + gendb = rawdb.NewMemoryDatabase() // Database for the block-generation code, they must be separate as they are path-based. + coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7") + account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + gspec = &Genesis{ + Config: config, + Alloc: GenesisAlloc{ + coinbase: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account1: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account2: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 3, + }, + }, + } + ) + genesis := gspec.MustCommit(bcdb) + gspec.MustCommit(gendb) + + // The goal of this test is to test SELFDESTRUCT that happens in a contract execution which is created + // in a *previous* transaction sending the remaining balance to itself. + + selfDestructContract := []byte{ + 0x60, 22, // PUSH1 22 + 0x60, 12, // PUSH1 12 + 0x60, 0x00, // PUSH1 0 + 0x39, // CODECOPY + + 0x60, 22, // PUSH1 22 + 0x60, 0x00, // PUSH1 0 + 0xF3, // RETURN + + // Deployed code + 0x73, // PUSH20 + 0x3a, 0x22, 0x0f, 0x35, 0x12, 0x52, 0x08, 0x9d, 0x38, 0x5b, 0x29, 0xbe, 0xca, 0x14, 0xe2, 0x7f, 0x20, 0x4c, 0x29, 0x6a, // 0x3a220f351252089d385b29beca14e27f204c296a + 0xFF, // SELFDESTRUCT + } + selfDestructContractAddr := common.HexToAddress("3a220f351252089d385b29beca14e27f204c296a") + _, _, _, statediff := GenerateVerkleChain(gspec.Config, genesis, beacon.New(ethash.NewFaker()), gendb, 2, func(i int, gen *BlockGen) { + gen.SetPoS() + if i == 0 { + // Create selfdestruct contract, sending 42 wei. + tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(42), 100_000, big.NewInt(875000000), selfDestructContract), signer, testKey) + gen.AddTx(tx) + } else { + // Call it. + tx, _ := types.SignTx(types.NewTransaction(1, selfDestructContractAddr, big.NewInt(0), 100_000, big.NewInt(875000000), nil), signer, testKey) + gen.AddTx(tx) + } + }) + + { + // Check self-destructed contract in the witness. + // The way 6780 is implemented today, it always SubBalance from the self-destructed contract, and AddBalance + // to the beneficiary. In this case both addresses are the same, thus this might be optimizable from a gas + // perspective. But until that happens, we need to honor this "balance reading" adding it to the witness. + + selfDestructContractTreeKey := utils.GetTreeKeyCodeKeccak(selfDestructContractAddr[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediff[1] { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + balanceStateDiff := statediff[1][stateDiffIdx].SuffixDiffs[1] + if balanceStateDiff.Suffix != utils.BalanceLeafKey { + t.Fatalf("balance invalid suffix") + } + + // The original balance was 42. + var fourtyTwo [32]byte + fourtyTwo[0] = 42 + if *balanceStateDiff.CurrentValue != fourtyTwo { + t.Fatalf("the pre-state balance before self-destruct must be 42") + } + + // Note that the SubBalance+AddBalance net effect is a 0 change, so NewValue + // must be nil. + if balanceStateDiff.NewValue != nil { + t.Fatalf("the post-state balance after self-destruct must be empty") + } + } +} + +func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiary(t *testing.T) { + var ( + config = ¶ms.ChainConfig{ + ChainID: big.NewInt(69421), + 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), + Ethash: new(params.EthashConfig), + ShanghaiTime: u64(0), + PragueTime: u64(0), + TerminalTotalDifficulty: common.Big0, + TerminalTotalDifficultyPassed: true, + ProofInBlocks: true, + } + signer = types.LatestSigner(config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain + gendb = rawdb.NewMemoryDatabase() // Database for the block-generation code, they must be separate as they are path-based. + coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7") + account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + gspec = &Genesis{ + Config: config, + Alloc: GenesisAlloc{ + coinbase: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account1: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account2: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 3, + }, + }, + } + ) + genesis := gspec.MustCommit(bcdb) + gspec.MustCommit(gendb) + + // The goal of this test is to test SELFDESTRUCT that happens in a contract execution which is created + // in **the same** transaction sending the remaining balance to itself. + + selfDestructContract := []byte{ + 0x73, // PUSH20 + 0x3a, 0x22, 0x0f, 0x35, 0x12, 0x52, 0x08, 0x9d, 0x38, 0x5b, 0x29, 0xbe, 0xca, 0x14, 0xe2, 0x7f, 0x20, 0x4c, 0x29, 0x6a, // 0x3a220f351252089d385b29beca14e27f204c296a + 0xFF, // SELFDESTRUCT + } + selfDestructContractAddr := common.HexToAddress("3a220f351252089d385b29beca14e27f204c296a") + _, _, _, statediff := GenerateVerkleChain(gspec.Config, genesis, beacon.New(ethash.NewFaker()), gendb, 1, func(i int, gen *BlockGen) { + gen.SetPoS() + tx, _ := types.SignTx(types.NewContractCreation(0, big.NewInt(42), 100_000, big.NewInt(875000000), selfDestructContract), signer, testKey) + gen.AddTx(tx) + }) + + { // Check self-destructed contract in the witness + selfDestructContractTreeKey := utils.GetTreeKeyCodeKeccak(selfDestructContractAddr[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediff[0] { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + balanceStateDiff := statediff[0][stateDiffIdx].SuffixDiffs[1] + if balanceStateDiff.Suffix != utils.BalanceLeafKey { + t.Fatalf("balance invalid suffix") + } + + if balanceStateDiff.CurrentValue != nil { + t.Fatalf("the pre-state balance before must be nil, since the contract didn't exist") + } + + if balanceStateDiff.NewValue != nil { + t.Fatalf("the post-state balance after self-destruct must be nil since the contract shouldn't be created at all") + } + } +} diff --git a/core/state_transition.go b/core/state_transition.go index 14dd86e4987..969e7a75fb9 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -29,6 +29,8 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/trie/utils" + "github.com/holiman/uint256" ) // ExecutionResult includes all output after executing given evm @@ -403,7 +405,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } st.gasRemaining -= gas - if rules.IsVerkle { + if rules.IsPrague { targetAddr := msg.To originAddr := msg.From @@ -476,6 +478,15 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { fee := new(big.Int).SetUint64(st.gasUsed()) fee.Mul(fee, effectiveTip) st.state.AddBalance(st.evm.Context.Coinbase, fee) + + // add the coinbase to the witness iff the fee is greater than 0 + if rules.IsPrague && fee.Sign() != 0 { + st.evm.Accesses.TouchAddressOnWriteAndComputeGas(st.evm.Context.Coinbase[:], uint256.Int{}, utils.VersionLeafKey) + st.evm.Accesses.TouchAddressOnWriteAndComputeGas(st.evm.Context.Coinbase[:], uint256.Int{}, utils.BalanceLeafKey) + st.evm.Accesses.TouchAddressOnWriteAndComputeGas(st.evm.Context.Coinbase[:], uint256.Int{}, utils.NonceLeafKey) + st.evm.Accesses.TouchAddressOnWriteAndComputeGas(st.evm.Context.Coinbase[:], uint256.Int{}, utils.CodeKeccakLeafKey) + st.evm.Accesses.TouchAddressOnWriteAndComputeGas(st.evm.Context.Coinbase[:], uint256.Int{}, utils.CodeSizeLeafKey) + } } return &ExecutionResult{ diff --git a/core/types/block.go b/core/types/block.go index 4452f4bb21f..64c9fe80ff2 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-verkle" ) // A BlockNonce is a 64-bit hash which proves (combined with the @@ -58,6 +59,18 @@ func (n *BlockNonce) UnmarshalText(input []byte) error { return hexutil.UnmarshalFixedText("BlockNonce", input, n[:]) } +type ExecutionWitness struct { + StateDiff verkle.StateDiff `json:"stateDiff"` + VerkleProof *verkle.VerkleProof `json:"verkleProof"` +} + +func (ew *ExecutionWitness) Copy() *ExecutionWitness { + return &ExecutionWitness{ + StateDiff: ew.StateDiff.Copy(), + VerkleProof: ew.VerkleProof.Copy(), + } +} + //go:generate go run github.com/fjl/gencodec -type Header -field-override headerMarshaling -out gen_header_json.go //go:generate go run ../../rlp/rlpgen -type Header -out gen_header_rlp.go @@ -90,6 +103,8 @@ type Header struct { // ExcessBlobGas was added by EIP-4844 and is ignored in legacy headers. ExcessBlobGas *uint64 `json:"excessBlobGas" rlp:"optional"` + + ExecutionWitness *ExecutionWitness `json:"executionWitness" rlp:"-"` } // field type overrides for gencodec @@ -292,6 +307,9 @@ func CopyHeader(h *Header) *Header { cpy.BlobGasUsed = new(uint64) *cpy.BlobGasUsed = *h.BlobGasUsed } + if h.ExecutionWitness != nil { + cpy.ExecutionWitness = h.ExecutionWitness.Copy() + } return &cpy } @@ -380,6 +398,8 @@ func (b *Block) BlobGasUsed() *uint64 { func (b *Block) Header() *Header { return CopyHeader(b.header) } +func (b *Block) ExecutionWitness() *ExecutionWitness { return b.header.ExecutionWitness } + // Body returns the non-header content of the block. func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles, b.withdrawals} } @@ -401,6 +421,18 @@ func (b *Block) SanityCheck() error { return b.header.SanityCheck() } +func (b *Block) SetVerkleProof(vp *verkle.VerkleProof, statediff verkle.StateDiff) { + b.header.ExecutionWitness = &ExecutionWitness{statediff, vp} + if statediff == nil { + b.header.ExecutionWitness.StateDiff = []verkle.StemStateDiff{} + } + if vp == nil { + b.header.ExecutionWitness.VerkleProof = &verkle.VerkleProof{ + IPAProof: &verkle.IPAProof{}, + } + } +} + type writeCounter uint64 func (c *writeCounter) Write(b []byte) (int, error) { diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 2942755f3fa..036d18a078f 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -150,7 +150,7 @@ func init() { // ActivePrecompiles returns the precompiles enabled with the current configuration. func ActivePrecompiles(rules params.Rules) []common.Address { switch { - case rules.IsVerkle: + case rules.IsPrague: return PrecompiledAddressesBerlin case rules.IsCancun: return PrecompiledAddressesCancun diff --git a/core/vm/evm.go b/core/vm/evm.go index 73c3c215061..782fc6d5674 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -41,7 +41,7 @@ type ( func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { var precompiles map[common.Address]PrecompiledContract switch { - case evm.chainRules.IsVerkle: + case evm.chainRules.IsPrague: precompiles = PrecompiledContractsBerlin case evm.chainRules.IsCancun: precompiles = PrecompiledContractsCancun @@ -137,8 +137,8 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig chainConfig: chainConfig, chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time), } - if txCtx.Accesses == nil && chainConfig.IsVerkle(blockCtx.BlockNumber, blockCtx.Time) { - txCtx.Accesses = evm.StateDB.(*state.StateDB).NewAccessWitness() + if txCtx.Accesses == nil && chainConfig.IsPrague(blockCtx.BlockNumber, blockCtx.Time) { + evm.Accesses = evm.StateDB.(*state.StateDB).NewAccessWitness() } evm.interpreter = NewEVMInterpreter(evm) return evm @@ -147,7 +147,7 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig // Reset resets the EVM with a new transaction context.Reset // This is not threadsafe and should only be done very cautiously. func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) { - if txCtx.Accesses == nil && evm.chainRules.IsVerkle { + if txCtx.Accesses == nil && evm.chainRules.IsPrague { txCtx.Accesses = evm.StateDB.(*state.StateDB).NewAccessWitness() } evm.TxContext = txCtx @@ -212,9 +212,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas var creation bool if !evm.StateDB.Exist(addr) { if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 { - if evm.chainRules.IsVerkle { + if evm.chainRules.IsPrague { // proof of absence - tryConsumeGas(&gas, evm.Accesses.TouchAndChargeProofOfAbsence(caller.Address().Bytes())) + tryConsumeGas(&gas, evm.Accesses.TouchAndChargeProofOfAbsence(addr.Bytes())) } // Calling a non existing account, don't do anything, but ping the tracer if debug { @@ -519,6 +519,15 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, } } + if err == nil && evm.chainRules.IsPrague { + if len(ret) > 0 { + touchCodeChunksRangeOnReadAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), evm.Accesses) + } + if !contract.UseGas(evm.Accesses.TouchAndChargeContractCreateCompleted(address.Bytes()[:])) { + err = ErrOutOfGas + } + } + // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in homestead this also counts for code storage gas errors. @@ -529,13 +538,6 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, } } - if err == nil && evm.chainRules.IsVerkle { - if !contract.UseGas(evm.Accesses.TouchAndChargeContractCreateCompleted(address.Bytes()[:])) { - evm.StateDB.RevertToSnapshot(snapshot) - err = ErrOutOfGas - } - } - if evm.Config.Tracer != nil { if evm.depth == 0 { evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, err) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index dc307b82904..f367c3c9297 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -101,7 +101,7 @@ var ( func gasExtCodeSize(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { usedGas := uint64(0) slot := stack.Back(0) - if evm.chainRules.IsVerkle { + if evm.chainRules.IsPrague { usedGas += evm.TxContext.Accesses.TouchAddressOnReadAndComputeGas(slot.Bytes(), uint256.Int{}, trieUtils.CodeSizeLeafKey) } @@ -111,7 +111,7 @@ func gasExtCodeSize(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem func gasSLoad(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { usedGas := uint64(0) - if evm.chainRules.IsVerkle { + if evm.chainRules.IsPrague { where := stack.Back(0) treeIndex, subIndex := trieUtils.GetTreeKeyStorageSlotTreeIndexes(where.Bytes()) usedGas += evm.Accesses.TouchAddressOnReadAndComputeGas(contract.Address().Bytes(), *treeIndex, subIndex) @@ -423,7 +423,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, ErrGasUintOverflow } - if evm.chainRules.IsVerkle { + if evm.chainRules.IsPrague { if _, isPrecompile := evm.precompile(address); !isPrecompile { gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes()[:])) if overflow { @@ -463,7 +463,7 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, ErrGasUintOverflow } - if evm.chainRules.IsVerkle { + if evm.chainRules.IsPrague { address := common.Address(stack.Back(1).Bytes20()) if _, isPrecompile := evm.precompile(address); !isPrecompile { gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes())) @@ -488,7 +488,7 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, ErrGasUintOverflow } - if evm.chainRules.IsVerkle { + if evm.chainRules.IsPrague { address := common.Address(stack.Back(1).Bytes20()) if _, isPrecompile := evm.precompile(address); !isPrecompile { gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes())) @@ -513,7 +513,7 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, ErrGasUintOverflow } - if evm.chainRules.IsVerkle { + if evm.chainRules.IsPrague { address := common.Address(stack.Back(1).Bytes20()) if _, isPrecompile := evm.precompile(address); !isPrecompile { gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes())) @@ -542,7 +542,7 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me } } - if evm.chainRules.IsVerkle { + if evm.chainRules.IsPrague { // TODO turn this into a panic (when we are sure this method // will never execute when verkle is enabled) log.Warn("verkle witness accumulation not supported for selfdestruct") diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 13252eda9df..095846feff1 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -17,13 +17,15 @@ package vm import ( + "encoding/binary" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" - trieUtils "github.com/ethereum/go-ethereum/trie/utils" + "github.com/ethereum/go-ethereum/trie/utils" "github.com/holiman/uint256" ) @@ -262,6 +264,13 @@ func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { slot := scope.Stack.peek() address := common.Address(slot.Bytes20()) + if interpreter.evm.chainRules.IsPrague { + statelessGas := interpreter.evm.Accesses.TouchAddressOnReadAndComputeGas(slot.Bytes(), uint256.Int{}, utils.BalanceLeafKey) + if !scope.Contract.UseGas(statelessGas) { + scope.Contract.Gas = 0 + return nil, ErrOutOfGas + } + } slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address)) return nil, nil } @@ -345,9 +354,12 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { slot := scope.Stack.peek() cs := uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20())) - if interpreter.evm.chainRules.IsVerkle { - statelessGas := interpreter.evm.Accesses.TouchAddressOnReadAndComputeGas(slot.Bytes(), uint256.Int{}, trieUtils.CodeSizeLeafKey) - scope.Contract.UseGas(statelessGas) + if interpreter.evm.chainRules.IsPrague { + statelessGas := interpreter.evm.Accesses.TouchAddressOnReadAndComputeGas(slot.Bytes(), uint256.Int{}, utils.CodeSizeLeafKey) + if !scope.Contract.UseGas(statelessGas) { + scope.Contract.Gas = 0 + return nil, ErrOutOfGas + } } slot.SetUint64(cs) return nil, nil @@ -373,8 +385,12 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ contractAddr := scope.Contract.Address() paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(scope.Contract.Code, uint64CodeOffset, length.Uint64()) - if interpreter.evm.chainRules.IsVerkle { - scope.Contract.UseGas(touchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], copyOffset, nonPaddedCopyLength, uint64(len(scope.Contract.Code)), interpreter.evm.Accesses)) + if interpreter.evm.chainRules.IsPrague && !scope.Contract.IsDeployment { + statelessGas := touchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], copyOffset, nonPaddedCopyLength, uint64(len(scope.Contract.Code)), interpreter.evm.Accesses) + if !scope.Contract.UseGas(statelessGas) { + scope.Contract.Gas = 0 + return nil, ErrOutOfGas + } } scope.Memory.Set(memOffset.Uint64(), uint64(len(paddedCodeCopy)), paddedCodeCopy) return nil, nil @@ -392,11 +408,13 @@ func touchCodeChunksRangeOnReadAndChargeGas(contractAddr []byte, startPC, size u return 0 } - // endPC is the last PC that must be touched. - endPC := startPC + size - 1 - if startPC+size > codeLen { + endPC := startPC + size + if endPC > codeLen { endPC = codeLen } + if endPC > 0 { + endPC -= 1 // endPC is the last bytecode that will be touched. + } var statelessGasCharged uint64 for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ { @@ -426,15 +444,18 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) uint64CodeOffset = 0xffffffffffffffff } addr := common.Address(a.Bytes20()) - if interpreter.evm.chainRules.IsVerkle { + if interpreter.evm.chainRules.IsPrague { code := interpreter.evm.StateDB.GetCode(addr) contract := &Contract{ Code: code, self: AccountRef(addr), } paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64()) - gas := touchCodeChunksRangeOnReadAndChargeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), interpreter.evm.Accesses) - scope.Contract.UseGas(gas) + statelessGas := touchCodeChunksRangeOnReadAndChargeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), interpreter.evm.Accesses) + if !scope.Contract.UseGas(statelessGas) { + scope.Contract.Gas = 0 + return nil, ErrOutOfGas + } scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy) } else { codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64()) @@ -473,6 +494,13 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { slot := scope.Stack.peek() address := common.Address(slot.Bytes20()) + if interpreter.evm.chainRules.IsPrague { + statelessGas := interpreter.evm.Accesses.TouchAddressOnReadAndComputeGas(slot.Bytes(), uint256.Int{}, utils.CodeKeccakLeafKey) + if !scope.Contract.UseGas(statelessGas) { + scope.Contract.Gas = 0 + return nil, ErrOutOfGas + } + } if interpreter.evm.StateDB.Empty(address) { slot.Clear() } else { @@ -487,6 +515,14 @@ func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ return nil, nil } +func getBlockHashFromContract(number uint64, statedb StateDB, witness *state.AccessWitness) common.Hash { + var pnum common.Hash + binary.BigEndian.PutUint64(pnum[24:], number) + treeIndex, suffix := utils.GetTreeKeyStorageSlotTreeIndexes(pnum.Bytes()) + witness.TouchAddressOnReadAndComputeGas(params.HistoryStorageAddress[:], *treeIndex, suffix) + return statedb.GetState(params.HistoryStorageAddress, pnum) +} + func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { num := scope.Stack.peek() num64, overflow := num.Uint64WithOverflow() @@ -494,6 +530,14 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( num.Clear() return nil, nil } + + evm := interpreter.evm + // if Prague is active, read it from the history contract (EIP 2935). + if evm.chainRules.IsPrague { + num.SetBytes(getBlockHashFromContract(num64, evm.StateDB, evm.Accesses).Bytes()) + return nil, nil + } + var upper, lower uint64 upper = interpreter.evm.Context.BlockNumber.Uint64() if upper < 257 { @@ -641,7 +685,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) gas = scope.Contract.Gas ) - if interpreter.evm.chainRules.IsVerkle { + if interpreter.evm.chainRules.IsPrague { contractAddress := crypto.CreateAddress(scope.Contract.Address(), interpreter.evm.StateDB.GetNonce(scope.Contract.Address())) statelessGas := interpreter.evm.Accesses.TouchAndChargeContractCreateInit(contractAddress.Bytes()[:], value.Sign() != 0) if !tryConsumeGas(&gas, statelessGas) { @@ -695,7 +739,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) gas = scope.Contract.Gas ) - if interpreter.evm.chainRules.IsVerkle { + if interpreter.evm.chainRules.IsPrague { codeAndHash := &codeAndHash{code: input} contractAddress := crypto.CreateAddress2(scope.Contract.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes()) statelessGas := interpreter.evm.Accesses.TouchAndChargeContractCreateInit(contractAddress.Bytes()[:], endowment.Sign() != 0) @@ -900,6 +944,22 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) tracer.CaptureExit([]byte{}, 0, nil) } + if interpreter.evm.chainRules.IsPrague { + contractAddr := scope.Contract.Address() + beneficiaryAddr := beneficiary.Bytes20() + // If the beneficiary isn't the contract, we need to touch the beneficiary's balance. + // If the beneficiary is the contract itself, there're two possibilities: + // 1. The contract was created in the same transaction: the balance is already touched (no need to touch again) + // 2. The contract wasn't created in the same transaction: there's no net change in balance, + // and SELFDESTRUCT will perform no action on the account header. (we touch since we did SubBalance+AddBalance above) + if contractAddr != beneficiaryAddr || interpreter.evm.StateDB.WasCreatedInCurrentTx(contractAddr) { + statelessGas := interpreter.evm.Accesses.TouchAddressOnReadAndComputeGas(beneficiaryAddr[:], uint256.Int{}, utils.BalanceLeafKey) + if !scope.Contract.UseGas(statelessGas) { + scope.Contract.Gas = 0 + return nil, ErrOutOfGas + } + } + } return nil, errStopToken } @@ -959,12 +1019,15 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by if *pc < codeLen { scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc]))) - if interpreter.evm.chainRules.IsVerkle && *pc%31 == 0 { + if !scope.Contract.IsDeployment && interpreter.evm.chainRules.IsPrague && *pc%31 == 0 { // touch next chunk if PUSH1 is at the boundary. if so, *pc has // advanced past this boundary. contractAddr := scope.Contract.Address() statelessGas := touchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], *pc+1, uint64(1), uint64(len(scope.Contract.Code)), interpreter.evm.Accesses) - scope.Contract.UseGas(statelessGas) + if !scope.Contract.UseGas(statelessGas) { + scope.Contract.Gas = 0 + return nil, ErrOutOfGas + } } } else { scope.Stack.push(integer.Clear()) @@ -987,10 +1050,13 @@ func makePush(size uint64, pushByteSize int) executionFunc { endMin = startMin + pushByteSize } - if interpreter.evm.chainRules.IsVerkle { + if !scope.Contract.IsDeployment && interpreter.evm.chainRules.IsPrague { contractAddr := scope.Contract.Address() statelessGas := touchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], uint64(startMin), uint64(pushByteSize), uint64(len(scope.Contract.Code)), interpreter.evm.Accesses) - scope.Contract.UseGas(statelessGas) + if !scope.Contract.UseGas(statelessGas) { + scope.Contract.Gas = 0 + return nil, ErrOutOfGas + } } integer := new(uint256.Int) diff --git a/core/vm/interface.go b/core/vm/interface.go index 0a02a0181c0..4241b1d45a7 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -57,6 +57,8 @@ type StateDB interface { Selfdestruct6780(common.Address) + WasCreatedInCurrentTx(common.Address) bool + // Exist reports whether the given account exists in state. // Notably this should also return true for self-destructed accounts. Exist(common.Address) bool diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index ad4222b447b..17b30fae120 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -56,9 +56,9 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { // If jump table was not initialised we set the default one. var table *JumpTable switch { - case evm.chainRules.IsVerkle: + case evm.chainRules.IsPrague: // TODO replace with prooper instruction set when fork is specified - table = &shanghaiInstructionSet + table = &pragueInstructionSet case evm.chainRules.IsCancun: table = &cancunInstructionSet case evm.chainRules.IsShanghai: @@ -179,7 +179,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( logged, pcCopy, gasCopy = false, pc, contract.Gas } - if in.evm.chainRules.IsCancun && !contract.IsDeployment { + if in.evm.chainRules.IsPrague && !contract.IsDeployment { // if the PC ends up in a new "chunk" of verkleized code, charge the // associated costs. contractAddr := contract.Address() diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 0a881236f64..5dcabe387d6 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -57,6 +57,7 @@ var ( mergeInstructionSet = newMergeInstructionSet() shanghaiInstructionSet = newShanghaiInstructionSet() cancunInstructionSet = newCancunInstructionSet() + pragueInstructionSet = newPragueInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. @@ -80,6 +81,12 @@ func validate(jt JumpTable) JumpTable { return jt } +func newPragueInstructionSet() JumpTable { + instructionSet := newShanghaiInstructionSet() + enable6780(&instructionSet) + return validate(instructionSet) +} + func newCancunInstructionSet() JumpTable { instructionSet := newShanghaiInstructionSet() enable4844(&instructionSet) // EIP-4844 (DATAHASH opcode) diff --git a/core/vm/jump_table_export.go b/core/vm/jump_table_export.go index 75bcb8d5bf9..2ceb75e7327 100644 --- a/core/vm/jump_table_export.go +++ b/core/vm/jump_table_export.go @@ -26,11 +26,8 @@ import ( // the rules. func LookupInstructionSet(rules params.Rules) (JumpTable, error) { switch { - case rules.IsVerkle: - // TODO set to newCancunInstructionSet() when verkle-fork is defined - return newShanghaiInstructionSet(), errors.New("verkle-fork not defined yet") case rules.IsPrague: - return newCancunInstructionSet(), errors.New("prague-fork not defined yet") + return newShanghaiInstructionSet(), errors.New("prague-fork not defined yet") case rules.IsCancun: return newCancunInstructionSet(), nil case rules.IsShanghai: diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 7d2296c4dc0..4d4fe8aed3e 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -52,7 +52,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc { } value := common.Hash(y.Bytes32()) - if evm.chainRules.IsVerkle { + if evm.chainRules.IsPrague { treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(x.Bytes()) cost += evm.Accesses.TouchAddressOnWriteAndComputeGas(contract.Address().Bytes(), *treeIndex, subIndex) } @@ -111,7 +111,7 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me slot := common.Hash(loc.Bytes32()) var gasUsed uint64 - if evm.chainRules.IsVerkle { + if evm.chainRules.IsPrague { where := stack.Back(0) treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(where.Bytes()) addr := contract.Address() diff --git a/eth/backend.go b/eth/backend.go index 667200bcedd..a6c80159077 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -198,8 +198,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if config.OverrideCancun != nil { overrides.OverrideCancun = config.OverrideCancun } - if config.OverrideVerkle != nil { - overrides.OverrideVerkle = config.OverrideVerkle + if config.OverridePrague != nil { + overrides.OverridePrague = config.OverridePrague } eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, config.Genesis, &overrides, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit) if err != nil { diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 1a221941427..63079415fc1 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -528,6 +528,17 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe log.Warn("Invalid timestamp", "parent", block.Time(), "block", block.Time()) return api.invalid(errors.New("invalid timestamp"), parent.Header()), nil } + // Trigger the start of the verkle conversion if we're at the right block + if api.eth.BlockChain().Config().IsPrague(block.Number(), block.Time()) && !api.eth.BlockChain().Config().IsPrague(parent.Number(), parent.Time()) { + parent := api.eth.BlockChain().GetHeaderByNumber(block.NumberU64() - 1) + if !api.eth.BlockChain().Config().IsPrague(parent.Number, parent.Time) { + api.eth.BlockChain().StartVerkleTransition(parent.Root, common.Hash{}, api.eth.BlockChain().Config(), nil) + } + } + // Reset db merge state in case of a reorg + if !api.eth.BlockChain().Config().IsPrague(block.Number(), block.Time()) { + api.eth.BlockChain().ReorgThroughVerkleTransition() + } // Another cornercase: if the node is in snap sync mode, but the CL client // tries to make it import a block. That should be denied as pushing something // into the database directly will conflict with the assumptions of snap sync diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 4bc8b8dc6c6..4606b60408d 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -157,7 +157,7 @@ type Config struct { OverrideCancun *uint64 `toml:",omitempty"` // OverrideVerkle (TODO: remove after the fork) - OverrideVerkle *uint64 `toml:",omitempty"` + OverridePrague *uint64 `toml:",omitempty"` } // CreateConsensusEngine creates a consensus engine for the given chain config. diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index 324fbe380ea..2ad499a5348 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -52,7 +52,7 @@ func (c Config) MarshalTOML() (interface{}, error) { RPCEVMTimeout time.Duration RPCTxFeeCap float64 OverrideCancun *uint64 `toml:",omitempty"` - OverrideVerkle *uint64 `toml:",omitempty"` + OverridePrague *uint64 `toml:",omitempty"` } var enc Config enc.Genesis = c.Genesis @@ -90,7 +90,7 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.RPCEVMTimeout = c.RPCEVMTimeout enc.RPCTxFeeCap = c.RPCTxFeeCap enc.OverrideCancun = c.OverrideCancun - enc.OverrideVerkle = c.OverrideVerkle + enc.OverridePrague = c.OverridePrague return &enc, nil } @@ -132,7 +132,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { RPCEVMTimeout *time.Duration RPCTxFeeCap *float64 OverrideCancun *uint64 `toml:",omitempty"` - OverrideVerkle *uint64 `toml:",omitempty"` + OverridePrague *uint64 `toml:",omitempty"` } var dec Config if err := unmarshal(&dec); err != nil { @@ -243,8 +243,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.OverrideCancun != nil { c.OverrideCancun = dec.OverrideCancun } - if dec.OverrideVerkle != nil { - c.OverrideVerkle = dec.OverrideVerkle + if dec.OverridePrague != nil { + c.OverridePrague = dec.OverridePrague } return nil } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 740a38ab9fb..5e90180df8d 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -1020,10 +1020,6 @@ func overrideConfig(original *params.ChainConfig, override *params.ChainConfig) copy.PragueTime = timestamp canon = false } - if timestamp := override.VerkleTime; timestamp != nil { - copy.VerkleTime = timestamp - canon = false - } return copy, canon } diff --git a/go.mod b/go.mod index c133d4bd5ba..81367549719 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,8 @@ require ( github.com/cespare/cp v0.1.0 github.com/cloudflare/cloudflare-go v0.14.0 github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 - github.com/consensys/gnark-crypto v0.10.0 + github.com/consensys/gnark-crypto v0.12.1 + github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 github.com/crate-crypto/go-kzg-4844 v0.3.0 github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set/v2 v2.1.0 @@ -25,7 +26,7 @@ require ( github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 github.com/fsnotify/fsnotify v1.6.0 github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff - github.com/gballet/go-verkle v0.0.0-20230725193842-b2d852dc666b + github.com/gballet/go-verkle v0.1.1-0.20231125115329-d193f0b46e01 github.com/go-stack/stack v1.8.1 github.com/gofrs/flock v0.8.1 github.com/golang-jwt/jwt/v4 v4.3.0 @@ -56,17 +57,17 @@ require ( github.com/rs/cors v1.7.0 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible github.com/status-im/keycard-go v0.2.0 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.2 github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/tyler-smith/go-bip39 v1.1.0 github.com/urfave/cli/v2 v2.24.1 go.uber.org/automaxprocs v1.5.2 - golang.org/x/crypto v0.9.0 + golang.org/x/crypto v0.10.0 golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc - golang.org/x/sync v0.3.0 - golang.org/x/sys v0.10.0 - golang.org/x/text v0.9.0 + golang.org/x/sync v0.4.0 + golang.org/x/sys v0.13.0 + golang.org/x/text v0.10.0 golang.org/x/time v0.3.0 golang.org/x/tools v0.9.1 gopkg.in/natefinch/lumberjack.v2 v2.0.0 @@ -85,17 +86,17 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.1.1 // indirect github.com/aws/smithy-go v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.7.0 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cockroachdb/errors v1.9.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.3 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/crate-crypto/go-ipa v0.0.0-20230710183535-d5eb1c4661bd // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/deepmap/oapi-codegen v1.8.2 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect + github.com/ethereum/go-verkle v0.1.1-0.20240119133216-f8289fc59149 // indirect github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect github.com/getsentry/sentry-go v0.18.0 // indirect github.com/go-ole/go-ole v1.2.1 // indirect diff --git a/go.sum b/go.sum index 47603d0dbc1..bf0e786ed54 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= -github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= @@ -75,8 +75,8 @@ github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZ github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark-crypto v0.10.0 h1:zRh22SR7o4K35SoNqouS9J/TKHTyU2QWaj5ldehyXtA= -github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -84,10 +84,8 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80 h1:DuBDHVjgGMPki7bAyh91+3cF1Vh34sAEdH8JQgbc2R0= -github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80/go.mod h1:gzbVz57IDJgQ9rLQwfSk696JGWof8ftznEL9GoAv3NI= -github.com/crate-crypto/go-ipa v0.0.0-20230710183535-d5eb1c4661bd h1:jgf65Q4+jHFuLlhVApaVfTUwcU7dAdXK+GESow2UlaI= -github.com/crate-crypto/go-ipa v0.0.0-20230710183535-d5eb1c4661bd/go.mod h1:gzbVz57IDJgQ9rLQwfSk696JGWof8ftznEL9GoAv3NI= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= github.com/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A= github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -129,6 +127,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/ethereum/c-kzg-4844 v0.3.0 h1:3Y3hD6l5i0dEYsBL50C+Om644kve3pNqoAcvE26o9zI= github.com/ethereum/c-kzg-4844 v0.3.0/go.mod h1:WI2Nd82DMZAAZI1wV2neKGost9EKjvbpQR9OqE5Qqa8= +github.com/ethereum/go-verkle v0.1.1-0.20240119133216-f8289fc59149 h1:7gbu2YdLL8SicVklig4nyizkWkw367BP+5eEivNPy04= +github.com/ethereum/go-verkle v0.1.1-0.20240119133216-f8289fc59149/go.mod h1:cZmLDzTyZPwUygE2ksQEcxOLZ8YpfRghnVtfxRnhgJM= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -146,10 +146,8 @@ github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILD github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/gballet/go-verkle v0.0.0-20230607174250-df487255f46b h1:vMT47RYsrftsHSTQhqXwC3BYflo38OLC3Y4LtXtLyU0= -github.com/gballet/go-verkle v0.0.0-20230607174250-df487255f46b/go.mod h1:CDncRYVRSDqwakm282WEkjfaAj1hxU/v5RXxk5nXOiI= -github.com/gballet/go-verkle v0.0.0-20230725193842-b2d852dc666b h1:2lDzSxjCii8FxrbuxtlFtFiw6c4nTPl9mhaZ6lgpwws= -github.com/gballet/go-verkle v0.0.0-20230725193842-b2d852dc666b/go.mod h1:+k9fzNguudDonU5q4/TUaTdmiHw3h3oGOIVmqyhaA3E= +github.com/gballet/go-verkle v0.1.1-0.20231125115329-d193f0b46e01 h1:Jm7DG6/BptrrNgOh9Jb6LPBbz75VJA5FkFKB4O/zbQw= +github.com/gballet/go-verkle v0.1.1-0.20231125115329-d193f0b46e01/go.mod h1:OzHSBt37xRRHc27lb9PaCldBnJYQZP8KcMdYyOB2dtU= github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= @@ -306,7 +304,6 @@ github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4F github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= -github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -428,8 +425,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b h1:u49mjRnygnB34h8OKbnNJFVUtWSKIKb1KukdV8bILUM= github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= @@ -480,8 +477,8 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= @@ -534,9 +531,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -569,17 +565,14 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -591,8 +584,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 3faf1d8bea7..1e2e2e13d4f 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1051,6 +1051,11 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S } evm, vmError := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true}, &blockCtx) + // Set witness if trie is verkle + if state.Database().TrieDB().IsVerkle() { + state.SetWitness(state.NewAccessWitness()) + } + // Wait for the context to be done and cancel the evm. Even if the // EVM has finished, cancelling may be done (repeatedly) go func() { diff --git a/les/client.go b/les/client.go index 132c857aa52..691635be0c5 100644 --- a/les/client.go +++ b/les/client.go @@ -95,8 +95,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) { if config.OverrideCancun != nil { overrides.OverrideCancun = config.OverrideCancun } - if config.OverrideVerkle != nil { - overrides.OverrideVerkle = config.OverrideVerkle + if config.OverridePrague != nil { + overrides.OverridePrague = config.OverridePrague } chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, trie.NewDatabase(chainDb), config.Genesis, &overrides) if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat { diff --git a/light/trie.go b/light/trie.go index e0a283fdc1d..53d54615d90 100644 --- a/light/trie.go +++ b/light/trie.go @@ -105,6 +105,10 @@ func (db *odrDatabase) StartVerkleTransition(originalRoot common.Hash, translate panic("not implemented") // TODO: Implement } +func (db *odrDatabase) ReorgThroughVerkleTransition() { + panic("not implemented") // TODO: Implement +} + func (db *odrDatabase) EndVerkleTransition() { panic("not implemented") // TODO: Implement } diff --git a/miner/worker.go b/miner/worker.go index 81aeb1d8138..aae4fe8b645 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -697,13 +697,7 @@ func (w *worker) resultLoop() { } // makeEnv creates a new environment for the sealing block. -func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase common.Address) (*environment, error) { - // Retrieve the parent state to execute on top and start a prefetcher for - // the miner to speed block sealing up a bit. - state, err := w.chain.StateAt(parent.Root) - if err != nil { - return nil, err - } +func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase common.Address, state *state.StateDB) (*environment, error) { state.StartPrefetcher("miner") // Note the passed coinbase may be different with header.Coinbase. @@ -895,6 +889,24 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { header.GasLimit = core.CalcGasLimit(parentGasLimit, w.config.GasCeil) } } + + // Trigger the start of the verkle conversion if we're at the right block + if w.chain.Config().IsPrague(header.Number, header.Time) { + parent := w.chain.GetHeaderByNumber(header.Number.Uint64() - 1) + if !w.chain.Config().IsPrague(parent.Number, parent.Time) { + w.chain.StartVerkleTransition(parent.Root, common.Hash{}, w.chain.Config(), nil) + } + } + + // Retrieve the parent state to execute on top and start a prefetcher for + // the miner to speed block sealing up a bit. + state, err := w.chain.StateAt(parent.Root) + if err != nil { + return nil, err + } + if w.chain.Config().IsPrague(header.Number, header.Time) { + core.OverlayVerkleTransition(state) + } // Run the consensus preparation with the default or customized consensus engine. if err := w.engine.Prepare(w.chain, header); err != nil { log.Error("Failed to prepare header for sealing", "err", err) @@ -903,11 +915,19 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { // Could potentially happen if starting to mine in an odd state. // Note genParams.coinbase can be different with header.Coinbase // since clique algorithm can modify the coinbase field in header. - env, err := w.makeEnv(parent, header, genParams.coinbase) + env, err := w.makeEnv(parent, header, genParams.coinbase, state) if err != nil { log.Error("Failed to create sealing context", "err", err) return nil, err } + if w.chainConfig.IsPrague(header.Number, header.Time) { + parent := w.chain.GetHeaderByNumber(header.Number.Uint64() - 1) + if !w.chain.Config().IsPrague(parent.Number, parent.Time) { + core.InsertBlockHashHistoryAtEip2935Fork(env.state, header.Number.Uint64()-1, header.ParentHash, w.chain) + } else { + core.ProcessParentBlockHash(env.state, header.Number.Uint64()-1, header.ParentHash) + } + } return env, nil } diff --git a/params/config.go b/params/config.go index 75c8fd89d09..19e633a71de 100644 --- a/params/config.go +++ b/params/config.go @@ -128,7 +128,6 @@ var ( ShanghaiTime: nil, CancunTime: nil, PragueTime: nil, - VerkleTime: nil, TerminalTotalDifficulty: nil, TerminalTotalDifficultyPassed: true, Ethash: new(EthashConfig), @@ -179,7 +178,6 @@ var ( ShanghaiTime: nil, CancunTime: nil, PragueTime: nil, - VerkleTime: nil, TerminalTotalDifficulty: nil, TerminalTotalDifficultyPassed: false, Ethash: nil, @@ -209,7 +207,6 @@ var ( ShanghaiTime: nil, CancunTime: nil, PragueTime: nil, - VerkleTime: nil, TerminalTotalDifficulty: nil, TerminalTotalDifficultyPassed: false, Ethash: new(EthashConfig), @@ -239,7 +236,6 @@ var ( ShanghaiTime: nil, CancunTime: nil, PragueTime: nil, - VerkleTime: nil, TerminalTotalDifficulty: nil, TerminalTotalDifficultyPassed: false, Ethash: new(EthashConfig), @@ -289,7 +285,6 @@ type ChainConfig struct { ShanghaiTime *uint64 `json:"shanghaiTime,omitempty"` // Shanghai switch time (nil = no fork, 0 = already on shanghai) CancunTime *uint64 `json:"cancunTime,omitempty"` // Cancun switch time (nil = no fork, 0 = already on cancun) PragueTime *uint64 `json:"pragueTime,omitempty"` // Prague switch time (nil = no fork, 0 = already on prague) - VerkleTime *uint64 `json:"verkleTime,omitempty"` // Verkle switch time (nil = no fork, 0 = already on verkle) // TerminalTotalDifficulty is the amount of total difficulty reached by // the network that triggers the consensus upgrade. @@ -304,6 +299,9 @@ type ChainConfig struct { Ethash *EthashConfig `json:"ethash,omitempty"` Clique *CliqueConfig `json:"clique,omitempty"` IsDevMode bool `json:"isDev,omitempty"` + + // Proof in block + ProofInBlocks bool `json:"proofInBlocks,omitempty"` } // EthashConfig is the consensus engine configs for proof-of-work based sealing. @@ -411,9 +409,6 @@ func (c *ChainConfig) Description() string { if c.PragueTime != nil { banner += fmt.Sprintf(" - Prague: @%-10v\n", *c.PragueTime) } - if c.VerkleTime != nil { - banner += fmt.Sprintf(" - Verkle: @%-10v\n", *c.VerkleTime) - } return banner } @@ -512,11 +507,6 @@ func (c *ChainConfig) IsPrague(num *big.Int, time uint64) bool { return c.IsLondon(num) && isTimestampForked(c.PragueTime, time) } -// IsVerkle returns whether num is either equal to the Verkle fork time or greater. -func (c *ChainConfig) IsVerkle(num *big.Int, time uint64) bool { - return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time) -} - // CheckCompatible checks whether scheduled fork transitions have been imported // with a mismatching chain configuration. func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64, time uint64) *ConfigCompatError { @@ -571,7 +561,6 @@ func (c *ChainConfig) CheckConfigForkOrder() error { {name: "shanghaiTime", timestamp: c.ShanghaiTime}, {name: "cancunTime", timestamp: c.CancunTime, optional: true}, {name: "pragueTime", timestamp: c.PragueTime, optional: true}, - {name: "verkleTime", timestamp: c.VerkleTime, optional: true}, } { if lastFork.name != "" { switch { @@ -675,9 +664,6 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, headNumber *big.Int, if isForkTimestampIncompatible(c.PragueTime, newcfg.PragueTime, headTimestamp) { return newTimestampCompatError("Prague fork timestamp", c.PragueTime, newcfg.PragueTime) } - if isForkTimestampIncompatible(c.VerkleTime, newcfg.VerkleTime, headTimestamp) { - return newTimestampCompatError("Verkle fork timestamp", c.VerkleTime, newcfg.VerkleTime) - } return nil } @@ -823,7 +809,6 @@ type Rules struct { IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsBerlin, IsLondon bool IsMerge, IsShanghai, IsCancun, IsPrague bool - IsVerkle bool } // Rules ensures c's ChainID is not nil. @@ -848,6 +833,5 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules IsShanghai: c.IsShanghai(num, timestamp), IsCancun: c.IsCancun(num, timestamp), IsPrague: c.IsPrague(num, timestamp), - IsVerkle: c.IsVerkle(num, timestamp), } } diff --git a/params/protocol_params.go b/params/protocol_params.go index a407ed14732..6d91ee48a85 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -16,7 +16,11 @@ package params -import "math/big" +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) const ( GasLimitBoundDivisor uint64 = 1024 // The bound divisor of the gas limit, used in update calculations. @@ -179,4 +183,7 @@ var ( GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block. MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be. DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not. + + // HistoryStorageAddress is where the historical block hashes are stored. + HistoryStorageAddress common.Address = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe") ) diff --git a/trie/transition.go b/trie/transition.go index ad5a7dc7015..24daf436ed8 100644 --- a/trie/transition.go +++ b/trie/transition.go @@ -21,7 +21,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/gballet/go-verkle" + "github.com/ethereum/go-verkle" ) type TransitionTrie struct { @@ -174,7 +174,7 @@ func (t *TransitionTrie) UpdateStem(key []byte, values [][]byte) error { trie := t.overlay switch root := trie.root.(type) { case *verkle.InternalNode: - return root.InsertStem(key, values, t.overlay.flatdbNodeResolver) + return root.InsertValuesAtStem(key, values, t.overlay.FlatdbNodeResolver) default: panic("invalid root type") } diff --git a/trie/utils/verkle.go b/trie/utils/verkle.go index 17fdf1ade34..65f4c1fa2ab 100644 --- a/trie/utils/verkle.go +++ b/trie/utils/verkle.go @@ -18,10 +18,10 @@ package utils import ( "encoding/binary" - "sync" "github.com/crate-crypto/go-ipa/bandersnatch/fr" - "github.com/gballet/go-verkle" + "github.com/ethereum/go-ethereum/common/lru" + "github.com/ethereum/go-verkle" "github.com/holiman/uint256" ) @@ -31,15 +31,17 @@ const ( NonceLeafKey = 2 CodeKeccakLeafKey = 3 CodeSizeLeafKey = 4 + + maxPointCacheByteSize = 100 << 20 ) var ( zero = uint256.NewInt(0) VerkleNodeWidthLog2 = 8 HeaderStorageOffset = uint256.NewInt(64) - mainStorageOffsetLshVerkleNodeWidth = new(uint256.Int).Lsh(uint256.NewInt(256), 31-uint(VerkleNodeWidthLog2)) + mainStorageOffsetLshVerkleNodeWidth = new(uint256.Int).Lsh(uint256.NewInt(1), 248-uint(VerkleNodeWidthLog2)) CodeOffset = uint256.NewInt(128) - MainStorageOffset = new(uint256.Int).Lsh(uint256.NewInt(256), 31) + MainStorageOffset = new(uint256.Int).Lsh(uint256.NewInt(1), 248 /* 8 * 31*/) VerkleNodeWidth = uint256.NewInt(256) codeStorageDelta = uint256.NewInt(0).Sub(CodeOffset, HeaderStorageOffset) @@ -47,28 +49,26 @@ var ( ) type PointCache struct { - cache map[string]*verkle.Point - lock sync.RWMutex + cache *lru.Cache[string, *verkle.Point] } func NewPointCache() *PointCache { + // Each verkle.Point is 96 bytes. + verklePointSize := 96 + capacity := maxPointCacheByteSize / verklePointSize return &PointCache{ - cache: make(map[string]*verkle.Point), + cache: lru.NewCache[string, *verkle.Point](capacity), } } func (pc *PointCache) GetTreeKeyHeader(addr []byte) *verkle.Point { - pc.lock.RLock() - point, ok := pc.cache[string(addr)] - pc.lock.RUnlock() + point, ok := pc.cache.Get(string(addr)) if ok { return point } point = EvaluateAddressPoint(addr) - pc.lock.Lock() - pc.cache[string(addr)] = point - pc.lock.Unlock() + pc.cache.Add(string(addr), point) return point } @@ -186,28 +186,6 @@ func GetTreeKeyCodeChunkWithEvaluatedAddress(addressPoint *verkle.Point, chunk * return GetTreeKeyWithEvaluatedAddess(addressPoint, treeIndex, subIndex) } -func GetTreeKeyStorageSlot(address []byte, storageKey *uint256.Int) []byte { - pos := storageKey.Clone() - if storageKey.Cmp(codeStorageDelta) < 0 { - pos.Add(HeaderStorageOffset, storageKey) - } else { - pos.Add(MainStorageOffset, storageKey) - } - treeIndex := new(uint256.Int).Div(pos, VerkleNodeWidth) - - // calculate the sub_index, i.e. the index in the stem tree. - // Because the modulus is 256, it's the last byte of treeIndex - subIndexMod := new(uint256.Int).Mod(pos, VerkleNodeWidth) - var subIndex byte - if len(subIndexMod) != 0 { - // uint256 is broken into 4 little-endian quads, - // each with native endianness. Extract the least - // significant byte. - subIndex = byte(subIndexMod[0]) - } - return GetTreeKey(address, treeIndex, subIndex) -} - func PointToHash(evaluated *verkle.Point, suffix byte) []byte { // The output of Byte() is big engian for banderwagon. This // introduces an imbalance in the tree, because hashes are @@ -289,12 +267,23 @@ func GetTreeKeyStorageSlotTreeIndexes(storageKey []byte) (*uint256.Int, byte) { } // If the storage slot is in the main storage, we need to add the main storage offset. + // The first MAIN_STORAGE_OFFSET group will see its + // first 64 slots unreachable. This is either a typo in the + // spec or intended to conserve the 256-u256 + // aligment. If we decide to ever access these 64 + // slots, uncomment this. + // // Get the new offset since we now know that we are above 64. + // pos.Sub(&pos, codeStorageDelta) + // suffix := byte(pos[0] & 0xFF) + suffix := storageKey[len(storageKey)-1] + // We first divide by VerkleNodeWidth to create room to avoid an overflow next. pos.Rsh(&pos, uint(VerkleNodeWidthLog2)) + // We add mainStorageOffset/VerkleNodeWidth which can't overflow. pos.Add(&pos, mainStorageOffsetLshVerkleNodeWidth) // The sub-index is the LSB of the original storage key, since mainStorageOffset // doesn't affect this byte, so we can avoid masks or shifts. - return &pos, storageKey[len(storageKey)-1] + return &pos, suffix } diff --git a/trie/utils/verkle_test.go b/trie/utils/verkle_test.go index 744df9df26a..66f1cc473ea 100644 --- a/trie/utils/verkle_test.go +++ b/trie/utils/verkle_test.go @@ -23,7 +23,7 @@ import ( "math/rand" "testing" - "github.com/gballet/go-verkle" + "github.com/ethereum/go-verkle" "github.com/holiman/uint256" ) @@ -51,7 +51,7 @@ func TestConstantPoint(t *testing.T) { verkle.FromLEBytes(&expectedPoly[0], []byte{2, 64}) expected := cfg.CommitToPoly(expectedPoly[:], 1) - if !verkle.Equal(expected, getTreePolyIndex0Point) { + if !expected.Equal(getTreePolyIndex0Point) { t.Fatalf("Marshalled constant value is incorrect: %x != %x", expected.Bytes(), getTreePolyIndex0Point.Bytes()) } } diff --git a/trie/verkle.go b/trie/verkle.go index baf9fde541a..760e30c8cda 100644 --- a/trie/verkle.go +++ b/trie/verkle.go @@ -28,7 +28,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/utils" - "github.com/gballet/go-verkle" + "github.com/ethereum/go-verkle" "github.com/holiman/uint256" ) @@ -54,16 +54,15 @@ func NewVerkleTrie(root verkle.VerkleNode, db *Database, pointCache *utils.Point } } -func (trie *VerkleTrie) flatdbNodeResolver(path []byte) ([]byte, error) { +func (trie *VerkleTrie) FlatdbNodeResolver(path []byte) ([]byte, error) { return trie.db.diskdb.Get(append(FlatDBVerkleNodeKeyPrefix, path...)) } func (trie *VerkleTrie) InsertMigratedLeaves(leaves []verkle.LeafNode) error { - return trie.root.(*verkle.InternalNode).InsertMigratedLeaves(leaves, trie.flatdbNodeResolver) + return trie.root.(*verkle.InternalNode).InsertMigratedLeaves(leaves, trie.FlatdbNodeResolver) } var ( - errInvalidProof = errors.New("invalid proof") errInvalidRootType = errors.New("invalid node type for root") // WORKAROUND: this special error is returned if it has been @@ -90,13 +89,13 @@ func (trie *VerkleTrie) GetKey(key []byte) []byte { func (trie *VerkleTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) { pointEval := trie.pointCache.GetTreeKeyHeader(addr[:]) k := utils.GetTreeKeyStorageSlotWithEvaluatedAddress(pointEval, key) - return trie.root.Get(k, trie.flatdbNodeResolver) + return trie.root.Get(k, trie.FlatdbNodeResolver) } // GetWithHashedKey returns the value, assuming that the key has already // been hashed. func (trie *VerkleTrie) GetWithHashedKey(key []byte) ([]byte, error) { - return trie.root.Get(key, trie.flatdbNodeResolver) + return trie.root.Get(key, trie.FlatdbNodeResolver) } func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error) { @@ -108,7 +107,7 @@ func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error ) switch t.root.(type) { case *verkle.InternalNode: - values, err = t.root.(*verkle.InternalNode).GetStem(versionkey[:31], t.flatdbNodeResolver) + values, err = t.root.(*verkle.InternalNode).GetValuesAtStem(versionkey[:31], t.FlatdbNodeResolver) default: return nil, errInvalidRootType } @@ -177,7 +176,7 @@ func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount) switch root := t.root.(type) { case *verkle.InternalNode: - err = root.InsertStem(stem, values, t.flatdbNodeResolver) + err = root.InsertValuesAtStem(stem, values, t.FlatdbNodeResolver) default: return errInvalidRootType } @@ -192,7 +191,7 @@ func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount) func (trie *VerkleTrie) UpdateStem(key []byte, values [][]byte) error { switch root := trie.root.(type) { case *verkle.InternalNode: - return root.InsertStem(key, values, trie.flatdbNodeResolver) + return root.InsertValuesAtStem(key, values, trie.FlatdbNodeResolver) default: panic("invalid root type") } @@ -210,31 +209,10 @@ func (trie *VerkleTrie) UpdateStorage(address common.Address, key, value []byte) } else { copy(v[32-len(value):], value[:]) } - return trie.root.Insert(k, v[:], trie.flatdbNodeResolver) + return trie.root.Insert(k, v[:], trie.FlatdbNodeResolver) } func (t *VerkleTrie) DeleteAccount(addr common.Address) error { - var ( - err error - values = make([][]byte, verkle.NodeWidth) - stem = t.pointCache.GetTreeKeyVersionCached(addr[:]) - ) - - for i := 0; i < verkle.NodeWidth; i++ { - values[i] = zero[:] - } - - switch root := t.root.(type) { - case *verkle.InternalNode: - err = root.InsertStem(stem, values, t.flatdbNodeResolver) - default: - return errInvalidRootType - } - if err != nil { - return fmt.Errorf("DeleteAccount (%x) error: %v", addr, err) - } - // TODO figure out if the code size needs to be updated, too - return nil } @@ -244,7 +222,7 @@ func (trie *VerkleTrie) DeleteStorage(addr common.Address, key []byte) error { pointEval := trie.pointCache.GetTreeKeyHeader(addr[:]) k := utils.GetTreeKeyStorageSlotWithEvaluatedAddress(pointEval, key) var zero [32]byte - return trie.root.Insert(k, zero[:], trie.flatdbNodeResolver) + return trie.root.Insert(k, zero[:], trie.FlatdbNodeResolver) } // Hash returns the root hash of the trie. It does not write to the database and @@ -287,9 +265,7 @@ func (trie *VerkleTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet, error) { } batch.Write() - // Serialize root commitment form - rootH := root.Hash().BytesLE() - return common.BytesToHash(rootH[:]), nil, nil + return trie.Hash(), nil, nil } // NodeIterator returns an iterator that returns nodes of the trie. Iteration @@ -311,8 +287,9 @@ func (trie *VerkleTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error { func (trie *VerkleTrie) Copy() *VerkleTrie { return &VerkleTrie{ - root: trie.root.Copy(), - db: trie.db, + root: trie.root.Copy(), + db: trie.db, + pointCache: trie.pointCache, } } @@ -320,8 +297,12 @@ func (trie *VerkleTrie) IsVerkle() bool { return true } -func (trie *VerkleTrie) ProveAndSerialize(keys [][]byte) (*verkle.VerkleProof, verkle.StateDiff, error) { - proof, _, _, _, err := verkle.MakeVerkleMultiProof(trie.root, keys) +func ProveAndSerialize(pretrie, posttrie *VerkleTrie, keys [][]byte, resolver verkle.NodeResolverFn) (*verkle.VerkleProof, verkle.StateDiff, error) { + var postroot verkle.VerkleNode + if posttrie != nil { + postroot = posttrie.root + } + proof, _, _, _, err := verkle.MakeVerkleMultiProof(pretrie.root, postroot, keys, resolver) if err != nil { return nil, nil, err } @@ -334,77 +315,58 @@ func (trie *VerkleTrie) ProveAndSerialize(keys [][]byte) (*verkle.VerkleProof, v return p, kvps, nil } -type set = map[string]struct{} - -func addKey(s set, key []byte) { - s[string(key)] = struct{}{} -} - -func DeserializeAndVerifyVerkleProof(vp *verkle.VerkleProof, root []byte, statediff verkle.StateDiff) error { - rootC := new(verkle.Point) - rootC.SetBytesTrusted(root) - proof, cis, indices, yis, err := deserializeVerkleProof(vp, rootC, statediff) - if err != nil { - return fmt.Errorf("could not deserialize proof: %w", err) - } - cfg := verkle.GetConfig() - if !verkle.VerifyVerkleProof(proof, cis, indices, yis, cfg) { - return errInvalidProof - } - - return nil -} - -func deserializeVerkleProof(vp *verkle.VerkleProof, rootC *verkle.Point, statediff verkle.StateDiff) (*verkle.Proof, []*verkle.Point, []byte, []*verkle.Fr, error) { - var others set = set{} // Mark when an "other" stem has been seen +func DeserializeAndVerifyVerkleProof(vp *verkle.VerkleProof, preStateRoot []byte, postStateRoot []byte, statediff verkle.StateDiff) error { + // TODO: check that `OtherStems` have expected length and values. proof, err := verkle.DeserializeProof(vp, statediff) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("verkle proof deserialization error: %w", err) - } - - for _, stem := range proof.PoaStems { - addKey(others, stem) + return fmt.Errorf("verkle proof deserialization error: %w", err) } - if len(proof.Keys) != len(proof.Values) { - return nil, nil, nil, nil, fmt.Errorf("keys and values are of different length %d != %d", len(proof.Keys), len(proof.Values)) - } - - tree, err := verkle.TreeFromProof(proof, rootC) + rootC := new(verkle.Point) + rootC.SetBytes(preStateRoot) + pretree, err := verkle.PreStateTreeFromProof(proof, rootC) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("error rebuilding the tree from proof: %w", err) + return fmt.Errorf("error rebuilding the pre-tree from proof: %w", err) } + // TODO this should not be necessary, remove it + // after the new proof generation code has stabilized. for _, stemdiff := range statediff { for _, suffixdiff := range stemdiff.SuffixDiffs { var key [32]byte copy(key[:31], stemdiff.Stem[:]) key[31] = suffixdiff.Suffix - val, err := tree.Get(key[:], nil) + val, err := pretree.Get(key[:], nil) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("could not find key %x in tree rebuilt from proof: %w", key, err) + return fmt.Errorf("could not find key %x in tree rebuilt from proof: %w", key, err) } if len(val) > 0 { if !bytes.Equal(val, suffixdiff.CurrentValue[:]) { - return nil, nil, nil, nil, fmt.Errorf("could not find correct value at %x in tree rebuilt from proof: %x != %x", key, val, *suffixdiff.CurrentValue) + return fmt.Errorf("could not find correct value at %x in tree rebuilt from proof: %x != %x", key, val, *suffixdiff.CurrentValue) } } else { if suffixdiff.CurrentValue != nil && len(suffixdiff.CurrentValue) != 0 { - return nil, nil, nil, nil, fmt.Errorf("could not find correct value at %x in tree rebuilt from proof: %x != %x", key, val, *suffixdiff.CurrentValue) + return fmt.Errorf("could not find correct value at %x in tree rebuilt from proof: %x != %x", key, val, *suffixdiff.CurrentValue) } } } } - // no need to resolve as the tree has been reconstructed from the proof - // and must not contain any unresolved nodes. - pe, _, _, err := tree.GetProofItems(proof.Keys) + // TODO: this is necessary to verify that the post-values are the correct ones. + // But all this can be avoided with a even faster way. The EVM block execution can + // keep track of the written keys, and compare that list with this post-values list. + // This can avoid regenerating the post-tree which is somewhat expensive. + posttree, err := verkle.PostStateTreeFromStateDiff(pretree, statediff) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("could not get proof items from tree rebuilt from proof: %w", err) + return fmt.Errorf("error rebuilding the post-tree from proof: %w", err) + } + regeneratedPostTreeRoot := posttree.Commitment().Bytes() + if !bytes.Equal(regeneratedPostTreeRoot[:], postStateRoot) { + return fmt.Errorf("post tree root mismatch: %x != %x", regeneratedPostTreeRoot, postStateRoot) } - return proof, pe.Cis, pe.Zis, pe.Yis, err + return verkle.VerifyVerkleProofWithPreState(proof, pretree) } // ChunkedCode represents a sequence of 32-bytes chunks of code (31 bytes of which diff --git a/trie/verkle_iterator.go b/trie/verkle_iterator.go index c5f59a0f593..5f5fc725ed4 100644 --- a/trie/verkle_iterator.go +++ b/trie/verkle_iterator.go @@ -19,7 +19,7 @@ package trie import ( "github.com/ethereum/go-ethereum/common" - "github.com/gballet/go-verkle" + "github.com/ethereum/go-verkle" ) type verkleNodeIteratorState struct { diff --git a/trie/verkle_iterator_test.go b/trie/verkle_iterator_test.go index 1fd3fd76a6d..d1611feee32 100644 --- a/trie/verkle_iterator_test.go +++ b/trie/verkle_iterator_test.go @@ -24,7 +24,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/trie/utils" - "github.com/gballet/go-verkle" + "github.com/ethereum/go-verkle" ) func TestVerkleIterator(t *testing.T) { diff --git a/trie/verkle_test.go b/trie/verkle_test.go index 4e21ee501a2..9f30c6bde85 100644 --- a/trie/verkle_test.go +++ b/trie/verkle_test.go @@ -23,7 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/trie/utils" - "github.com/gballet/go-verkle" + "github.com/ethereum/go-verkle" ) func TestReproduceTree(t *testing.T) { @@ -69,11 +69,15 @@ func TestReproduceTree(t *testing.T) { for i, key := range presentKeys { root.Insert(key, values[i], nil) } + root.Commit() - proof, Cs, zis, yis, _ := verkle.MakeVerkleMultiProof(root, append(presentKeys, absentKeys...)) + proof, Cs, zis, yis, err := verkle.MakeVerkleMultiProof(root, nil, append(presentKeys, absentKeys...), nil) + if err != nil { + t.Fatalf("could not create proof: %v", err) + } cfg := verkle.GetConfig() - if !verkle.VerifyVerkleProof(proof, Cs, zis, yis, cfg) { - t.Fatal("could not verify proof") + if ok, err := verkle.VerifyVerkleProof(proof, Cs, zis, yis, cfg); !ok || err != nil { + t.Fatalf("could not verify proof: %v", err) } t.Log("commitments returned by proof:") @@ -288,10 +292,11 @@ func TestReproduceCondrieuStemAggregationInProofOfAbsence(t *testing.T) { for i, key := range presentKeys { root.Insert(key, values[i], nil) } + root.Commit() - proof, Cs, zis, yis, _ := verkle.MakeVerkleMultiProof(root, append(presentKeys, absentKeys...)) + proof, Cs, zis, yis, _ := verkle.MakeVerkleMultiProof(root, nil, append(presentKeys, absentKeys...), nil) cfg := verkle.GetConfig() - if !verkle.VerifyVerkleProof(proof, Cs, zis, yis, cfg) { + if ok, err := verkle.VerifyVerkleProof(proof, Cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") } @@ -308,8 +313,8 @@ func TestReproduceCondrieuStemAggregationInProofOfAbsence(t *testing.T) { t.Logf("tree: %s\n%x\n", verkle.ToDot(root), root.Commitment().Bytes()) t.Logf("%d", len(proof.ExtStatus)) - if len(proof.ExtStatus) != 5 { - t.Fatalf("invalid number of declared stems: %d != 5", len(proof.ExtStatus)) + if len(proof.ExtStatus) != 6 { + t.Fatalf("invalid number of declared stems: %d != 6", len(proof.ExtStatus)) } } @@ -333,10 +338,11 @@ func TestReproduceCondrieuPoAStemConflictWithAnotherStem(t *testing.T) { for i, key := range presentKeys { root.Insert(key, values[i], nil) } + root.Commit() - proof, Cs, zis, yis, _ := verkle.MakeVerkleMultiProof(root, append(presentKeys, absentKeys...)) + proof, Cs, zis, yis, _ := verkle.MakeVerkleMultiProof(root, nil, append(presentKeys, absentKeys...), nil) cfg := verkle.GetConfig() - if !verkle.VerifyVerkleProof(proof, Cs, zis, yis, cfg) { + if ok, err := verkle.VerifyVerkleProof(proof, Cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") } @@ -360,7 +366,7 @@ func TestReproduceCondrieuPoAStemConflictWithAnotherStem(t *testing.T) { func TestEmptyKeySetInProveAndSerialize(t *testing.T) { tree := verkle.New() - verkle.MakeVerkleMultiProof(tree, [][]byte{}) + verkle.MakeVerkleMultiProof(tree, nil, [][]byte{}, nil) } func TestGetTreeKeys(t *testing.T) {