Skip to content

Commit 3c23956

Browse files
committed
all: implement EIP-compliant verkle trees
verkle: Implement Trie, NodeIterator and Database ifs Fix crash in TestDump Fix TestDump Fix TrieCopy remove unnecessary traces fix: Error() returned errIteratorEnd in verkle node iterator rewrite the iterator and change the signature of OpenStorageTrie add the adapter to reuse the account trie for storage don't try to deserialize a storage leaf into an account Fix statedb unit tests (#14) * debug code * Fix more unit tests * remove traces * Go back to the full range One tree to rule them all remove updateRoot, there is no root to update store code inside the account leaf fix build save current state for Sina Update go-verkle to latest Charge WITNESS_*_COST gas on storage loads Add witness costs for SSTORE as well Charge witness gas in the case of code execution corresponding code deletion add a --verkle flag to separate verkle experiments from regular geth operations use the snapshot to get data stateless execution from block witness AccessWitness functions Add block generation test + genesis snapshot generation test stateless block execution (#18) * test stateless block execution * Force tree resolution before generating the proof increased coverage in stateless test execution (#19) * test stateless block execution * Force tree resolution before generating the proof * increase coverage in stateless test execution ensure geth compiles fix issues in tests with verkle trees deactivated Ensure stateless data is available when executing statelessly (#20) * Ensure stateless data is available when executing statelessly * Actual execution of a statless block * bugfixes in stateless block execution * code cleanup - Reduce PR footprint by reverting NewEVM to its original signature - Move the access witness to the block context - prepare for a change in AW semantics Need to store the initial values. - Use the touch helper function, DRY * revert the signature of MustCommit to its original form (#21) fix leaf proofs in stateless execution (#22) * Fixes in witness pre-state * Add the recipient's nonce to the witness * reduce PR footprint and investigate issue in root state calculation * quick build fix cleanup: Remove extra parameter in ToBlock revert ToBlock to its older signature
1 parent f2491c5 commit 3c23956

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1592
-63
lines changed

cmd/geth/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ var (
150150
utils.MinerNotifyFullFlag,
151151
configFileFlag,
152152
utils.CatalystFlag,
153+
utils.VerkleFlag,
153154
}
154155

155156
rpcFlags = []cli.Flag{

cmd/geth/snapshot.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ func verifyState(ctx *cli.Context) error {
215215
log.Error("Failed to load head block")
216216
return errors.New("no head block")
217217
}
218-
snaptree, err := snapshot.New(chaindb, trie.NewDatabase(chaindb), 256, headBlock.Root(), false, false, false)
218+
snaptree, err := snapshot.New(chaindb, trie.NewDatabase(chaindb), 256, headBlock.Root(), false, false, false, false)
219219
if err != nil {
220220
log.Error("Failed to open snapshot tree", "err", err)
221221
return err
@@ -467,7 +467,7 @@ func dumpState(ctx *cli.Context) error {
467467
if err != nil {
468468
return err
469469
}
470-
snaptree, err := snapshot.New(db, trie.NewDatabase(db), 256, root, false, false, false)
470+
snaptree, err := snapshot.New(db, trie.NewDatabase(db), 256, root, false, false, false, false)
471471
if err != nil {
472472
return err
473473
}

cmd/geth/usage.go

+1
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
227227
utils.BloomFilterSizeFlag,
228228
cli.HelpFlag,
229229
utils.CatalystFlag,
230+
utils.VerkleFlag,
230231
},
231232
},
232233
}

cmd/utils/flags.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,11 @@ var (
775775
Name: "catalyst",
776776
Usage: "Catalyst mode (eth2 integration testing)",
777777
}
778+
779+
VerkleFlag = cli.BoolFlag{
780+
Name: "verkle",
781+
Usage: "Enable geth with verkle trees (EXPERIMENTAL)",
782+
}
778783
)
779784

780785
// MakeDataDir retrieves the currently requested data directory, terminating
@@ -1449,7 +1454,7 @@ func CheckExclusive(ctx *cli.Context, args ...interface{}) {
14491454
// SetEthConfig applies eth-related command line flags to the config.
14501455
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
14511456
// Avoid conflicting network flags
1452-
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, RopstenFlag, RinkebyFlag, GoerliFlag)
1457+
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, RopstenFlag, RinkebyFlag, GoerliFlag, VerkleFlag)
14531458
CheckExclusive(ctx, LightServeFlag, SyncModeFlag, "light")
14541459
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
14551460
if ctx.GlobalString(GCModeFlag.Name) == "archive" && ctx.GlobalUint64(TxLookupLimitFlag.Name) != 0 {
@@ -1584,6 +1589,13 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
15841589
}
15851590
cfg.Genesis = core.DefaultGenesisBlock()
15861591
SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash)
1592+
case ctx.GlobalBool(VerkleFlag.Name):
1593+
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
1594+
cfg.NetworkId = 86 // 'V'
1595+
}
1596+
cfg.Genesis = core.DefaultGenesisBlock()
1597+
cfg.Genesis.Config.UseVerkle = true
1598+
SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash)
15871599
case ctx.GlobalBool(RopstenFlag.Name):
15881600
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
15891601
cfg.NetworkId = 3

