From 08cc404362cb13772c0af222c8a26e4ec08e1f33 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 5 Jul 2024 21:54:00 +0200 Subject: [PATCH 01/12] consensus/beacon, core/{state,types}: add verkle witness builder --- beacon/engine/gen_ed.go | 74 +++++++++++++++-------------- beacon/engine/types.go | 71 ++++++++++++++-------------- consensus/beacon/consensus.go | 32 ++++++++++++- core/state/statedb.go | 21 ++++++++- core/types/block.go | 32 +++++++++++++ core/types/gen_header_json.go | 88 +++++++++++++++++++---------------- trie/verkle.go | 22 +++++++++ 7 files changed, 228 insertions(+), 112 deletions(-) diff --git a/beacon/engine/gen_ed.go b/beacon/engine/gen_ed.go index 6893d64a1626..0ae5a3b8f1f1 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,omitempty"` } 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,omitempty"` } 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 d1b3aa22abdf..a21eb747193f 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -59,23 +59,24 @@ type payloadAttributesMarshaling struct { // ExecutableData is the data necessary to execute an EL payload. 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 []byte `json:"logsBloom" gencodec:"required"` - Random common.Hash `json:"prevRandao" gencodec:"required"` - Number uint64 `json:"blockNumber" gencodec:"required"` - GasLimit uint64 `json:"gasLimit" gencodec:"required"` - GasUsed uint64 `json:"gasUsed" gencodec:"required"` - Timestamp uint64 `json:"timestamp" gencodec:"required"` - ExtraData []byte `json:"extraData" gencodec:"required"` - BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"` - BlockHash common.Hash `json:"blockHash" gencodec:"required"` - Transactions [][]byte `json:"transactions" gencodec:"required"` - Withdrawals []*types.Withdrawal `json:"withdrawals"` - BlobGasUsed *uint64 `json:"blobGasUsed"` - ExcessBlobGas *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 []byte `json:"logsBloom" gencodec:"required"` + Random common.Hash `json:"prevRandao" gencodec:"required"` + Number uint64 `json:"blockNumber" gencodec:"required"` + GasLimit uint64 `json:"gasLimit" gencodec:"required"` + GasUsed uint64 `json:"gasUsed" gencodec:"required"` + Timestamp uint64 `json:"timestamp" gencodec:"required"` + ExtraData []byte `json:"extraData" gencodec:"required"` + BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"` + BlockHash common.Hash `json:"blockHash" gencodec:"required"` + Transactions [][]byte `json:"transactions" gencodec:"required"` + Withdrawals []*types.Withdrawal `json:"withdrawals"` + BlobGasUsed *uint64 `json:"blobGasUsed"` + ExcessBlobGas *uint64 `json:"excessBlobGas"` + ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"` } // JSON type overrides for executableData. @@ -250,6 +251,7 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b ExcessBlobGas: data.ExcessBlobGas, BlobGasUsed: data.BlobGasUsed, ParentBeaconRoot: beaconRoot, + ExecutionWitness: params.ExecutionWitness, } block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}) if block.Hash() != data.BlockHash { @@ -262,23 +264,24 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b // fields from the given block. It assumes the given block is post-merge block. func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.BlobTxSidecar) *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(), } bundle := BlobsBundleV1{ Commitments: make([]hexutil.Bytes, 0), diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 19763ed303f1..e1332ad69df9 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -386,9 +386,37 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea // Assign the final state root to header. header.Root = state.IntermediateRoot(true) - // Assemble and return the final block. - return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)), nil + + block := types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)) + + if chain.Config().IsVerkle(header.Number, header.Time) { + keys := state.AccessEvents().Keys() + + // Open the pre-tree to prove the pre-state against + parent := chain.GetHeaderByNumber(header.Number.Uint64() - 1) + if parent == nil { + return nil, fmt.Errorf("nil parent header for block %d", header.Number) + } + + preTrie, err := state.Database().OpenTrie(parent.Root) + if err != nil { + return nil, fmt.Errorf("error opening pre-state tree root: %w", err) + } + + vtrpre, okpre := preTrie.(*trie.VerkleTrie) + vtrpost, okpost := state.GetTrie().(*trie.VerkleTrie) + 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) + } + block.SetVerkleProof(p, k) + } + } + } + return block, nil } // Seal generates a new sealing request for the given input block and pushes diff --git a/core/state/statedb.go b/core/state/statedb.go index d93271d76b86..b19d7e366016 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -130,7 +130,8 @@ type StateDB struct { preimages map[common.Hash][]byte // Per-transaction access list - accessList *accessList + accessList *accessList + accessEvents *AccessEvents // Transient storage transientStorage transientStorage @@ -184,6 +185,9 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) transientStorage: newTransientStorage(), hasher: crypto.NewKeccakState(), } + if db.TrieDB().IsVerkle() { + sdb.accessEvents = sdb.NewAccessEvents() + } if sdb.snaps != nil { sdb.snap = sdb.snaps.Snapshot(root) } @@ -1452,3 +1456,18 @@ func (s *StateDB) PointCache() *utils.PointCache { func (s *StateDB) Witness() *stateless.Witness { return s.witness } + +func (s *StateDB) NewAccessEvents() *AccessEvents { + return &AccessEvents{ + branches: make(map[branchAccessKey]mode), + chunks: make(map[chunkAccessKey]mode), + pointCache: utils.NewPointCache(100), + } +} + +func (s *StateDB) AccessEvents() *AccessEvents { + if s.accessEvents == nil { + s.accessEvents = s.NewAccessEvents() + } + return s.accessEvents +} diff --git a/core/types/block.go b/core/types/block.go index 4857cd6e50c8..c14bce1cc22a 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -30,6 +30,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 @@ -59,6 +60,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 @@ -94,6 +107,8 @@ type Header struct { // ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers. ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` + + ExecutionWitness *ExecutionWitness `json:"executionWitness,omitempty" rlp:"-"` } // field type overrides for gencodec @@ -302,6 +317,9 @@ func CopyHeader(h *Header) *Header { cpy.ParentBeaconRoot = new(common.Hash) *cpy.ParentBeaconRoot = *h.ParentBeaconRoot } + if h.ExecutionWitness != nil { + cpy.ExecutionWitness = h.ExecutionWitness.Copy() + } return &cpy } @@ -401,6 +419,8 @@ func (b *Block) BlobGasUsed() *uint64 { return blobGasUsed } +func (b *Block) ExecutionWitness() *ExecutionWitness { return b.header.ExecutionWitness } + // Size returns the true RLP encoded storage size of the block, either by encoding // and returning it, or returning a previously cached value. func (b *Block) Size() uint64 { @@ -477,6 +497,18 @@ func (b *Block) Hash() common.Hash { return h } +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 Blocks []*Block // HeaderParentHashFromRLP returns the parentHash of an RLP-encoded diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go index fb1f915d01d9..d59f5dfd4933 100644 --- a/core/types/gen_header_json.go +++ b/core/types/gen_header_json.go @@ -16,27 +16,28 @@ var _ = (*headerMarshaling)(nil) // MarshalJSON marshals as JSON. func (h Header) MarshalJSON() ([]byte, error) { type Header struct { - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` - Coinbase common.Address `json:"miner"` - Root common.Hash `json:"stateRoot" gencodec:"required"` - TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` - ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` - Bloom Bloom `json:"logsBloom" gencodec:"required"` - Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` - Number *hexutil.Big `json:"number" gencodec:"required"` - GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` - Extra hexutil.Bytes `json:"extraData" gencodec:"required"` - MixDigest common.Hash `json:"mixHash"` - Nonce BlockNonce `json:"nonce"` - BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` - WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` - BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` - ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` - ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` - Hash common.Hash `json:"hash"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase common.Address `json:"miner"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra hexutil.Bytes `json:"extraData" gencodec:"required"` + MixDigest common.Hash `json:"mixHash"` + Nonce BlockNonce `json:"nonce"` + BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` + WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` + BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` + ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` + ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` + ExecutionWitness *ExecutionWitness `json:"executionWitness,omitempty" rlp:"-"` + Hash common.Hash `json:"hash"` } var enc Header enc.ParentHash = h.ParentHash @@ -59,6 +60,7 @@ func (h Header) MarshalJSON() ([]byte, error) { enc.BlobGasUsed = (*hexutil.Uint64)(h.BlobGasUsed) enc.ExcessBlobGas = (*hexutil.Uint64)(h.ExcessBlobGas) enc.ParentBeaconRoot = h.ParentBeaconRoot + enc.ExecutionWitness = h.ExecutionWitness enc.Hash = h.Hash() return json.Marshal(&enc) } @@ -66,26 +68,27 @@ func (h Header) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals from JSON. func (h *Header) UnmarshalJSON(input []byte) error { type Header struct { - ParentHash *common.Hash `json:"parentHash" gencodec:"required"` - UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` - Coinbase *common.Address `json:"miner"` - Root *common.Hash `json:"stateRoot" gencodec:"required"` - TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` - ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` - Bloom *Bloom `json:"logsBloom" gencodec:"required"` - Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` - Number *hexutil.Big `json:"number" gencodec:"required"` - GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` - Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` - MixDigest *common.Hash `json:"mixHash"` - Nonce *BlockNonce `json:"nonce"` - BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` - WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` - BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` - ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` - ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase *common.Address `json:"miner"` + Root *common.Hash `json:"stateRoot" gencodec:"required"` + TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom *Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` + MixDigest *common.Hash `json:"mixHash"` + Nonce *BlockNonce `json:"nonce"` + BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` + WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` + BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` + ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` + ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` + ExecutionWitness *ExecutionWitness `json:"executionWitness,omitempty" rlp:"-"` } var dec Header if err := json.Unmarshal(input, &dec); err != nil { @@ -163,5 +166,8 @@ func (h *Header) UnmarshalJSON(input []byte) error { if dec.ParentBeaconRoot != nil { h.ParentBeaconRoot = dec.ParentBeaconRoot } + if dec.ExecutionWitness != nil { + h.ExecutionWitness = dec.ExecutionWitness + } return nil } diff --git a/trie/verkle.go b/trie/verkle.go index fab1ccb48ac5..ffbae9fb6337 100644 --- a/trie/verkle.go +++ b/trie/verkle.go @@ -69,6 +69,10 @@ func NewVerkleTrie(root common.Hash, db database.Database, cache *utils.PointCac }, nil } +func (trie *VerkleTrie) FlatdbNodeResolver(path []byte) ([]byte, error) { + return trie.reader.node(path, common.Hash{}) +} + // GetKey returns the sha3 preimage of a hashed key that was previously used // to store a value. func (t *VerkleTrie) GetKey(key []byte) []byte { @@ -303,6 +307,24 @@ func (t *VerkleTrie) IsVerkle() bool { return true } +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 + } + + p, kvps, err := verkle.SerializeProof(proof) + if err != nil { + return nil, nil, err + } + + return p, kvps, nil +} + // ChunkedCode represents a sequence of 32-bytes chunks of code (31 bytes of which // are actual code, and 1 byte is the pushdata offset). type ChunkedCode []byte From 7729aaaf35d02eaf36caab6e6c32f91511ba4482 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Mon, 22 Jul 2024 15:29:08 +0200 Subject: [PATCH 02/12] review feedback --- beacon/engine/types.go | 3 +- consensus/beacon/consensus.go | 2 +- core/chain_makers.go | 5 +- core/state/statedb.go | 6 +-- core/state_processor.go | 4 ++ core/state_processor_test.go | 10 +++- core/types/block.go | 32 ++++++------- core/types/gen_header_json.go | 88 ++++++++++++++++------------------- trie/verkle.go | 7 ++- 9 files changed, 79 insertions(+), 78 deletions(-) diff --git a/beacon/engine/types.go b/beacon/engine/types.go index a21eb747193f..7f0e71e0fc09 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -251,9 +251,8 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b ExcessBlobGas: data.ExcessBlobGas, BlobGasUsed: data.BlobGasUsed, ParentBeaconRoot: beaconRoot, - ExecutionWitness: params.ExecutionWitness, } - block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}) + block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals, Witness: data.ExecutionWitness}) if block.Hash() != data.BlockHash { return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", data.BlockHash, block.Hash()) } diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index e1332ad69df9..9e06105249c4 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -408,7 +408,7 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea vtrpost, okpost := state.GetTrie().(*trie.VerkleTrie) if okpre && okpost { if len(keys) > 0 { - p, k, err := trie.ProveAndSerialize(vtrpre, vtrpost, keys, vtrpre.FlatdbNodeResolver) + p, k, err := vtrpre.Proof(vtrpost, keys, vtrpre.FlatdbNodeResolver) if err != nil { return nil, fmt.Errorf("error generating verkle proof for block %d: %w", header.Number, err) } diff --git a/core/chain_makers.go b/core/chain_makers.go index 58985347bb31..6cee6fdc8a11 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -467,9 +467,8 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine panic(fmt.Sprintf("trie write error: %v", err)) } - // TODO uncomment when proof generation is merged - // proofs = append(proofs, block.ExecutionWitness().VerkleProof) - // keyvals = append(keyvals, block.ExecutionWitness().StateDiff) + proofs = append(proofs, block.ExecutionWitness().VerkleProof) + keyvals = append(keyvals, block.ExecutionWitness().StateDiff) return block, b.receipts } diff --git a/core/state/statedb.go b/core/state/statedb.go index b19d7e366016..dc7f3f52f6ad 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -713,6 +713,9 @@ func (s *StateDB) Copy() *StateDB { if s.witness != nil { state.witness = s.witness.Copy() } + if s.accessEvents != nil { + state.accessEvents = s.accessEvents.Copy() + } // Deep copy cached state objects. for addr, obj := range s.stateObjects { state.stateObjects[addr] = obj.deepCopy(state) @@ -1466,8 +1469,5 @@ func (s *StateDB) NewAccessEvents() *AccessEvents { } func (s *StateDB) AccessEvents() *AccessEvents { - if s.accessEvents == nil { - s.accessEvents = s.NewAccessEvents() - } return s.accessEvents } diff --git a/core/state_processor.go b/core/state_processor.go index 44224958ddce..09cc4b17223f 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -153,6 +153,10 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) } + // Merge the tx-local access event into the "block-local" one, in order to collect + // all values, so that the witness can be built. + statedb.AccessEvents().Merge(evm.AccessEvents) + // Set the receipt logs and create the bloom filter. receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash) receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index bf29fb97733f..83d06a5aa806 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -38,6 +38,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-verkle" "github.com/holiman/uint256" "golang.org/x/crypto/sha3" ) @@ -491,7 +492,7 @@ func TestProcessVerkle(t *testing.T) { txCost1*2 + txCost2, txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas, } - _, chain, _, _, _ := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { + _, chain, _, proofs, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 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) @@ -512,7 +513,12 @@ func TestProcessVerkle(t *testing.T) { } }) - t.Log("inserting blocks into the chain") + err := verkle.Verify(proofs[1], chain[0].Root().Bytes(), chain[1].Root().Bytes(), statediffs[1]) + if err != nil { + t.Fatal(err) + } + + t.Log("verified verkle proof, inserting blocks into the chain") endnum, err := blockchain.InsertChain(chain) if err != nil { diff --git a/core/types/block.go b/core/types/block.go index c14bce1cc22a..371884d93337 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -60,18 +60,13 @@ func (n *BlockNonce) UnmarshalText(input []byte) error { return hexutil.UnmarshalFixedText("BlockNonce", input, n[:]) } +// ExecutionWitness represents the witness + proof used in a verkle context, +// to provide the ability to execute a block statelessly. 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 @@ -107,8 +102,6 @@ type Header struct { // ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers. ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` - - ExecutionWitness *ExecutionWitness `json:"executionWitness,omitempty" rlp:"-"` } // field type overrides for gencodec @@ -186,7 +179,8 @@ func (h *Header) EmptyReceipts() bool { type Body struct { Transactions []*Transaction Uncles []*Header - Withdrawals []*Withdrawal `rlp:"optional"` + Withdrawals []*Withdrawal `rlp:"optional"` + Witness *ExecutionWitness `rlp:"-"` } // Block represents an Ethereum block. @@ -212,6 +206,8 @@ type Block struct { transactions Transactions withdrawals Withdrawals + witness *ExecutionWitness + // caches hash atomic.Pointer[common.Hash] size atomic.Uint64 @@ -317,9 +313,6 @@ func CopyHeader(h *Header) *Header { cpy.ParentBeaconRoot = new(common.Hash) *cpy.ParentBeaconRoot = *h.ParentBeaconRoot } - if h.ExecutionWitness != nil { - cpy.ExecutionWitness = h.ExecutionWitness.Copy() - } return &cpy } @@ -348,7 +341,7 @@ func (b *Block) EncodeRLP(w io.Writer) error { // Body returns the non-header content of the block. // Note the returned data is not an independent copy. func (b *Block) Body() *Body { - return &Body{b.transactions, b.uncles, b.withdrawals} + return &Body{b.transactions, b.uncles, b.withdrawals, b.witness} } // Accessors for body data. These do not return a copy because the content @@ -419,7 +412,8 @@ func (b *Block) BlobGasUsed() *uint64 { return blobGasUsed } -func (b *Block) ExecutionWitness() *ExecutionWitness { return b.header.ExecutionWitness } +// ExecutionWitness returns the verkle execution witneess + proof for a block +func (b *Block) ExecutionWitness() *ExecutionWitness { return b.witness } // Size returns the true RLP encoded storage size of the block, either by encoding // and returning it, or returning a previously cached value. @@ -479,6 +473,7 @@ func (b *Block) WithBody(body Body) *Block { transactions: slices.Clone(body.Transactions), uncles: make([]*Header, len(body.Uncles)), withdrawals: slices.Clone(body.Withdrawals), + witness: body.Witness, } for i := range body.Uncles { block.uncles[i] = CopyHeader(body.Uncles[i]) @@ -497,13 +492,14 @@ func (b *Block) Hash() common.Hash { return h } +// SetVerkleProof attaches an execution witness + proof to the block in a verkle context. func (b *Block) SetVerkleProof(vp *verkle.VerkleProof, statediff verkle.StateDiff) { - b.header.ExecutionWitness = &ExecutionWitness{statediff, vp} + b.witness = &ExecutionWitness{statediff, vp} if statediff == nil { - b.header.ExecutionWitness.StateDiff = []verkle.StemStateDiff{} + b.witness.StateDiff = []verkle.StemStateDiff{} } if vp == nil { - b.header.ExecutionWitness.VerkleProof = &verkle.VerkleProof{ + b.witness.VerkleProof = &verkle.VerkleProof{ IPAProof: &verkle.IPAProof{}, } } diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go index d59f5dfd4933..fb1f915d01d9 100644 --- a/core/types/gen_header_json.go +++ b/core/types/gen_header_json.go @@ -16,28 +16,27 @@ var _ = (*headerMarshaling)(nil) // MarshalJSON marshals as JSON. func (h Header) MarshalJSON() ([]byte, error) { type Header struct { - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` - Coinbase common.Address `json:"miner"` - Root common.Hash `json:"stateRoot" gencodec:"required"` - TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` - ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` - Bloom Bloom `json:"logsBloom" gencodec:"required"` - Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` - Number *hexutil.Big `json:"number" gencodec:"required"` - GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` - Extra hexutil.Bytes `json:"extraData" gencodec:"required"` - MixDigest common.Hash `json:"mixHash"` - Nonce BlockNonce `json:"nonce"` - BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` - WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` - BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` - ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` - ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` - ExecutionWitness *ExecutionWitness `json:"executionWitness,omitempty" rlp:"-"` - Hash common.Hash `json:"hash"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase common.Address `json:"miner"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra hexutil.Bytes `json:"extraData" gencodec:"required"` + MixDigest common.Hash `json:"mixHash"` + Nonce BlockNonce `json:"nonce"` + BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` + WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` + BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` + ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` + ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` + Hash common.Hash `json:"hash"` } var enc Header enc.ParentHash = h.ParentHash @@ -60,7 +59,6 @@ func (h Header) MarshalJSON() ([]byte, error) { enc.BlobGasUsed = (*hexutil.Uint64)(h.BlobGasUsed) enc.ExcessBlobGas = (*hexutil.Uint64)(h.ExcessBlobGas) enc.ParentBeaconRoot = h.ParentBeaconRoot - enc.ExecutionWitness = h.ExecutionWitness enc.Hash = h.Hash() return json.Marshal(&enc) } @@ -68,27 +66,26 @@ func (h Header) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals from JSON. func (h *Header) UnmarshalJSON(input []byte) error { type Header struct { - ParentHash *common.Hash `json:"parentHash" gencodec:"required"` - UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` - Coinbase *common.Address `json:"miner"` - Root *common.Hash `json:"stateRoot" gencodec:"required"` - TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` - ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` - Bloom *Bloom `json:"logsBloom" gencodec:"required"` - Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` - Number *hexutil.Big `json:"number" gencodec:"required"` - GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` - Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` - MixDigest *common.Hash `json:"mixHash"` - Nonce *BlockNonce `json:"nonce"` - BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` - WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` - BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` - ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` - ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` - ExecutionWitness *ExecutionWitness `json:"executionWitness,omitempty" rlp:"-"` + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase *common.Address `json:"miner"` + Root *common.Hash `json:"stateRoot" gencodec:"required"` + TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom *Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` + MixDigest *common.Hash `json:"mixHash"` + Nonce *BlockNonce `json:"nonce"` + BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` + WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"` + BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` + ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` + ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` } var dec Header if err := json.Unmarshal(input, &dec); err != nil { @@ -166,8 +163,5 @@ func (h *Header) UnmarshalJSON(input []byte) error { if dec.ParentBeaconRoot != nil { h.ParentBeaconRoot = dec.ParentBeaconRoot } - if dec.ExecutionWitness != nil { - h.ExecutionWitness = dec.ExecutionWitness - } return nil } diff --git a/trie/verkle.go b/trie/verkle.go index ffbae9fb6337..ca190c7ad5e8 100644 --- a/trie/verkle.go +++ b/trie/verkle.go @@ -307,12 +307,15 @@ func (t *VerkleTrie) IsVerkle() bool { return true } -func ProveAndSerialize(pretrie, posttrie *VerkleTrie, keys [][]byte, resolver verkle.NodeResolverFn) (*verkle.VerkleProof, verkle.StateDiff, error) { +// Proof builds and returns the verkle multiproof for keys, built against +// the pre tree. The post tree is passed in order to add the post values +// to that proof. +func (t *VerkleTrie) Proof(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) + proof, _, _, _, err := verkle.MakeVerkleMultiProof(t.root, postroot, keys, resolver) if err != nil { return nil, nil, err } From 523dcbe20c21539da2317aa5d40f2e13fb829978 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Wed, 24 Jul 2024 12:39:46 +0200 Subject: [PATCH 03/12] review feedback --- core/state/statedb.go | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index dc7f3f52f6ad..300ce29a674f 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -186,7 +186,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) hasher: crypto.NewKeccakState(), } if db.TrieDB().IsVerkle() { - sdb.accessEvents = sdb.NewAccessEvents() + sdb.accessEvents = NewAccessEvents(db.(*cachingDB).pointCache) } if sdb.snaps != nil { sdb.snap = sdb.snaps.Snapshot(root) @@ -1460,14 +1460,6 @@ func (s *StateDB) Witness() *stateless.Witness { return s.witness } -func (s *StateDB) NewAccessEvents() *AccessEvents { - return &AccessEvents{ - branches: make(map[branchAccessKey]mode), - chunks: make(map[chunkAccessKey]mode), - pointCache: utils.NewPointCache(100), - } -} - func (s *StateDB) AccessEvents() *AccessEvents { return s.accessEvents } From 7649860d764e3e1141a555b5badc5e9643804cb0 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Wed, 24 Jul 2024 14:45:30 +0200 Subject: [PATCH 04/12] guard access events merge --- core/state_processor.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/state_processor.go b/core/state_processor.go index 09cc4b17223f..c13b69602244 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -155,7 +155,9 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo // Merge the tx-local access event into the "block-local" one, in order to collect // all values, so that the witness can be built. - statedb.AccessEvents().Merge(evm.AccessEvents) + if statedb.GetTrie().IsVerkle() { + statedb.AccessEvents().Merge(evm.AccessEvents) + } // Set the receipt logs and create the bloom filter. receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash) From 0d72a4c26b61c61922524bc9fe44272028b39900 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 25 Jul 2024 10:50:53 +0200 Subject: [PATCH 05/12] fix linter --- trie/verkle.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trie/verkle.go b/trie/verkle.go index ca190c7ad5e8..60293e899894 100644 --- a/trie/verkle.go +++ b/trie/verkle.go @@ -69,8 +69,8 @@ func NewVerkleTrie(root common.Hash, db database.Database, cache *utils.PointCac }, nil } -func (trie *VerkleTrie) FlatdbNodeResolver(path []byte) ([]byte, error) { - return trie.reader.node(path, common.Hash{}) +func (t *VerkleTrie) FlatdbNodeResolver(path []byte) ([]byte, error) { + return t.reader.node(path, common.Hash{}) } // GetKey returns the sha3 preimage of a hashed key that was previously used From 7f99bdc1377fd73cc2d73f70a35cc41cb98d7fcc Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Wed, 21 Aug 2024 21:17:14 +0200 Subject: [PATCH 06/12] use WithWitness and rename a few variables --- beacon/engine/types.go | 4 +++- consensus/beacon/consensus.go | 8 ++++---- core/types/block.go | 21 +++++++++++++++++---- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/beacon/engine/types.go b/beacon/engine/types.go index 7f0e71e0fc09..8687a6f5041d 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -252,7 +252,9 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b BlobGasUsed: data.BlobGasUsed, ParentBeaconRoot: beaconRoot, } - block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals, Witness: data.ExecutionWitness}) + block := types.NewBlockWithHeader(header) + block = block.WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}) + block = block.WithWitness(data.ExecutionWitness) if block.Hash() != data.BlockHash { return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", data.BlockHash, block.Hash()) } diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 9e06105249c4..3095404330c6 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -404,15 +404,15 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea return nil, fmt.Errorf("error opening pre-state tree root: %w", err) } - vtrpre, okpre := preTrie.(*trie.VerkleTrie) - vtrpost, okpost := state.GetTrie().(*trie.VerkleTrie) + vktPreTrie, okpre := preTrie.(*trie.VerkleTrie) + vktPostTrie, okpost := state.GetTrie().(*trie.VerkleTrie) if okpre && okpost { if len(keys) > 0 { - p, k, err := vtrpre.Proof(vtrpost, keys, vtrpre.FlatdbNodeResolver) + verkleProof, stateDiff, err := vktPreTrie.Proof(vktPostTrie, keys, vktPreTrie.FlatdbNodeResolver) if err != nil { return nil, fmt.Errorf("error generating verkle proof for block %d: %w", header.Number, err) } - block.SetVerkleProof(p, k) + return block.WithWitness(&types.ExecutionWitness{StateDiff: stateDiff, VerkleProof: verkleProof}), nil } } } diff --git a/core/types/block.go b/core/types/block.go index 371884d93337..2988cb4d434e 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -179,8 +179,7 @@ func (h *Header) EmptyReceipts() bool { type Body struct { Transactions []*Transaction Uncles []*Header - Withdrawals []*Withdrawal `rlp:"optional"` - Witness *ExecutionWitness `rlp:"-"` + Withdrawals []*Withdrawal `rlp:"optional"` } // Block represents an Ethereum block. @@ -341,7 +340,7 @@ func (b *Block) EncodeRLP(w io.Writer) error { // Body returns the non-header content of the block. // Note the returned data is not an independent copy. func (b *Block) Body() *Body { - return &Body{b.transactions, b.uncles, b.withdrawals, b.witness} + return &Body{b.transactions, b.uncles, b.withdrawals} } // Accessors for body data. These do not return a copy because the content @@ -473,7 +472,6 @@ func (b *Block) WithBody(body Body) *Block { transactions: slices.Clone(body.Transactions), uncles: make([]*Header, len(body.Uncles)), withdrawals: slices.Clone(body.Withdrawals), - witness: body.Witness, } for i := range body.Uncles { block.uncles[i] = CopyHeader(body.Uncles[i]) @@ -481,6 +479,21 @@ func (b *Block) WithBody(body Body) *Block { return block } +func (b *Block) WithWitness(witness *ExecutionWitness) *Block { + + block := &Block{ + header: b.header, + transactions: slices.Clone(b.transactions), + uncles: make([]*Header, len(b.uncles)), + withdrawals: slices.Clone(b.withdrawals), + witness: witness, + } + for i := range b.uncles { + block.uncles[i] = CopyHeader(b.uncles[i]) + } + return block +} + // Hash returns the keccak256 hash of b's header. // The hash is computed on the first call and cached thereafter. func (b *Block) Hash() common.Hash { From de728294d97da5565a1970b88138648411ce0489 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Wed, 21 Aug 2024 21:24:44 +0200 Subject: [PATCH 07/12] check proofs for both blocks in tests --- core/state_processor_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 83d06a5aa806..307ab75c5ba5 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -513,7 +513,12 @@ func TestProcessVerkle(t *testing.T) { } }) - err := verkle.Verify(proofs[1], chain[0].Root().Bytes(), chain[1].Root().Bytes(), statediffs[1]) + // Check proof for both blocks + err := verkle.Verify(proofs[0], gspec.ToBlock().Root().Bytes(), chain[0].Root().Bytes(), statediffs[0]) + if err != nil { + t.Fatal(err) + } + err = verkle.Verify(proofs[1], chain[0].Root().Bytes(), chain[1].Root().Bytes(), statediffs[1]) if err != nil { t.Fatal(err) } From 84ef0db2c034ec34a8210c8420983d963443e85f Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 23 Aug 2024 08:23:09 +0200 Subject: [PATCH 08/12] remove no-longer used SetVerkleProof method --- core/types/block.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/core/types/block.go b/core/types/block.go index 2988cb4d434e..c1ae8f732d86 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -505,19 +505,6 @@ func (b *Block) Hash() common.Hash { return h } -// SetVerkleProof attaches an execution witness + proof to the block in a verkle context. -func (b *Block) SetVerkleProof(vp *verkle.VerkleProof, statediff verkle.StateDiff) { - b.witness = &ExecutionWitness{statediff, vp} - if statediff == nil { - b.witness.StateDiff = []verkle.StemStateDiff{} - } - if vp == nil { - b.witness.VerkleProof = &verkle.VerkleProof{ - IPAProof: &verkle.IPAProof{}, - } - } -} - type Blocks []*Block // HeaderParentHashFromRLP returns the parentHash of an RLP-encoded From 9e2e8f041dae4054e7682966a805e205133db216 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Mon, 26 Aug 2024 09:39:49 +0200 Subject: [PATCH 09/12] don't copy header fields in WithBody --- core/types/block.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/types/block.go b/core/types/block.go index c1ae8f732d86..bcc7ab608e81 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -483,13 +483,13 @@ func (b *Block) WithWitness(witness *ExecutionWitness) *Block { block := &Block{ header: b.header, - transactions: slices.Clone(b.transactions), + transactions: b.transactions, uncles: make([]*Header, len(b.uncles)), - withdrawals: slices.Clone(b.withdrawals), + withdrawals: b.withdrawals, witness: witness, } for i := range b.uncles { - block.uncles[i] = CopyHeader(b.uncles[i]) + block.uncles[i] = b.uncles[i] } return block } From 5bfe055e1c091fb9dd4c5446368bceb0e3c6ae49 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 29 Aug 2024 13:29:17 +0200 Subject: [PATCH 10/12] core/types: fix witness --- core/types/block.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/types/block.go b/core/types/block.go index bcc7ab608e81..e6ddf2012f61 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -205,6 +205,9 @@ type Block struct { transactions Transactions withdrawals Withdrawals + // witness is not an encoded part of the block body. + // It is held in Block in order for easy relaying to the places + // that process it. witness *ExecutionWitness // caches @@ -461,6 +464,7 @@ func (b *Block) WithSeal(header *Header) *Block { transactions: b.transactions, uncles: b.uncles, withdrawals: b.withdrawals, + witness: b.witness, } } @@ -472,6 +476,7 @@ func (b *Block) WithBody(body Body) *Block { transactions: slices.Clone(body.Transactions), uncles: make([]*Header, len(body.Uncles)), withdrawals: slices.Clone(body.Withdrawals), + witness: b.witness, } for i := range body.Uncles { block.uncles[i] = CopyHeader(body.Uncles[i]) @@ -480,18 +485,13 @@ func (b *Block) WithBody(body Body) *Block { } func (b *Block) WithWitness(witness *ExecutionWitness) *Block { - - block := &Block{ + return &Block{ header: b.header, transactions: b.transactions, - uncles: make([]*Header, len(b.uncles)), + uncles: b.uncles, withdrawals: b.withdrawals, witness: witness, } - for i := range b.uncles { - block.uncles[i] = b.uncles[i] - } - return block } // Hash returns the keccak256 hash of b's header. From fe6b7fb0b4e172b3a4c4f31c37893439c7e56bad Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 29 Aug 2024 13:48:36 +0200 Subject: [PATCH 11/12] go.mod: upgrade go-verkle --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 49f2835c3e77..a8f5d11a89eb 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 github.com/ethereum/c-kzg-4844 v1.0.0 - github.com/ethereum/go-verkle v0.1.1-0.20240726143912-7dc5142667fa + github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 github.com/fatih/color v1.16.0 github.com/ferranbt/fastssz v0.1.2 github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e diff --git a/go.sum b/go.sum index 4540dbfa42a8..ab9bff9960be 100644 --- a/go.sum +++ b/go.sum @@ -170,8 +170,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-verkle v0.1.1-0.20240726143912-7dc5142667fa h1:mXkPoR07WlPVAClNzWuGAQNqmhxLqQILXhm73J5d9Ew= -github.com/ethereum/go-verkle v0.1.1-0.20240726143912-7dc5142667fa/go.mod h1:D9AJLVXSyZQXJQVk8oh1EwjISE+sJTn2duYIZC0dy3w= +github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= +github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNPk= From ade2284ce181e3abee97984b69667aa90e136d08 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 29 Aug 2024 13:52:35 +0200 Subject: [PATCH 12/12] consensus/beacon: add some comments --- consensus/beacon/consensus.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 3095404330c6..ae4c9f29a1cd 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -386,10 +386,12 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea // Assign the final state root to header. header.Root = state.IntermediateRoot(true) - // Assemble and return the final block. + // Assemble the final block. block := types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)) + // Create the block witness and attach to block. + // This step needs to happen as late as possible to catch all access events. if chain.Config().IsVerkle(header.Number, header.Time) { keys := state.AccessEvents().Keys() @@ -412,10 +414,11 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea if err != nil { return nil, fmt.Errorf("error generating verkle proof for block %d: %w", header.Number, err) } - return block.WithWitness(&types.ExecutionWitness{StateDiff: stateDiff, VerkleProof: verkleProof}), nil + block = block.WithWitness(&types.ExecutionWitness{StateDiff: stateDiff, VerkleProof: verkleProof}) } } } + return block, nil }