core/state: move state account to core/types + abstracted write account to trie #23567 (#1072)

This commit is contained in:
Daniel Liu 2025-08-08 09:13:34 +08:00 committed by GitHub
parent bc7e213b96
commit 3da4549e07
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 81 additions and 38 deletions

View file

@ -23,6 +23,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/lru" "github.com/XinFinOrg/XDPoSChain/common/lru"
"github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/trie" "github.com/XinFinOrg/XDPoSChain/trie"
) )
@ -69,6 +70,9 @@ type Trie interface {
// trie.MissingNodeError is returned. // trie.MissingNodeError is returned.
TryGet(key []byte) ([]byte, error) TryGet(key []byte) ([]byte, error)
// TryUpdateAccount abstract an account write in the trie.
TryUpdateAccount(key []byte, account *types.StateAccount) error
// TryUpdate associates key with value in the trie. If value has length zero, any // TryUpdate associates key with value in the trie. If value has length zero, any
// existing value is deleted from the trie. The value bytes must not be modified // existing value is deleted from the trie. The value bytes must not be modified
// by the caller while they are stored in the trie. If a node was not found in the // by the caller while they are stored in the trie. If a node was not found in the

View file

@ -22,6 +22,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/rlp"
"github.com/XinFinOrg/XDPoSChain/trie" "github.com/XinFinOrg/XDPoSChain/trie"
@ -91,7 +92,7 @@ func (s *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissingP
c.onRoot(s.trie.Hash()) c.onRoot(s.trie.Hash())
it := trie.NewIterator(s.trie.NodeIterator(nil)) it := trie.NewIterator(s.trie.NodeIterator(nil))
for it.Next() { for it.Next() {
var data Account var data types.StateAccount
if err := rlp.DecodeBytes(it.Value, &data); err != nil { if err := rlp.DecodeBytes(it.Value, &data); err != nil {
panic(err) panic(err)
} }

View file

@ -105,7 +105,7 @@ func (it *nodeIterator) step() error {
return nil return nil
} }
// Otherwise we've reached an account node, initiate data iteration // Otherwise we've reached an account node, initiate data iteration
var account Account var account types.StateAccount
if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob()), &account); err != nil { if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob()), &account); err != nil {
return err return err
} }

View file

