Skip to content

Commit e1dd112

Browse files
committed
all: simplify witness and integrate it into live geth
1 parent d86b650 commit e1dd112

40 files changed

+748
-897
lines changed

cmd/evm/blockrunner.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func blockTestCmd(ctx *cli.Context) error {
8686
continue
8787
}
8888
test := tests[name]
89-
if err := test.Run(false, rawdb.HashScheme, tracer, func(res error, chain *core.BlockChain) {
89+
if err := test.Run(false, rawdb.HashScheme, false, tracer, func(res error, chain *core.BlockChain) {
9090
if ctx.Bool(DumpFlag.Name) {
9191
if state, _ := chain.State(); state != nil {
9292
fmt.Println(string(state.Dump(nil)))

cmd/utils/stateless/stateless.go

Lines changed: 0 additions & 86 deletions
This file was deleted.

core/block_validator.go

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/ethereum/go-ethereum/common"
2424
"github.com/ethereum/go-ethereum/consensus"
2525
"github.com/ethereum/go-ethereum/core/state"
26+
"github.com/ethereum/go-ethereum/core/stateless"
2627
"github.com/ethereum/go-ethereum/core/types"
2728
"github.com/ethereum/go-ethereum/params"
2829
"github.com/ethereum/go-ethereum/trie"
@@ -35,14 +36,12 @@ import (
3536
type BlockValidator struct {
3637
config *params.ChainConfig // Chain configuration options
3738
bc *BlockChain // Canonical block chain
38-
engine consensus.Engine // Consensus engine used for validating
3939
}
4040

4141
// NewBlockValidator returns a new block validator which is safe for re-use
42-
func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engine consensus.Engine) *BlockValidator {
42+
func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain) *BlockValidator {
4343
validator := &BlockValidator{
4444
config: config,
45-
engine: engine,
4645
bc: blockchain,
4746
}
4847
return validator
@@ -60,7 +59,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
6059
// Header validity is known at this point. Here we verify that uncles, transactions
6160
// and withdrawals given in the block body match the header.
6261
header := block.Header()
63-
if err := v.engine.VerifyUncles(v.bc, block); err != nil {
62+
if err := v.bc.engine.VerifyUncles(v.bc, block); err != nil {
6463
return err
6564
}
6665
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
@@ -122,31 +121,55 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
122121

123122
// ValidateState validates the various changes that happen after a state transition,
124123
// such as amount of used gas, the receipt roots and the state root itself.
125-
// If validateRemoteRoot is false, the provided block header's root is not asserted to be equal to the one computed from
126-
// execution.
127-
func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64, checkRemoteRoot bool) (root common.Hash, err error) {
124+
func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64, stateless bool) error {
128125
header := block.Header()
129126
if block.GasUsed() != usedGas {
130-
return root, fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas)
127+
return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas)
131128
}
132129
// Validate the received block's bloom with the one derived from the generated receipts.
133130
// For valid blocks this should always validate to true.
134131
rbloom := types.CreateBloom(receipts)
135132
if rbloom != header.Bloom {
136-
return root, fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
133+
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
134+
}
135+
// In stateless mode, return early because the receipt and state root are not
136+
// provided through the witness, rather the cross validator needs to return it.
137+
if stateless {
138+
return nil
137139
}
138140
// The receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]]))
139141
receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil))
140142
if receiptSha != header.ReceiptHash {
141-
return root, fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
143+
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
142144
}
143-
// Compute the state root and if enabled, check it against the
144-
// received state root and throw an error if they don't match.
145-
root = statedb.IntermediateRoot(v.config.IsEIP158(header.Number))
146-
if checkRemoteRoot && header.Root != root {
147-
return root, fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error())
145+
// Validate the state root against the received state root and throw
146+
// an error if they don't match.
147+
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
148+
return fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error())
148149
}
149-
return root, nil
150+
return nil
151+
}
152+
153+
// ValidateWitness cross validates a block execution with stateless remote clients.
154+
//
155+
// Normally we'd distribute the block witness to remote cross validators, wait
156+
// for them to respond and then merge the results. For now, however, it's only
157+
// Geth, so do an internal stateless run.
158+
func (v *BlockValidator) ValidateWitness(witness *stateless.Witness, receiptRoot common.Hash, stateRoot common.Hash) error {
159+
// Run the cross client stateless execution
160+
// TODO(karalabe): Self-stateless for now, swap with other clients
161+
crossReceiptRoot, crossStateRoot, err := ExecuteStateless(v.config, witness)
162+
if err != nil {
163+
return fmt.Errorf("stateless execution failed: %v", err)
164+
}
165+
// Stateless cross execution suceeeded, validate the withheld computed fields
166+
if crossReceiptRoot != receiptRoot {
167+
return fmt.Errorf("cross validator receipt root mismatch (cross: %x local: %x)", crossReceiptRoot, receiptRoot)
168+
}
169+
if crossStateRoot != stateRoot {
170+
return fmt.Errorf("cross validator state root mismatch (cross: %x local: %x)", crossStateRoot, stateRoot)
171+
}
172+
return nil
150173
}
151174

152175
// CalcGasLimit computes the gas limit of the next block after parent. It aims

core/blockchain.go

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"github.com/ethereum/go-ethereum/core/rawdb"
3838
"github.com/ethereum/go-ethereum/core/state"
3939
"github.com/ethereum/go-ethereum/core/state/snapshot"
40+
"github.com/ethereum/go-ethereum/core/stateless"
4041
"github.com/ethereum/go-ethereum/core/tracing"
4142
"github.com/ethereum/go-ethereum/core/types"
4243
"github.com/ethereum/go-ethereum/core/vm"
@@ -302,18 +303,18 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
302303
vmConfig: vmConfig,
303304
logger: vmConfig.Tracer,
304305
}
305-
bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit))
306-
bc.forker = NewForkChoice(bc, shouldPreserve)
307-
bc.stateCache = state.NewDatabaseWithNodeDB(bc.db, bc.triedb)
308-
bc.validator = NewBlockValidator(chainConfig, bc, engine)
309-
bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine)
310-
bc.processor = NewStateProcessor(chainConfig, bc, engine)
311-
312306
var err error
313307
bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.insertStopped)
314308
if err != nil {
315309
return nil, err
316310
}
311+
bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit))
312+
bc.forker = NewForkChoice(bc, shouldPreserve)
313+
bc.stateCache = state.NewDatabaseWithNodeDB(bc.db, bc.triedb)
314+
bc.validator = NewBlockValidator(chainConfig, bc)
315+
bc.prefetcher = newStatePrefetcher(chainConfig, bc.hc)
316+
bc.processor = NewStateProcessor(chainConfig, bc.hc)
317+
317318
bc.genesisBlock = bc.GetBlockByNumber(0)
318319
if bc.genesisBlock == nil {
319320
return nil, ErrNoGenesis
@@ -1809,7 +1810,14 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
18091810
// while processing transactions. Before Byzantium the prefetcher is mostly
18101811
// useless due to the intermediate root hashing after each transaction.
18111812
if bc.chainConfig.IsByzantium(block.Number()) {
1812-
statedb.StartPrefetcher("chain", !bc.vmConfig.EnableWitnessCollection)
1813+
var witness *stateless.Witness
1814+
if bc.vmConfig.EnableWitnessCollection {
1815+
witness, err = stateless.NewWitness(bc, block)
1816+
if err != nil {
1817+
return it.index, err
1818+
}
1819+
}
1820+
statedb.StartPrefetcher("chain", witness)
18131821
}
18141822
activeState = statedb
18151823

@@ -1916,19 +1924,26 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
19161924

19171925
// Process block using the parent state as reference point
19181926
pstart := time.Now()
1919-
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig, nil)
1927+
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig)
19201928
if err != nil {
19211929
bc.reportBlock(block, receipts, err)
19221930
return nil, err
19231931
}
19241932
ptime := time.Since(pstart)
19251933

