consensus/beacon, core/{state,types}: add verkle witness builder

This commit is contained in:
Guillaume Ballet 2024-07-05 21:54:00 +02:00 committed by Felix Lange
parent ea3b5095f4
commit 08cc404362
7 changed files with 228 additions and 112 deletions

View file

@ -34,6 +34,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
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,6 +59,7 @@ 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)
}
@ -81,6 +83,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
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
}

View file

@ -76,6 +76,7 @@ type ExecutableData struct {
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 {
@ -279,6 +281,7 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.
Withdrawals: block.Withdrawals(),
BlobGasUsed: block.BlobGasUsed(),
ExcessBlobGas: block.ExcessBlobGas(),
ExecutionWitness: block.ExecutionWitness(),
}
bundle := BlobsBundleV1{
Commitments: make([]hexutil.Bytes, 0),

View file

@ -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

View file

@ -131,6 +131,7 @@ type StateDB struct {
// Per-transaction access list
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
}

View file

@ -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

View file

@ -36,6 +36,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
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
@ -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)
}
@ -86,6 +88,7 @@ func (h *Header) UnmarshalJSON(input []byte) error {
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
}

View file

@ -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