Skip to content

Commit

Permalink
move proof building before root computation
Browse files Browse the repository at this point in the history
Signed-off-by: Guillaume Ballet <[email protected]>
  • Loading branch information
gballet committed Nov 7, 2024
1 parent a66df52 commit c284ea4
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 72 deletions.
10 changes: 9 additions & 1 deletion cmd/evm/internal/t8ntool/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,10 +371,18 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
default:
return nil, nil, fmt.Errorf("invalid tree type in proof generation: %v", tr)
}
vktProof, vktStateDiff, err = trie.ProveAndSerialize(vtrpre, proofTrie, keys, vtrpre.FlatdbNodeResolver)
proof, err := trie.Proof(vtrpre, proofTrie, keys, vtrpre.FlatdbNodeResolver)
if err != nil {
return nil, nil, fmt.Errorf("error generating verkle proof for block %d: %w", pre.Env.Number, err)
}
err = trie.AddPostValuesToProof(keys, proofTrie, proof)
if err != nil {
return nil, nil, fmt.Errorf("error adding post values to proof: %w", err)
}
vktProof, vktStateDiff, err = verkle.SerializeProof(proof)
if err != nil {
return nil, nil, fmt.Errorf("error serializing proof: %w", err)
}
}
}

Expand Down
85 changes: 32 additions & 53 deletions consensus/beacon/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,97 +393,76 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
// Finalize and assemble the block.
beacon.Finalize(chain, header, state, txs, uncles, withdrawals)

// Assign the final state root to header.
header.Root = state.IntermediateRoot(true)
// Associate current conversion state to computed state
// root and store it in the database for later recovery.
state.Database().SaveTransitionState(header.Root)

var (
proof *verkle.VerkleProof
stateDiff verkle.StateDiff
proot common.Hash
proof *verkle.Proof
proot common.Hash
keys = state.Witness().Keys()
)
if chain.Config().IsVerkle(header.Number, header.Time) {
parent := chain.GetHeaderByNumber(header.Number.Uint64() - 1)
if parent == nil {
return nil, fmt.Errorf("nil parent header for block %d", header.Number)
}
// Load transition state at beginning of block, because
// OpenTrie needs to know what the conversion status is.
state.Database().LoadTransitionState(parent.Root)

var err error
stateDiff, proof, err = BuildVerkleProof(header, state, parent.Root)
proof, err = BuildVerkleProof(header, state, parent.Root, keys)
if err != nil {
return nil, fmt.Errorf("error building verkle proof: %w", err)
}
proot = parent.Root
}

// Assign the final state root to header.
header.Root = state.IntermediateRoot(true)
// Associate current conversion state to computed state
// root and store it in the database for later recovery.
state.Database().SaveTransitionState(header.Root)

// Assemble and return the final block.
block := types.NewBlockWithWithdrawals(header, txs, uncles, receipts, withdrawals, trie.NewStackTrie(nil))
if chain.Config().IsVerkle(header.Number, header.Time) && chain.Config().ProofInBlocks {
block.SetVerkleProof(proof, stateDiff, proot)
err := trie.AddPostValuesToProof(keys, state.GetTrie().(*trie.VerkleTrie), proof)
if err != nil {
return nil, fmt.Errorf("error adding post values to proof: %w", err)
}
vp, stateDiff, err := verkle.SerializeProof(proof)
if err != nil {
return nil, fmt.Errorf("error serializing proof: %w", err)
}
block.SetVerkleProof(vp, stateDiff, proot)
}
return block, nil
}

