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] 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 a21eb74719..7f0e71e0fc 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 e1332ad69d..9e06105249 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 58985347bb..6cee6fdc8a 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 b19d7e3660..dc7f3f52f6 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 44224958dd..09cc4b1722 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 bf29fb9773..83d06a5aa8 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 c14bce1cc2..371884d933 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 d59f5dfd49..fb1f915d01 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 ffbae9fb63..ca190c7ad5 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 }