@ -62,9 +62,9 @@ func (s Storage) Copy() Storage {
// Finally, call commitTrie to write the modified storage trie into a database. // Finally, call commitTrie to write the modified storage trie into a database.
type stateObject struct { type stateObject struct {
db *StateDB db *StateDB
address common.Address // address of ethereum account address common.Address // address of ethereum account
addrHash common.Hash // hash of ethereum address of the account addrHash common.Hash // hash of ethereum address of the account
data Account // Account data with all mutations applied in the scope of block data types.StateAccount // Account data with all mutations applied in the scope of block
// DB error. // DB error.
// State objects are used by the consensus core and VM which are // State objects are used by the consensus core and VM which are
@ -103,17 +103,8 @@ func (s *stateObject) empty() bool {
return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, types.EmptyCodeHash.Bytes()) return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, types.EmptyCodeHash.Bytes())
} }
// Account is the Ethereum consensus representation of accounts.
// These objects are stored in the main account trie.
type Account struct {
Nonce uint64
Balance *big.Int
Root common.Hash // merkle root of the storage trie
CodeHash []byte
}
// newObject creates a state object. // newObject creates a state object.
func newObject(db *StateDB, address common.Address, data Account) *stateObject { func newObject(db *StateDB, address common.Address, data types.StateAccount) *stateObject {
if data.Balance == nil { if data.Balance == nil {
data.Balance = new(big.Int) data.Balance = new(big.Int)
} }
@ -136,7 +127,7 @@ func newObject(db *StateDB, address common.Address, data Account) *stateObject {
// EncodeRLP implements rlp.Encoder. // EncodeRLP implements rlp.Encoder.
func (s *stateObject) EncodeRLP(w io.Writer) error { func (s *stateObject) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, s.data) return rlp.Encode(w, &s.data)
} }
// setError remembers the first non-nil error it is called with. // setError remembers the first non-nil error it is called with.

View file

@ -492,12 +492,7 @@ func (s *StateDB) updateStateObject(obj *stateObject) {
// Encode the account and update the account trie // Encode the account and update the account trie
addr := obj.Address() addr := obj.Address()
if err := s.trie.TryUpdateAccount(addr[:], &obj.data); err != nil {
data, err := rlp.EncodeToBytes(obj)
if err != nil {
panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
}
if err = s.trie.TryUpdate(addr[:], data); err != nil {
s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err)) s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err))
} }
} }
@ -554,13 +549,13 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
if len(enc) == 0 { if len(enc) == 0 {
return nil return nil
} }
var data Account data := new(types.StateAccount)
if err := rlp.DecodeBytes(enc, &data); err != nil { if err := rlp.DecodeBytes(enc, data); err != nil {
log.Error("Failed to decode state object", "addr", addr, "err", err) log.Error("Failed to decode state object", "addr", addr, "err", err)
return nil return nil
} }
// Insert into the live set // Insert into the live set
obj := newObject(s, addr, data) obj := newObject(s, addr, *data)
s.setStateObject(obj) s.setStateObject(obj)
return obj return obj
} }
@ -582,7 +577,7 @@ func (s *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
// the given address, it is overwritten and returned as the second return value. // the given address, it is overwritten and returned as the second return value.
func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) { func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
prev = s.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that! prev = s.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that!
newobj = newObject(s, addr, Account{}) newobj = newObject(s, addr, types.StateAccount{})
if prev == nil { if prev == nil {
s.journal.append(createObjectChange{account: &addr}) s.journal.append(createObjectChange{account: &addr})
} else { } else {
@ -852,7 +847,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
start := time.Now() start := time.Now()
root, accountCommitted, err := s.trie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error { root, accountCommitted, err := s.trie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error {
var account Account var account types.StateAccount
if err := rlp.DecodeBytes(leaf, &account); err != nil { if err := rlp.DecodeBytes(leaf, &account); err != nil {
return nil return nil
} }

View file

@ -20,6 +20,7 @@ import (
"bytes" "bytes"
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/rlp" "github.com/XinFinOrg/XDPoSChain/rlp"
"github.com/XinFinOrg/XDPoSChain/trie" "github.com/XinFinOrg/XDPoSChain/trie"
@ -43,7 +44,7 @@ func NewStateSync(root common.Hash, database ethdb.KeyValueReader, bloom *trie.S
return err return err
} }
} }
var obj Account var obj types.StateAccount
if err := rlp.Decode(bytes.NewReader(leaf), &obj); err != nil { if err := rlp.Decode(bytes.NewReader(leaf), &obj); err != nil {
return err return err
} }

View file

@ -203,7 +203,7 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
} }
results[len(hashQueue)+i] = trie.SyncResult{Hash: crypto.Keccak256Hash(data), Data: data} results[len(hashQueue)+i] = trie.SyncResult{Hash: crypto.Keccak256Hash(data), Data: data}
} else { } else {
var acc Account var acc types.StateAccount
if err := rlp.DecodeBytes(srcTrie.Get(path[0]), &acc); err != nil { if err := rlp.DecodeBytes(srcTrie.Get(path[0]), &acc); err != nil {
t.Fatalf("failed to decode account on path %x: %v", path, err) t.Fatalf("failed to decode account on path %x: %v", path, err)
} }

View file

@ -0,0 +1,32 @@
// Copyright 2021 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package types
import (
"math/big"
"github.com/XinFinOrg/XDPoSChain/common"
)
// StateAccount is the Ethereum consensus representation of accounts.
// These objects are stored in the main account trie.
type StateAccount struct {
Nonce uint64
Balance *big.Int
Root common.Hash // merkle root of the storage trie
CodeHash []byte
}

View file

@ -20,7 +20,9 @@ import (
"fmt" "fmt"
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/rlp"
) )
// SecureTrie wraps a trie with key hashing. In a secure trie, all // SecureTrie wraps a trie with key hashing. In a secure trie, all
@ -85,6 +87,21 @@ func (t *SecureTrie) TryGetNode(path []byte) ([]byte, int, error) {
return t.trie.TryGetNode(path) return t.trie.TryGetNode(path)
} }
// TryUpdateAccount will abstract the write of an account to the
// secure trie.
func (t *SecureTrie) TryUpdateAccount(key []byte, acc *types.StateAccount) error {
hk := t.hashKey(key)
data, err := rlp.EncodeToBytes(acc)
if err != nil {
return err
}
if err := t.trie.TryUpdate(hk, data); err != nil {
return err
}
t.getSecKeyCache()[string(hk)] = common.CopyBytes(key)
return nil
}
// Update associates key with value in the trie. Subsequent calls to // Update associates key with value in the trie. Subsequent calls to
// Get will return value. If value has length zero, any existing value // Get will return value. If value has length zero, any existing value
// is deleted from the trie and calls to Get will return nil. // is deleted from the trie and calls to Get will return nil.

