Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/evm/internal/t8ntool/transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ func VerkleRoot(ctx *cli.Context) error {
}

func genVktFromAlloc(alloc core.GenesisAlloc) (*trie.VerkleTrie, error) {
vkt := trie.NewVerkleTrie(verkle.New(), trie.NewDatabase(rawdb.NewMemoryDatabase()), utils.NewPointCache(), true)
vkt := trie.NewVerkleTrie(trie.NewBinaryNode(), trie.NewDatabase(rawdb.NewMemoryDatabase()), utils.NewPointCache(), true)

for addr, acc := range alloc {
for slot, value := range acc.Storage {
Expand Down
2 changes: 1 addition & 1 deletion consensus/beacon/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ func BuildVerkleProof(header *types.Header, state *state.StateDB, parentRoot com
// conversion, when the previous tree is a merkle tree.
// Logically, the "previous" verkle tree is an empty tree.
okpre = true
vtrpre = trie.NewVerkleTrie(verkle.New(), state.Database().TrieDB(), utils.NewPointCache(), false)
vtrpre = trie.NewVerkleTrie(trie.NewBinaryNode(), state.Database().TrieDB(), utils.NewPointCache(), false)
post := state.GetTrie().(*trie.TransitionTrie)
vtrpost = post.Overlay()
okpost = true
Expand Down
198 changes: 10 additions & 188 deletions core/overlay/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,9 @@
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"io"
"os"
"runtime"
"sync"
"time"

"github.com/ethereum/go-ethereum/common"
Expand All @@ -35,184 +32,28 @@
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/utils"
"github.com/ethereum/go-verkle"
"github.com/holiman/uint256"
)

var zeroTreeIndex uint256.Int

Check failure on line 39 in core/overlay/conversion.go

View workflow job for this annotation

GitHub Actions / lint

var `zeroTreeIndex` is unused (unused)

// keyValueMigrator is a helper module that collects key-values from the overlay-tree migration for Verkle Trees.
// It assumes that the walk of the base tree is done in address-order, so it exploit that fact to
// collect the key-values in a way that is efficient.
type keyValueMigrator struct {
// leafData contains the values for the future leaf for a particular VKT branch.
leafData map[branchKey]*migratedKeyValue

// When prepare() is called, it will start a background routine that will process the leafData
// saving the result in newLeaves to be used by migrateCollectedKeyValues(). The background
// routine signals that it is done by closing processingReady.
processingReady chan struct{}
newLeaves []verkle.LeafNode
prepareErr error
}

func newKeyValueMigrator() *keyValueMigrator {
// We do initialize the VKT config since prepare() might indirectly make multiple GetConfig() calls
// in different goroutines when we never called GetConfig() before, causing a race considering the way
// that `config` is designed in go-verkle.
// TODO: jsign as a fix for this in the PR where we move to a file-less precomp, since it allows safe
// concurrent calls to GetConfig(). When that gets merged, we can remove this line.
_ = verkle.GetConfig()
return &keyValueMigrator{
processingReady: make(chan struct{}),
leafData: make(map[branchKey]*migratedKeyValue, 10_000),
}
}

type migratedKeyValue struct {

Check failure on line 41 in core/overlay/conversion.go

View workflow job for this annotation

GitHub Actions / lint

type `migratedKeyValue` is unused (unused)
branchKey branchKey
leafNodeData verkle.BatchNewLeafNodeData
}
type branchKey struct {

Check failure on line 45 in core/overlay/conversion.go

View workflow job for this annotation

GitHub Actions / lint

type `branchKey` is unused (unused)
addr common.Address
treeIndex uint256.Int
}

func newBranchKey(addr []byte, treeIndex *uint256.Int) branchKey {

Check failure on line 50 in core/overlay/conversion.go

View workflow job for this annotation

GitHub Actions / lint

func `newBranchKey` is unused (unused)
var sk branchKey
copy(sk.addr[:], addr)
sk.treeIndex = *treeIndex
return sk
}

func (kvm *keyValueMigrator) addStorageSlot(addr []byte, slotNumber []byte, slotValue []byte) {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deleted the batching computation, it can definitely be done as a later optimization but it's overkill for now.

treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(slotNumber)
leafNodeData := kvm.getOrInitLeafNodeData(newBranchKey(addr, treeIndex))
leafNodeData.Values[subIndex] = slotValue
}

func (kvm *keyValueMigrator) addAccount(addr []byte, acc *types.StateAccount) {
leafNodeData := kvm.getOrInitLeafNodeData(newBranchKey(addr, &zeroTreeIndex))

var basicData [verkle.LeafValueSize]byte
basicData[utils.BasicDataVersionOffset] = 0
binary.BigEndian.PutUint64(basicData[utils.BasicDataNonceOffset:], acc.Nonce)

// get the lower 16 bytes of water and change its endianness
balanceBytes := acc.Balance.Bytes()
copy(basicData[32-len(balanceBytes):], balanceBytes[:])

leafNodeData.Values[utils.BasicDataLeafKey] = basicData[:]
leafNodeData.Values[utils.CodeHashLeafKey] = acc.CodeHash[:]
}

// addAccountCode needs to be called AFTER addAccount, as it will reuse the leaf
func (kvm *keyValueMigrator) addAccountCode(addr []byte, codeSize uint64, chunks []byte) {
leafNodeData := kvm.getOrInitLeafNodeData(newBranchKey(addr, &zeroTreeIndex))

// Save the code size.
binary.BigEndian.PutUint32(leafNodeData.Values[utils.BasicDataLeafKey][utils.BasicDataCodeSizeOffset-1:utils.BasicDataNonceOffset], uint32(codeSize))

// The first 128 chunks are stored in the account header leaf.
for i := 0; i < 128 && i < len(chunks)/32; i++ {
leafNodeData.Values[byte(128+i)] = chunks[32*i : 32*(i+1)]
}

// Potential further chunks, have their own leaf nodes.
for i := 128; i < len(chunks)/32; {
treeIndex, _ := utils.GetTreeKeyCodeChunkIndices(uint256.NewInt(uint64(i)))
leafNodeData := kvm.getOrInitLeafNodeData(newBranchKey(addr, treeIndex))

j := i
for ; (j-i) < 256 && j < len(chunks)/32; j++ {
leafNodeData.Values[byte((j-128)%256)] = chunks[32*j : 32*(j+1)]
}
i = j
}
}

func (kvm *keyValueMigrator) getOrInitLeafNodeData(bk branchKey) *verkle.BatchNewLeafNodeData {
if ld, ok := kvm.leafData[bk]; ok {
return &ld.leafNodeData
}
kvm.leafData[bk] = &migratedKeyValue{
branchKey: bk,
leafNodeData: verkle.BatchNewLeafNodeData{
Stem: nil, // It will be calculated in the prepare() phase, since it's CPU heavy.
Values: make(map[byte][]byte, 256),
},
}
return &kvm.leafData[bk].leafNodeData
}

func (kvm *keyValueMigrator) prepare() {
// We fire a background routine to process the leafData and save the result in newLeaves.
// The background routine signals that it is done by closing processingReady.
go func() {
// Step 1: We split kvm.leafData in numBatches batches, and we process each batch in a separate goroutine.
// This fills each leafNodeData.Stem with the correct value.
leafData := make([]migratedKeyValue, 0, len(kvm.leafData))
for _, v := range kvm.leafData {
leafData = append(leafData, *v)
}
var wg sync.WaitGroup
batchNum := runtime.NumCPU()
batchSize := (len(kvm.leafData) + batchNum - 1) / batchNum
for i := 0; i < len(kvm.leafData); i += batchSize {
start := i
end := i + batchSize
if end > len(kvm.leafData) {
end = len(kvm.leafData)
}
wg.Add(1)

batch := leafData[start:end]
go func() {
defer wg.Done()
var currAddr common.Address
var currPoint *verkle.Point
for i := range batch {
if batch[i].branchKey.addr != currAddr || currAddr == (common.Address{}) {
currAddr = batch[i].branchKey.addr
currPoint = utils.EvaluateAddressPoint(currAddr[:])
}
stem := utils.GetTreeKeyWithEvaluatedAddess(currPoint, &batch[i].branchKey.treeIndex, 0)
stem = stem[:verkle.StemSize]
batch[i].leafNodeData.Stem = stem
}
}()
}
wg.Wait()

// Step 2: Now that we have all stems (i.e: tree keys) calculated, we can create the new leaves.
nodeValues := make([]verkle.BatchNewLeafNodeData, len(kvm.leafData))
for i := range leafData {
nodeValues[i] = leafData[i].leafNodeData
}

// Create all leaves in batch mode so we can optimize cryptography operations.
kvm.newLeaves, kvm.prepareErr = verkle.BatchNewLeafNode(nodeValues)
close(kvm.processingReady)
}()
}

func (kvm *keyValueMigrator) migrateCollectedKeyValues(tree *trie.VerkleTrie) error {
now := time.Now()
<-kvm.processingReady
if kvm.prepareErr != nil {
return fmt.Errorf("failed to prepare key values: %w", kvm.prepareErr)
}
log.Info("Prepared key values from base tree", "duration", time.Since(now))

// Insert into the tree.
if err := tree.InsertMigratedLeaves(kvm.newLeaves); err != nil {
return fmt.Errorf("failed to insert migrated leaves: %w", err)
}

return nil
}

// OverlayVerkleTransition contains the overlay conversion logic
func OverlayVerkleTransition(statedb *state.StateDB, root common.Hash, maxMovedCount uint64) error {
migrdb := statedb.Database()
Expand Down Expand Up @@ -274,10 +115,6 @@
preimageSeek += int64(len(addr))
}

// mkv will be assiting in the collection of up to maxMovedCount key values to be migrated to the VKT.
// It has internal caches to do efficient MPT->VKT key calculations, which will be discarded after
// this function.
mkv := newKeyValueMigrator()
// move maxCount accounts into the verkle tree, starting with the
// slots from the previous account.
count := uint64(0)
Expand Down Expand Up @@ -348,7 +185,9 @@
}
preimageSeek += int64(len(slotnr))

mkv.addStorageSlot(migrdb.GetCurrentAccountAddress().Bytes(), slotnr, safeValue[:])
if err := tt.Overlay().UpdateStorage(*migrdb.GetCurrentAccountAddress(), slotnr, safeValue[:]); err != nil {
return fmt.Errorf("updating storage slot %x at address %x: %w", slotnr, *migrdb.GetCurrentAccountAddress(), err)
}

// advance the storage iterator
migrdb.SetStorageProcessed(!stIt.Next())
Expand All @@ -366,17 +205,15 @@
if count < maxMovedCount {
count++ // count increase for the account itself

mkv.addAccount(migrdb.GetCurrentAccountAddress().Bytes(), acc)
vkt.ClearStrorageRootConversion(*migrdb.GetCurrentAccountAddress())

// Store the account code if present
var code []byte
if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash[:]) {
code := rawdb.ReadCode(statedb.Database().DiskDB(), common.BytesToHash(acc.CodeHash))
chunks := trie.ChunkifyCode(code)

mkv.addAccountCode(migrdb.GetCurrentAccountAddress().Bytes(), uint64(len(code)), chunks)
code = rawdb.ReadCode(statedb.Database().DiskDB(), common.BytesToHash(acc.CodeHash))
tt.Overlay().UpdateContractCode(*migrdb.GetCurrentAccountAddress(), common.BytesToHash(acc.CodeHash), code)
}

tt.Overlay().UpdateAccount(*migrdb.GetCurrentAccountAddress(), acc, len(code))
vkt.ClearStrorageRootConversion(*migrdb.GetCurrentAccountAddress())

// reset storage iterator marker for next account
migrdb.SetStorageProcessed(false)
migrdb.SetCurrentSlotHash(common.Hash{})
Expand Down Expand Up @@ -412,22 +249,7 @@
}
migrdb.SetCurrentPreimageOffset(preimageSeek)

log.Info("Collected key values from base tree", "count", count, "duration", time.Since(now), "last account hash", statedb.Database().GetCurrentAccountHash(), "last account address", statedb.Database().GetCurrentAccountAddress(), "storage processed", statedb.Database().GetStorageProcessed(), "last storage", statedb.Database().GetCurrentSlotHash())

// Take all the collected key-values and prepare the new leaf values.
// This fires a background routine that will start doing the work that
// migrateCollectedKeyValues() will use to insert into the tree.
//
// TODO: Now both prepare() and migrateCollectedKeyValues() are next to each other, but
// after we fix an existing bug, we can call prepare() before the block execution and
// let it do the work in the background. After the block execution and finalization
// finish, we can call migrateCollectedKeyValues() which should already find everything ready.
mkv.prepare()
now = time.Now()
if err := mkv.migrateCollectedKeyValues(tt.Overlay()); err != nil {
return fmt.Errorf("could not migrate key values: %w", err)
}
log.Info("Inserted key values in overlay tree", "count", count, "duration", time.Since(now))
log.Info("Inserted key values in overlay tree", "count", count, "duration", time.Since(now), "last account hash", statedb.Database().GetCurrentAccountHash(), "last account address", statedb.Database().GetCurrentAccountAddress(), "storage processed", statedb.Database().GetStorageProcessed(), "last storage", statedb.Database().GetCurrentSlotHash())
}