19261934
vstart := time.Now()
1927-
if _, err := bc.validator.ValidateState(block, statedb, receipts, usedGas, true); err != nil {
1935+
if err := bc.validator.ValidateState(block, statedb, receipts, usedGas, false); err != nil {
19281936
bc.reportBlock(block, receipts, err)
19291937
return nil, err
19301938
}
19311939
vtime := time.Since(vstart)
1940+
1941+
if witness := statedb.Witness(); witness != nil {
1942+
if err = bc.validator.ValidateWitness(witness, block.ReceiptHash(), block.Root()); err != nil {
1943+
bc.reportBlock(block, receipts, err)
1944+
return nil, fmt.Errorf("cross verification failed: %v", err)
1945+
}
1946+
}
19321947
proctime := time.Since(start) // processing + validation
19331948

19341949
// Update the metrics touched during block processing and validation

core/blockchain_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,12 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
163163
if err != nil {
164164
return err
165165
}
166-
receipts, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{}, nil)
166+
receipts, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{})
167167
if err != nil {
168168
blockchain.reportBlock(block, receipts, err)
169169
return err
170170
}
171-
_, err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, true)
172-
if err != nil {
171+
if err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, false); err != nil {
173172
blockchain.reportBlock(block, receipts, err)
174173
return err
175174
}