View file

@ -26,6 +26,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/rlp"
) )
// LeafCallback is a callback type invoked when a trie operation reaches a leaf // LeafCallback is a callback type invoked when a trie operation reaches a leaf
@ -402,6 +403,14 @@ func (t *Trie) Update(key, value []byte) {
} }
} }
func (t *Trie) TryUpdateAccount(key []byte, acc *types.StateAccount) error {
data, err := rlp.EncodeToBytes(acc)
if err != nil {
return fmt.Errorf("can't encode object at %x: %w", key[:], err)
}
return t.TryUpdate(key, data)
}
// TryUpdate associates key with value in the trie. Subsequent calls to // TryUpdate associates key with value in the trie. Subsequent calls to
// Get will return value. If value has length zero, any existing value // Get will return value. If value has length zero, any existing value
// is deleted from the trie and calls to Get will return nil. // is deleted from the trie and calls to Get will return nil.

View file

@ -481,13 +481,6 @@ func BenchmarkHash(b *testing.B) {
trie.Hash() trie.Hash()
} }
type account struct {
Nonce uint64
Balance *big.Int
Root common.Hash
Code []byte
}
// Benchmarks the trie Commit following a Hash. Since the trie caches the result of any operation, // Benchmarks the trie Commit following a Hash. Since the trie caches the result of any operation,
// we cannot use b.N as the number of hashing rounds, since all rounds apart from // we cannot use b.N as the number of hashing rounds, since all rounds apart from
// the first one will be NOOP. As such, we'll use b.N as the number of account to // the first one will be NOOP. As such, we'll use b.N as the number of account to
@ -496,7 +489,7 @@ func BenchmarkCommitAfterHash(b *testing.B) {
b.Run("no-onleaf", func(b *testing.B) { b.Run("no-onleaf", func(b *testing.B) {
benchmarkCommitAfterHash(b, nil) benchmarkCommitAfterHash(b, nil)
}) })
var a account var a types.StateAccount
onleaf := func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error { onleaf := func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error {
rlp.DecodeBytes(leaf, &a) rlp.DecodeBytes(leaf, &a)
return nil return nil
@ -592,7 +585,7 @@ func makeAccounts(size int) (addresses [][20]byte, accounts [][]byte) {
balanceBytes := make([]byte, numBytes) balanceBytes := make([]byte, numBytes)
random.Read(balanceBytes) random.Read(balanceBytes)
balance := new(big.Int).SetBytes(balanceBytes) balance := new(big.Int).SetBytes(balanceBytes)
data, _ := rlp.EncodeToBytes(&account{nonce, balance, root, code}) data, _ := rlp.EncodeToBytes(&types.StateAccount{Nonce: nonce, Balance: balance, Root: root, CodeHash: code})
accounts[i] = data accounts[i] = data
} }
return addresses, accounts return addresses, accounts