core/blockchain.go

+18-2
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
233233
Cache: cacheConfig.TrieCleanLimit,
234234
Journal: cacheConfig.TrieCleanJournal,
235235
Preimages: cacheConfig.Preimages,
236+
UseVerkle: chainConfig.UseVerkle,
236237
}),
237238
quit: make(chan struct{}),
238239
shouldPreserve: shouldPreserve,
@@ -370,7 +371,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
370371
log.Warn("Enabling snapshot recovery", "chainhead", head.NumberU64(), "diskbase", *layer)
371372
recover = true
372373
}
373-
bc.snaps, _ = snapshot.New(bc.db, bc.stateCache.TrieDB(), bc.cacheConfig.SnapshotLimit, head.Root(), !bc.cacheConfig.SnapshotWait, true, recover)
374+
bc.snaps, _ = snapshot.New(bc.db, bc.stateCache.TrieDB(), bc.cacheConfig.SnapshotLimit, head.Root(), !bc.cacheConfig.SnapshotWait, true, recover, bc.Config().UseVerkle)
374375
}
375376
// Take ownership of this particular state
376377
go bc.update()
@@ -1825,7 +1826,22 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
18251826
}
18261827
// Process block using the parent state as reference point
18271828
substart := time.Now()
1828-
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig)
1829+
var (
1830+
usedGas uint64
1831+
receipts types.Receipts
1832+
logs []*types.Log
1833+
)
1834+
if len(block.Header().VerkleProof) == 0 {
1835+
receipts, logs, _, usedGas, err = bc.processor.Process(block, statedb, bc.vmConfig)
1836+
} else {
1837+
var leaves map[common.Hash]common.Hash
1838+
_, _, _, leaves, err = trie.DeserializeVerkleProof(block.Header().VerkleProof)
1839+
if err != nil {
1840+
return it.index, err
1841+
}
1842+
statedb.SetStateless(leaves)
1843+
receipts, logs, usedGas, err = bc.processor.ProcessStateless(block, statedb, bc.vmConfig, leaves)
1844+
}
18291845
if err != nil {
18301846
bc.reportBlock(block, receipts, err)
18311847
atomic.StoreUint32(&followupInterrupt, 1)

core/blockchain_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
153153
if err != nil {
154154
return err
155155
}
156-
receipts, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{})
156+
receipts, _, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{})
157157
if err != nil {
158158
blockchain.reportBlock(block, receipts, err)
159159
return err

core/chain_makers.go

+94-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/ethereum/go-ethereum/core/vm"
2929
"github.com/ethereum/go-ethereum/ethdb"
3030
"github.com/ethereum/go-ethereum/params"
31+
"github.com/ethereum/go-ethereum/trie"
3132
)
3233

3334
// BlockGen creates blocks for testing.
@@ -43,6 +44,7 @@ type BlockGen struct {
4344
txs []*types.Transaction
4445
receipts []*types.Receipt
4546
uncles []*types.Header
47+
witness *types.AccessWitness
4648

4749
config *params.ChainConfig
4850
engine consensus.Engine
@@ -103,10 +105,17 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
103105
b.SetCoinbase(common.Address{})
104106
}
105107
b.statedb.Prepare(tx.Hash(), len(b.txs))
106-
receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{})
108+
receipt, accesses, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{})
107109
if err != nil {
108110
panic(err)
109111
}
112+
if accesses != nil {
113+
if b.witness != nil {
114+
b.witness.Merge(accesses)
115+
} else {
116+
b.witness = accesses
117+
}
118+
}
110119
b.txs = append(b.txs, tx)
111120
b.receipts = append(b.receipts, receipt)
112121
}
@@ -250,6 +259,90 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
250259
return blocks, receipts
251260
}
252261