core/chain_makers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func (b *BlockGen) Difficulty() *big.Int {
9999
func (b *BlockGen) SetParentBeaconRoot(root common.Hash) {
100100
b.header.ParentBeaconRoot = &root
101101
var (
102-
blockContext = NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase, nil)
102+
blockContext = NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
103103
vmenv = vm.NewEVM(blockContext, vm.TxContext{}, b.statedb, b.cm.config, vm.Config{})
104104
)
105105
ProcessBeaconBlockRoot(root, vmenv, b.statedb)

core/evm.go

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ package core
1919
import (
2020
"math/big"
2121

22-
"github.com/ethereum/go-ethereum/core/state"
23-
2422
"github.com/ethereum/go-ethereum/common"
2523
"github.com/ethereum/go-ethereum/consensus"
2624
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
@@ -40,9 +38,8 @@ type ChainContext interface {
4038
GetHeader(common.Hash, uint64) *types.Header
4139
}
4240

43-
// NewEVMBlockContext creates a new context for use in the EVM. If witness is non-nil, the context sources block hashes
44-
// for the BLOCKHASH opcode from the witness.
45-
func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address, witness *state.Witness) vm.BlockContext {
41+
// NewEVMBlockContext creates a new context for use in the EVM.
42+
func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address) vm.BlockContext {
4643
var (
4744
beneficiary common.Address
4845
baseFee *big.Int
@@ -65,18 +62,10 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
6562
if header.Difficulty.Sign() == 0 {
6663
random = &header.MixDigest
6764
}
68-
var getHash vm.GetHashFunc
69-
if witness != nil {
70-
getHash = func(n uint64) common.Hash {
71-
return witness.BlockHash(n)
72-
}
73-
} else {
74-
getHash = GetHashFn(header, chain)
75-
}
7665
return vm.BlockContext{
7766
CanTransfer: CanTransfer,
7867
Transfer: Transfer,
79-
GetHash: getHash,
68+
GetHash: GetHashFn(header, chain),
8069
Coinbase: beneficiary,
8170
BlockNumber: new(big.Int).Set(header.Number),
8271
Time: header.Time,

core/state/database.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,9 @@ type Trie interface {
125125
// be created with new root and updated trie database for following usage
126126
Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet)
127127

128-
// AccessList returns a map of path->blob containing all trie nodes that have
129-
// been accessed.
130-
AccessList() map[string][]byte
128+
// Witness returns a set containing all trie nodes that have been accessed.
129+
// The returned map could be nil if the witness is empty.
130+
Witness() map[string]struct{}
131131

132132
// NodeIterator returns an iterator that returns nodes of the trie. Iteration
133133
// starts at the key after the given start key. And error will be returned

0 commit comments

Comments
 (0)