return nil
Expand Down
4 changes: 2 additions & 2 deletions core/state/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,10 +343,10 @@ func (db *cachingDB) openMPTTrie(root common.Hash) (Trie, error) {
func (db *cachingDB) openVKTrie(_ common.Hash) (Trie, error) {
payload, err := db.DiskDB().Get(trie.FlatDBVerkleNodeKeyPrefix)
if err != nil {
return trie.NewVerkleTrie(verkle.New(), db.triedb, db.addrToPoint, db.CurrentTransitionState.Ended), nil
return trie.NewVerkleTrie(trie.NewBinaryNode(), db.triedb, db.addrToPoint, db.CurrentTransitionState.Ended), nil
}

r, err := verkle.ParseNode(payload, 0)
r, err := trie.DeserializeNode(payload, 0)
if err != nil {
panic(err)
}
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ require (
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 // indirect
github.com/kilic/bls12-381 v0.1.0 // indirect
github.com/klauspost/compress v1.15.15 // indirect
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
Expand All @@ -126,6 +127,7 @@ require (
github.com/tklauser/go-sysconf v0.3.5 // indirect
github.com/tklauser/numcpus v0.2.2 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/zeebo/blake3 v0.2.4 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,10 @@ github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0
github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
Expand Down Expand Up @@ -466,6 +469,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME=
go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
Expand Down
Loading
Loading