262+
func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
263+
if config == nil {
264+
config = params.TestChainConfig
265+
}
266+
blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
267+
chainreader := &fakeChainReader{config: config}
268+
genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) {
269+
b := &BlockGen{i: i, chain: blocks, parent: parent, statedb: statedb, config: config, engine: engine, witness: types.NewAccessWitness()}
270+
b.header = makeHeader(chainreader, parent, statedb, b.engine)
271+
272+
// Mutate the state and block according to any hard-fork specs
273+
if daoBlock := config.DAOForkBlock; daoBlock != nil {
274+
limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)
275+
if b.header.Number.Cmp(daoBlock) >= 0 && b.header.Number.Cmp(limit) < 0 {
276+
if config.DAOForkSupport {
277+
b.header.Extra = common.CopyBytes(params.DAOForkBlockExtra)
278+
}
279+
}
280+
}
281+
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(b.header.Number) == 0 {
282+
misc.ApplyDAOHardFork(statedb)
283+
}
284+
// Execute any user modifications to the block
285+
if gen != nil {
286+
gen(i, b)
287+
}
288+
if b.engine != nil {
289+
// Finalize and seal the block
290+
block, err := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts)
291+
if err != nil {
292+
panic(err)
293+
}
294+
295+
// Write state changes to db
296+
root, err := statedb.Commit(config.IsEIP158(b.header.Number))
297+
if err != nil {
298+
panic(fmt.Sprintf("state write error: %v", err))
299+
}
300+
if err := statedb.Database().TrieDB().Commit(root, false, nil); err != nil {
301+
panic(fmt.Sprintf("trie write error: %v", err))
302+
}
303+
304+
// Generate an associated verkle proof
305+
if tr := statedb.GetTrie(); tr.IsVerkle() {
306+
vtr := tr.(*trie.VerkleTrie)
307+
// Generate the proof if we are using a verkle tree
308+
// WORKAROUND: make sure all keys are resolved
309+
// before building the proof. Ultimately, node
310+
// resolution can be done with a prefetcher or
311+
// from GetCommitmentsAlongPath.
312+
keys := b.witness.Keys()
313+
for _, key := range keys {
314+
out, err := vtr.TryGet(key)
315+
if err != nil {
316+
panic(err)
317+
}
318+
if len(out) == 0 {
319+
panic(fmt.Sprintf("%x should be present in the tree", key))
320+
}
321+
}
322+
vtr.Hash()
323+
_, err := vtr.ProveAndSerialize(keys, b.witness.KeyVals())
324+
//block.SetVerkleProof(p)
325+
if err != nil {
326+
panic(err)
327+
}
328+
}
329+
return block, b.receipts
330+
}
331+
return nil, nil
332+
}
333+
for i := 0; i < n; i++ {
334+
statedb, err := state.New(parent.Root(), state.NewDatabaseWithConfig(db, &trie.Config{UseVerkle: true}), nil)
335+
if err != nil {
336+
panic(err)
337+
}
338+
block, receipt := genblock(i, parent, statedb)
339+
blocks[i] = block
340+
receipts[i] = receipt
341+
parent = block
342+
}
343+
return blocks, receipts
344+
}
345+
253346
func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.StateDB, engine consensus.Engine) *types.Header {
254347
var time uint64
255348
if parent.Time() == 0 {

core/evm.go

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ func NewEVMTxContext(msg Message) vm.TxContext {
6969
return vm.TxContext{
7070
Origin: msg.From(),
7171
GasPrice: new(big.Int).Set(msg.GasPrice()),
72+
Accesses: types.NewAccessWitness(),
7273
}
7374
}
7475

core/genesis.go

+18-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/ethereum/go-ethereum/common/math"
3131
"github.com/ethereum/go-ethereum/core/rawdb"
3232
"github.com/ethereum/go-ethereum/core/state"
33+
"github.com/ethereum/go-ethereum/core/state/snapshot"
3334
"github.com/ethereum/go-ethereum/core/types"
3435
"github.com/ethereum/go-ethereum/crypto"
3536
"github.com/ethereum/go-ethereum/ethdb"
@@ -256,10 +257,18 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
256257
// ToBlock creates the genesis block and writes state of a genesis specification
257258
// to the given database (or discards it if nil).
258259
func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
260+
return g.ToBlockWithSnaps(db, nil)
261+
}
262+
263+
func (g *Genesis) ToBlockWithSnaps(db ethdb.Database, snaps *snapshot.Tree) *types.Block {
259264
if db == nil {
260265
db = rawdb.NewMemoryDatabase()
261266
}
262-
statedb, err := state.New(common.Hash{}, state.NewDatabase(db), nil)
267+
var trieCfg *trie.Config
268+
if g.Config != nil {
269+
trieCfg = &trie.Config{UseVerkle: g.Config.UseVerkle}
270+
}
271+
statedb, err := state.New(common.Hash{}, state.NewDatabaseWithConfig(db, trieCfg), snaps)
263272
if err != nil {
264273
panic(err)
265274
}
@@ -301,14 +310,21 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
301310
}
302311
statedb.Commit(false)
303312
statedb.Database().TrieDB().Commit(root, true, nil)
313+
if err := statedb.Cap(root); err != nil {
314+
panic(err)
315+
}
304316

305317
return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil))
306318
}
307319

308320
// Commit writes the block and state of a genesis specification to the database.
309321
// The block is committed as the canonical head block.
310322
func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) {
311-
block := g.ToBlock(db)
323+
return g.CommitWithSnaps(db, nil)
324+
}
325+
326+
func (g *Genesis) CommitWithSnaps(db ethdb.Database, snaps *snapshot.Tree) (*types.Block, error) {
327+
block := g.ToBlockWithSnaps(db, snaps)
312328
if block.Number().Sign() != 0 {
313329
return nil, errors.New("can't commit genesis block with number > 0")
314330
}

0 commit comments

Comments
 (0)