Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

trie: add RollBackAccount function to verkle trees #30135

Merged
merged 4 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
48 changes: 48 additions & 0 deletions trie/verkle.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,54 @@ func (t *VerkleTrie) DeleteAccount(addr common.Address) error {
return nil
}

// RollBackAccount removes the account info + code from the tree, unlike DeleteAccount
// that will overwrite it with 0s. The first 64 storage slots are also removed.
func (t *VerkleTrie) RollBackAccount(addr common.Address) error {
var (
evaluatedAddr = t.cache.Get(addr.Bytes())
codeSizeKey = utils.CodeSizeKeyWithEvaluatedAddress(evaluatedAddr)
)
codeSizeBytes, err := t.root.Get(codeSizeKey, t.nodeResolver)
if err != nil {
return fmt.Errorf("rollback: error finding code size: %w", err)
}
codeSize := binary.LittleEndian.Uint64(codeSizeBytes)

// Delete the account header + first 64 slots + first 128 code chunks
key := common.CopyBytes(codeSizeKey)
for i := 0; i < verkle.NodeWidth; i++ {
key[31] = byte(i)

// this is a workaround to avoid deleting nil leaves, the lib needs to be
// fixed to be able to handle that
v, err := t.root.Get(key, t.nodeResolver)
if err != nil {
return fmt.Errorf("error rolling back account header: %w", err)
}
if len(v) == 0 {
continue
}
_, err = t.root.Delete(key, t.nodeResolver)
if err != nil {
return fmt.Errorf("error rolling back account header: %w", err)
}
}
// Delete all further code
for i, chunknr := uint64(32*128), uint64(128); i < codeSize; i, chunknr = i+32, chunknr+1 {
// evaluate group key at the start of a new group
groupOffset := (chunknr + 128) % 256
if groupOffset == 0 {
key = utils.CodeChunkKeyWithEvaluatedAddress(evaluatedAddr, uint256.NewInt(chunknr))
}
key[31] = byte(groupOffset)
_, err = t.root.Delete(key[:], t.nodeResolver)
if err != nil {
return fmt.Errorf("error deleting code chunk (addr=%x) error: %w", addr[:], err)
}
}
return nil
}

// DeleteStorage implements state.Trie, deleting the specified storage slot from
// the trie. If the storage slot was not existent in the trie, no error will be
// returned. If the trie is corrupted, an error will be returned.
Expand Down
82 changes: 82 additions & 0 deletions trie/verkle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/trie/utils"
"github.com/holiman/uint256"
)
Expand Down Expand Up @@ -89,3 +90,84 @@ func TestVerkleTreeReadWrite(t *testing.T) {
}
}
}

func TestVerkleRollBack(t *testing.T) {
db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.PathScheme)
tr, _ := NewVerkleTrie(types.EmptyVerkleHash, db, utils.NewPointCache(100))

for addr, acct := range accounts {
if err := tr.UpdateAccount(addr, acct); err != nil {
t.Fatalf("Failed to update account, %v", err)
}
for key, val := range storages[addr] {
if err := tr.UpdateStorage(addr, key.Bytes(), val); err != nil {
t.Fatalf("Failed to update account, %v", err)
}
}
// create more than 128 chunks of code
code := make([]byte, 129*32)
for i := 0; i < len(code); i += 2 {
code[i] = 0x60
code[i+1] = byte(i % 256)
}
hash := crypto.Keccak256Hash(code)
if err := tr.UpdateContractCode(addr, hash, code); err != nil {
t.Fatalf("Failed to update contract, %v", err)
}
}

// Check that things were created
for addr, acct := range accounts {
stored, err := tr.GetAccount(addr)
if err != nil {
t.Fatalf("Failed to get account, %v", err)
}
if !reflect.DeepEqual(stored, acct) {
t.Fatal("account is not matched")
}
for key, val := range storages[addr] {
stored, err := tr.GetStorage(addr, key.Bytes())
if err != nil {
t.Fatalf("Failed to get storage, %v", err)
}
if !bytes.Equal(stored, val) {
t.Fatal("storage is not matched")
}
}
}

// ensure there is some code in the 2nd group
keyOf2ndGroup := []byte{141, 124, 185, 236, 50, 22, 185, 39, 244, 47, 97, 209, 96, 235, 22, 13, 205, 38, 18, 201, 128, 223, 0, 59, 146, 199, 222, 119, 133, 13, 91, 0}
chunk, err := tr.root.Get(keyOf2ndGroup, nil)
if err != nil {
t.Fatalf("Failed to get account, %v", err)
}
if len(chunk) == 0 {
t.Fatal("account was not created ")
}

// Rollback first account and check that it is gone
addr1 := common.Address{1}
err = tr.RollBackAccount(addr1)
if err != nil {
t.Fatalf("error rolling back address 1: %v", err)
}

// ensure the account is gone
stored, err := tr.GetAccount(addr1)
if err != nil {
t.Fatalf("Failed to get account, %v", err)
}
if stored != nil {
t.Fatal("account was not deleted")
}

// ensure that the last code chunk is also gone from the tree
chunk, err = tr.root.Get(keyOf2ndGroup, nil)
if err != nil {
t.Fatalf("Failed to get account, %v", err)
}
if len(chunk) != 0 {
t.Fatal("account was not deleted")
}
}