Skip to content

Commit ce6c94a

Browse files
authored
Include base -> overlay key-values migration logic (#199)
* mod: add go-verkle version with key-value migration new apis Signed-off-by: Ignacio Hagopian <[email protected]> * core/stateprocessor: use constant for max number of migrated key-values Signed-off-by: Ignacio Hagopian <[email protected]> * core: add base->overlay key-values migration logic Signed-off-by: Ignacio Hagopian <[email protected]> * core: fix some compiler errors Signed-off-by: Ignacio Hagopian <[email protected]> * trie: consider removing transition trie api in the future Signed-off-by: Ignacio Hagopian <[email protected]> * mod: use latest go-verkle Signed-off-by: Ignacio Hagopian <[email protected]> --------- Signed-off-by: Ignacio Hagopian <[email protected]>
1 parent 22334e5 commit ce6c94a

File tree

5 files changed

+153
-25
lines changed

5 files changed

+153
-25
lines changed

core/state_processor.go

+139-10
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ package core
1818

1919
import (
2020
"bytes"
21+
"encoding/binary"
2122
"fmt"
2223
"math/big"
24+
"time"
2325

2426
"github.com/ethereum/go-ethereum/common"
2527
"github.com/ethereum/go-ethereum/consensus"
@@ -33,6 +35,9 @@ import (
3335
"github.com/ethereum/go-ethereum/log"
3436
"github.com/ethereum/go-ethereum/params"
3537
"github.com/ethereum/go-ethereum/trie"
38+
tutils "github.com/ethereum/go-ethereum/trie/utils"
39+
"github.com/gballet/go-verkle"
40+
"github.com/holiman/uint256"
3641
)
3742

3843
// StateProcessor is a basic Processor, which takes care of transitioning
@@ -96,6 +101,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
96101
// N values from the MPT into the verkle tree.
97102
if fdb, ok := statedb.Database().(*state.ForkingDB); ok {
98103
if fdb.InTransition() {
104+
now := time.Now()
99105
// XXX overkill, just save the parent root in the forking db
100106
tt := statedb.GetTrie().(*trie.TransitionTrie)
101107
mpt := tt.Base()
@@ -109,17 +115,22 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
109115
return nil, nil, 0, err
110116
}
111117

112-
// move N=500 accounts into the verkle tree, starting with the
118+
const maxMovedCount = 500
119+
// mkv will be assiting in the collection of up to maxMovedCount key values to be migrated to the VKT.
120+
// It has internal caches to do efficient MPT->VKT key calculations, which will be discarded after
121+
// this function.
122+
mkv := &keyValueMigrator{}
123+
// move maxCount accounts into the verkle tree, starting with the
113124
// slots from the previous account.
114125
count := 0
115-
for ; stIt.Next() && count < 500; count++ {
126+
addr := rawdb.ReadPreimage(statedb.Database().DiskDB(), accIt.Hash())
127+
for ; stIt.Next() && count < maxMovedCount; count++ {
116128
slotnr := rawdb.ReadPreimage(statedb.Database().DiskDB(), stIt.Hash())
117-
118-
// @jsign: do your magic here adding the slot `slotnr`
129+
mkv.addStorageSlot(addr, slotnr, stIt.Slot())
119130
}
120131

121-
// if less than 500 slots were moved, move to the next account
122-
for count < 500 {
132+
// if less than maxCount slots were moved, move to the next account
133+
for count < maxMovedCount {
123134
if accIt.Next() {
124135
acc, err := snapshot.FullAccount(accIt.Account())
125136
if err != nil {
@@ -128,21 +139,21 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
128139
}
129140
addr := rawdb.ReadPreimage(statedb.Database().DiskDB(), accIt.Hash())
130141

131-
// @jsign: do your magic here adding the account at `addr
142+
mkv.addAccount(addr, acc)
132143

133144
// Store the account code if present
134145
if !bytes.Equal(acc.CodeHash, emptyCode) {
135146
code := rawdb.ReadCode(statedb.Database().DiskDB(), common.BytesToHash(acc.CodeHash))
136147
chunks := trie.ChunkifyCode(code)
137148

138-
// @jsign: do your magic here with the code chunks
149+
mkv.addAccountCode(addr, uint64(len(code)), chunks)
139150
}
140151

141152
if !bytes.Equal(acc.Root, emptyRoot[:]) {
142-
for ; stIt.Next() && count < 500; count++ {
153+
for ; stIt.Next() && count < maxMovedCount; count++ {
143154
slotnr := rawdb.ReadPreimage(statedb.Database().DiskDB(), stIt.Hash())
144155

145-
// @jsign do your magic here with extra slots
156+
mkv.addStorageSlot(addr, slotnr, stIt.Slot())
146157
}
147158
}
148159
}
@@ -157,6 +168,13 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
157168
fdb.LastAccHash = accIt.Hash()
158169
fdb.LastSlotHash = stIt.Hash()
159170
}
171+
log.Info("Collected and prepared key values from base tree", "count", count, "duration", time.Since(now))
172+
173+
now = time.Now()
174+
if err := mkv.migrateCollectedKeyValues(tt.Overlay()); err != nil {
175+
return nil, nil, 0, fmt.Errorf("could not migrate key values: %w", err)
176+
}
177+
log.Info("Inserted key values in overlay tree", "count", count, "duration", time.Since(now))
160178
}
161179
}
162180

@@ -232,3 +250,114 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
232250
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg)
233251
return applyTransaction(msg, config, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
234252
}
253+
254+
// keyValueMigrator is a helper struct that collects key-values from the base tree.
255+
// The walk is done in account order, so **we assume** the APIs hold this invariant. This is
256+
// useful to be smart about caching banderwagon.Points to make VKT key calculations faster.
257+
type keyValueMigrator struct {
258+
currAddr []byte
259+
currAddrPoint *verkle.Point
260+
261+
vktLeafData map[string]*verkle.BatchNewLeafNodeData
262+
}
263+
264+
func (kvm *keyValueMigrator) addStorageSlot(addr []byte, slotNumber []byte, slotValue []byte) {
265+
addrPoint := kvm.getAddrPoint(addr)
266+
267+
vktKey := tutils.GetTreeKeyStorageSlotWithEvaluatedAddress(addrPoint, slotNumber)
268+
leafNodeData := kvm.getOrInitLeafNodeData(vktKey)
269+
270+
leafNodeData.Values[vktKey[verkle.StemSize]] = slotValue
271+
}
272+
273+
func (kvm *keyValueMigrator) addAccount(addr []byte, acc snapshot.Account) {
274+
addrPoint := kvm.getAddrPoint(addr)
275+
276+
vktKey := tutils.GetTreeKeyVersionWithEvaluatedAddress(addrPoint)
277+
leafNodeData := kvm.getOrInitLeafNodeData(vktKey)
278+
279+
var version [verkle.LeafValueSize]byte
280+
leafNodeData.Values[tutils.VersionLeafKey] = version[:]
281+
282+
var balance [verkle.LeafValueSize]byte
283+
for i, b := range acc.Balance.Bytes() {
284+
balance[len(acc.Balance.Bytes())-1-i] = b
285+
}
286+
leafNodeData.Values[tutils.BalanceLeafKey] = balance[:]
287+
288+
var nonce [verkle.LeafValueSize]byte
289+
binary.LittleEndian.PutUint64(nonce[:8], acc.Nonce)
290+
leafNodeData.Values[tutils.NonceLeafKey] = balance[:]
291+
292+
leafNodeData.Values[tutils.CodeKeccakLeafKey] = acc.CodeHash[:]
293+
294+
// Code size is ignored here. If this isn't an EOA, the tree-walk will call
295+
// addAccountCode with this information.
296+
}
297+
298+
func (kvm *keyValueMigrator) addAccountCode(addr []byte, codeSize uint64, chunks []byte) {
299+
addrPoint := kvm.getAddrPoint(addr)
300+
301+
vktKey := tutils.GetTreeKeyVersionWithEvaluatedAddress(addrPoint)
302+
leafNodeData := kvm.getOrInitLeafNodeData(vktKey)
303+
304+
// Save the code size.
305+
var codeSizeBytes [verkle.LeafValueSize]byte
306+
binary.LittleEndian.PutUint64(codeSizeBytes[:8], codeSize)
307+
leafNodeData.Values[tutils.CodeSizeLeafKey] = codeSizeBytes[:]
308+
309+
// The first 128 chunks are stored in the account header leaf.
310+
for i := 0; i < 128 && i < len(chunks)/32; i++ {
311+
leafNodeData.Values[byte(128+i)] = chunks[32*i : 32*(i+1)]
312+
}
313+
314+
// Potential further chunks, have their own leaf nodes.
315+
for i := 128; i < len(chunks)/32; {
316+
vktKey := tutils.GetTreeKeyCodeChunkWithEvaluatedAddress(addrPoint, uint256.NewInt(uint64(i)))
317+
leafNodeData := kvm.getOrInitLeafNodeData(vktKey)
318+
319+
j := i
320+
for ; (j-i) < 256 && j < len(chunks)/32; j++ {
321+
leafNodeData.Values[byte((j-128)%256)] = chunks[32*j : 32*(j+1)]
322+
}
323+
i = j
324+
}
325+
}
326+
327+
func (kvm *keyValueMigrator) getAddrPoint(addr []byte) *verkle.Point {
328+
if bytes.Equal(addr, kvm.currAddr) {
329+
return kvm.currAddrPoint
330+
}
331+
kvm.currAddr = addr
332+
kvm.currAddrPoint = tutils.EvaluateAddressPoint(addr)
333+
return kvm.currAddrPoint
334+
}
335+
336+
func (kvm *keyValueMigrator) getOrInitLeafNodeData(stem []byte) *verkle.BatchNewLeafNodeData {
337+
stemStr := string(stem)
338+
if _, ok := kvm.vktLeafData[stemStr]; !ok {
339+
kvm.vktLeafData[stemStr] = &verkle.BatchNewLeafNodeData{
340+
Stem: stem,
341+
Values: make(map[byte][]byte),
342+
}
343+
}
344+
return kvm.vktLeafData[stemStr]
345+
}
346+
347+
func (kvm *keyValueMigrator) migrateCollectedKeyValues(tree *trie.VerkleTrie) error {
348+
// Transform the map into a slice.
349+
nodeValues := make([]verkle.BatchNewLeafNodeData, 0, len(kvm.vktLeafData))
350+
for _, vld := range kvm.vktLeafData {
351+
nodeValues = append(nodeValues, *vld)
352+
}
353+
354+
// Create all leaves in batch mode so we can optimize cryptography operations.
355+
newLeaves := verkle.BatchNewLeafNode(nodeValues)
356+
357+
// Insert into the tree.
358+
if err := tree.InsertMigratedLeaves(newLeaves); err != nil {
359+
return fmt.Errorf("failed to insert migrated leaves: %w", err)
360+
}
361+
362+
return nil
363+
}

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ require (
2323
github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c
2424
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
2525
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff
26-
github.com/gballet/go-verkle v0.0.0-20230414192453-2838510d5ee0
26+
github.com/gballet/go-verkle v0.0.0-20230424151626-de802a6b19f8
2727
github.com/go-stack/stack v1.8.0
2828
github.com/golang-jwt/jwt/v4 v4.3.0
2929
github.com/golang/protobuf v1.5.2

go.sum

+2-14
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,6 @@ github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1
8686
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
8787
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
8888
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
89-
github.com/crate-crypto/go-ipa v0.0.0-20230315201338-1643fdc2ead8 h1:2EBbIwPDRqlCD2K34Eojyy0x9d3RhOuHAZfbQm508X8=
90-
github.com/crate-crypto/go-ipa v0.0.0-20230315201338-1643fdc2ead8/go.mod h1:gzbVz57IDJgQ9rLQwfSk696JGWof8ftznEL9GoAv3NI=
9189
github.com/crate-crypto/go-ipa v0.0.0-20230410135559-ce4a96995014 h1:bbyTlFQ12wkFA6aVL+9HrBZwVl85AN0VS/Bwam7o93U=
9290
github.com/crate-crypto/go-ipa v0.0.0-20230410135559-ce4a96995014/go.mod h1:gzbVz57IDJgQ9rLQwfSk696JGWof8ftznEL9GoAv3NI=
9391
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -137,16 +135,8 @@ github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgx
137135
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8=
138136
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
139137
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
140-
github.com/gballet/go-verkle v0.0.0-20230317174103-141354da6b11 h1:x4hiQFgr1SlqR4IoAZiXLFZK4L7KbibqkORqa1fwKp8=
141-
github.com/gballet/go-verkle v0.0.0-20230317174103-141354da6b11/go.mod h1:IyOnn1kujMWaT+wet/6Ix1BtvYwateOBy9puuWH/8sw=
142-
github.com/gballet/go-verkle v0.0.0-20230412090410-4015adc3d072 h1:gKcktHMBKLdtCSZnaG8tv9bFG80p1tp7MjU1Uvl9nag=
143-
github.com/gballet/go-verkle v0.0.0-20230412090410-4015adc3d072/go.mod h1:P3bwGrLhsUNIsUDlq2yzMPvO1c/15oiB3JS85P+hNfw=
144-
github.com/gballet/go-verkle v0.0.0-20230413104310-bd8d6d33de95 h1:s8p8L/dQVmr/mgMjGIsGnnpvJMYCdfv4GHadLd/ALug=
145-
github.com/gballet/go-verkle v0.0.0-20230413104310-bd8d6d33de95/go.mod h1:P3bwGrLhsUNIsUDlq2yzMPvO1c/15oiB3JS85P+hNfw=
146-
github.com/gballet/go-verkle v0.0.0-20230413135631-4bea2763ed0f h1:gP4uR2/1qx6hsIzbRI28JWcsVuP7xyjyj6SpLnoFobc=
147-
github.com/gballet/go-verkle v0.0.0-20230413135631-4bea2763ed0f/go.mod h1:P3bwGrLhsUNIsUDlq2yzMPvO1c/15oiB3JS85P+hNfw=
148-
github.com/gballet/go-verkle v0.0.0-20230414192453-2838510d5ee0 h1:ENyj6hcn+dtO8iJ1GTzM/gkhdrAFqMi65Yf99cppdPA=
149-
github.com/gballet/go-verkle v0.0.0-20230414192453-2838510d5ee0/go.mod h1:P3bwGrLhsUNIsUDlq2yzMPvO1c/15oiB3JS85P+hNfw=
138+
github.com/gballet/go-verkle v0.0.0-20230424151626-de802a6b19f8 h1:UHRmPcIjYxqcS070yG9OVbr9aPfC/ToIBwRakFxQC9Y=
139+
github.com/gballet/go-verkle v0.0.0-20230424151626-de802a6b19f8/go.mod h1:P3bwGrLhsUNIsUDlq2yzMPvO1c/15oiB3JS85P+hNfw=
150140
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
151141
github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
152142
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -564,8 +554,6 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc
564554
golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
565555
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
566556
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
567-
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
568-
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
569557
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
570558
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
571559
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=

trie/transition.go

+5
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ func (t *TransitionTrie) Base() *SecureTrie {
3838
return t.base
3939
}
4040

41+
// TODO(gballet/jsign): consider removing this API.
42+
func (t *TransitionTrie) Overlay() *VerkleTrie {
43+
return t.overlay
44+
}
45+
4146
// GetKey returns the sha3 preimage of a hashed key that was previously used
4247
// to store a value.
4348
//

trie/verkle.go

+6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ func NewVerkleTrie(root verkle.VerkleNode, db *Database, pointCache *utils.Point
5050
}
5151
}
5252

53+
func (trie *VerkleTrie) InsertMigratedLeaves(leaves []verkle.LeafNode) error {
54+
return trie.root.(*verkle.InternalNode).InsertMigratedLeaves(leaves, func(hash []byte) ([]byte, error) {
55+
return trie.db.diskdb.Get(hash)
56+
})
57+
}
58+
5359
var errInvalidProof = errors.New("invalid proof")
5460

5561
// GetKey returns the sha3 preimage of a hashed key that was previously used

0 commit comments

Comments
 (0)