func BuildVerkleProof(header *types.Header, state *state.StateDB, parentRoot common.Hash) (verkle.StateDiff, *verkle.VerkleProof, error) {
func BuildVerkleProof(header *types.Header, state *state.StateDB, parentRoot common.Hash, keys [][]byte) (*verkle.Proof, error) {
var (
proof *verkle.VerkleProof
stateDiff verkle.StateDiff
proof *verkle.Proof
)

preTrie, err := state.Database().OpenTrie(parentRoot)
if err != nil {
return nil, nil, fmt.Errorf("error opening pre-state tree root: %w", err)
return nil, fmt.Errorf("error opening pre-state tree root: %w", err)
}

var okpre, okpost bool
var vtrpre, vtrpost *trie.VerkleTrie
var vtr *trie.VerkleTrie
switch pre := preTrie.(type) {
case *trie.VerkleTrie:
vtrpre, okpre = preTrie.(*trie.VerkleTrie)
switch tr := state.GetTrie().(type) {
case *trie.VerkleTrie:
vtrpost = tr
okpost = true
// This is to handle a situation right at the start of the conversion:
// the post trie is a transition tree when the pre tree is an empty
// verkle tree.
case *trie.TransitionTrie:
vtrpost = tr.Overlay()
okpost = true
default:
okpost = false
}
vtr = pre
case *trie.TransitionTrie:
vtrpre = pre.Overlay()
okpre = true
post, _ := state.GetTrie().(*trie.TransitionTrie)
vtrpost = post.Overlay()
okpost = true
vtr = pre.Overlay()
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 {
keys := state.Witness().Keys()
if len(keys) > 0 {
proof, stateDiff, err = trie.ProveAndSerialize(vtrpre, vtrpost, keys, vtrpre.FlatdbNodeResolver)
if err != nil {
return nil, nil, fmt.Errorf("error generating verkle proof for block %d: %w", header.Number, err)
}
vtr = trie.NewVerkleTrie(verkle.New(), state.Database().TrieDB(), utils.NewPointCache(), false)
}
if len(keys) > 0 {
proof, err = trie.Proof(vtr, nil, keys, vtr.FlatdbNodeResolver)
if err != nil {
return nil, fmt.Errorf("error generating verkle proof for block %d: %w", header.Number, err)
}
}
return stateDiff, proof, nil
return proof, nil
}

// Seal generates a new sealing request for the given input block and pushes
Expand Down
34 changes: 27 additions & 7 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ import (
"errors"
"fmt"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-verkle"
)

// BlockValidator is responsible for validating block headers, uncles and
Expand Down Expand Up @@ -127,24 +129,42 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
if receiptSha != header.ReceiptHash {
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
}
var (
proot common.Hash
proof *verkle.Proof
err error
keys [][]byte
)
if blockEw := block.ExecutionWitness(); blockEw != nil {
parent := v.bc.GetBlockByHash(header.ParentHash)
if parent == nil {
return fmt.Errorf("nil parent header for block %d", header.Number)
}
proot = parent.Root()
keys = statedb.Witness().Keys()
proof, err = beacon.BuildVerkleProof(header, statedb, proot, keys)
if err != nil {
return fmt.Errorf("error building verkle proof: %w", err)
}
}
// Validate the state root against the received state root and throw
// an error if they don't match.
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
return fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error())
}
if blockEw := block.ExecutionWitness(); blockEw != nil {
parent := v.bc.GetBlockByHash(header.ParentHash)
if parent == nil {
return fmt.Errorf("nil parent header for block %d", header.Number)
err := trie.AddPostValuesToProof(keys, statedb.GetTrie().(*trie.VerkleTrie), proof)
if err != nil {
return fmt.Errorf("error adding post values to proof: %w", err)
}
stateDiff, proof, err := beacon.BuildVerkleProof(header, statedb, parent.Root())
vp, stateDiff, err := verkle.SerializeProof(proof)
if err != nil {
return fmt.Errorf("error building verkle proof: %w", err)
return fmt.Errorf("error serializing proof: %w", err)
}
ew := types.ExecutionWitness{
StateDiff: stateDiff,
VerkleProof: proof,
ParentStateRoot: parent.Root(),
VerkleProof: vp,
ParentStateRoot: proot,
}
if err := ew.Equal(blockEw); err != nil {
return fmt.Errorf("invalid execution witness: %v", err)
Expand Down
32 changes: 21 additions & 11 deletions trie/verkle.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,22 +290,32 @@ func (trie *VerkleTrie) IsVerkle() bool {
return true
}

func ProveAndSerialize(pretrie, posttrie *VerkleTrie, keys [][]byte, resolver verkle.NodeResolverFn) (*verkle.VerkleProof, verkle.StateDiff, error) {
func AddPostValuesToProof(keys [][]byte, postroot *VerkleTrie, proof *verkle.Proof) error {
proof.PostValues = make([][]byte, len(keys))
if postroot != nil {
// keys were sorted already in the above GetcommitmentsForMultiproof.
// Set the post values, if they are untouched, leave them `nil`
for i := range keys {
val, err := postroot.root.Get(keys[i], nil)
if err != nil {
return fmt.Errorf("error getting post-state value for key %x: %w", keys[i], err)
}
if !bytes.Equal(proof.PreValues[i], val) {
proof.PostValues[i] = val
}
}
}

return nil
}

func Proof(pretrie, posttrie *VerkleTrie, keys [][]byte, resolver verkle.NodeResolverFn) (*verkle.Proof, 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
return proof, err
}

// ChunkedCode represents a sequence of 32-bytes chunks of code (31 bytes of which
Expand Down

0 comments on commit c284ea4

Please sign in to comment.