@@ -18,8 +18,10 @@ package core
18
18
19
19
import (
20
20
"bytes"
21
+ "encoding/binary"
21
22
"fmt"
22
23
"math/big"
24
+ "time"
23
25
24
26
"github.com/ethereum/go-ethereum/common"
25
27
"github.com/ethereum/go-ethereum/consensus"
@@ -33,6 +35,9 @@ import (
33
35
"github.com/ethereum/go-ethereum/log"
34
36
"github.com/ethereum/go-ethereum/params"
35
37
"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"
36
41
)
37
42
38
43
// 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
96
101
// N values from the MPT into the verkle tree.
97
102
if fdb , ok := statedb .Database ().(* state.ForkingDB ); ok {
98
103
if fdb .InTransition () {
104
+ now := time .Now ()
99
105
// XXX overkill, just save the parent root in the forking db
100
106
tt := statedb .GetTrie ().(* trie.TransitionTrie )
101
107
mpt := tt .Base ()
@@ -109,17 +115,22 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
109
115
return nil , nil , 0 , err
110
116
}
111
117
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
113
124
// slots from the previous account.
114
125
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 ++ {
116
128
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 ())
119
130
}
120
131
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 {
123
134
if accIt .Next () {
124
135
acc , err := snapshot .FullAccount (accIt .Account ())
125
136
if err != nil {
@@ -128,21 +139,21 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
128
139
}
129
140
addr := rawdb .ReadPreimage (statedb .Database ().DiskDB (), accIt .Hash ())
130
141
131
- // @jsign: do your magic here adding the account at ` addr
142
+ mkv . addAccount ( addr , acc )
132
143
133
144
// Store the account code if present
134
145
if ! bytes .Equal (acc .CodeHash , emptyCode ) {
135
146
code := rawdb .ReadCode (statedb .Database ().DiskDB (), common .BytesToHash (acc .CodeHash ))
136
147
chunks := trie .ChunkifyCode (code )
137
148
138
- // @jsign: do your magic here with the code chunks
149
+ mkv . addAccountCode ( addr , uint64 ( len ( code )), chunks )
139
150
}
140
151
141
152
if ! bytes .Equal (acc .Root , emptyRoot [:]) {
142
- for ; stIt .Next () && count < 500 ; count ++ {
153
+ for ; stIt .Next () && count < maxMovedCount ; count ++ {
143
154
slotnr := rawdb .ReadPreimage (statedb .Database ().DiskDB (), stIt .Hash ())
144
155
145
- // @jsign do your magic here with extra slots
156
+ mkv . addStorageSlot ( addr , slotnr , stIt . Slot ())
146
157
}
147
158
}
148
159
}
@@ -157,6 +168,13 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
157
168
fdb .LastAccHash = accIt .Hash ()
158
169
fdb .LastSlotHash = stIt .Hash ()
159
170
}
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 ))
160
178
}
161
179
}
162
180
@@ -232,3 +250,114 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
232
250
vmenv := vm .NewEVM (blockContext , vm.TxContext {}, statedb , config , cfg )
233
251
return applyTransaction (msg , config , author , gp , statedb , header .Number , header .Hash (), tx , usedGas , vmenv )
234
252
}
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
+ }
0 commit comments