fix: need to pass block_hash to StateObject and Database

This commit is contained in:
i-norden 2023-02-28 12:07:03 -06:00
parent ede0af3c26
commit 6b25cde9dd
4 changed files with 33 additions and 28 deletions

View File

@ -5,14 +5,10 @@ import (
"errors" "errors"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/crypto"
"github.com/VictoriaMetrics/fastcache" "github.com/VictoriaMetrics/fastcache"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/statediff/indexer/ipld" "github.com/ethereum/go-ethereum/statediff/indexer/ipld"
"github.com/ethereum/go-ethereum/trie"
lru "github.com/hashicorp/golang-lru" lru "github.com/hashicorp/golang-lru"
"github.com/jackc/pgx/pgxpool" "github.com/jackc/pgx/pgxpool"
) )
@ -35,29 +31,29 @@ var (
type Database interface { type Database interface {
ContractCode(addrHash common.Hash, codeHash common.Hash) ([]byte, error) ContractCode(addrHash common.Hash, codeHash common.Hash) ([]byte, error)
ContractCodeSize(addrHash common.Hash, codeHash common.Hash) (int, error) ContractCodeSize(addrHash common.Hash, codeHash common.Hash) (int, error)
StateAccount(address common.Address) (*types.StateAccount, error) StateAccount(addressHash, blockHash common.Hash) (*types.StateAccount, error)
StorageSlot(addressHash, slotHash common.Hash) ([]byte, error) StorageValue(addressHash, slotHash, blockHash common.Hash) ([]byte, error)
} }
var _ Database = &stateDatabase{} var _ Database = &stateDatabase{}
type stateDatabase struct { type stateDatabase struct {
pgdb *pgxpool.Pool pgdb *pgxpool.Pool
trieDB *trie.Database
codeSizeCache *lru.Cache codeSizeCache *lru.Cache
codeCache *fastcache.Cache codeCache *fastcache.Cache
} }
func NewStateDatabase(pgdb *pgxpool.Pool, ethdb ethdb.Database, config *trie.Config) (*stateDatabase, error) { // NewStateDatabase returns a new Database implementation using the provided postgres connection pool
func NewStateDatabase(pgdb *pgxpool.Pool) (*stateDatabase, error) {
csc, _ := lru.New(codeSizeCacheSize) csc, _ := lru.New(codeSizeCacheSize)
return &stateDatabase{ return &stateDatabase{
pgdb: pgdb, pgdb: pgdb,
trieDB: trie.NewDatabaseWithConfig(ethdb, config),
codeSizeCache: csc, codeSizeCache: csc,
codeCache: fastcache.New(codeCacheSize), codeCache: fastcache.New(codeCacheSize),
}, nil }, nil
} }
// ContractCode satisfies Database, it returns the contract code for a give codehash
func (sd *stateDatabase) ContractCode(_, codeHash common.Hash) ([]byte, error) { func (sd *stateDatabase) ContractCode(_, codeHash common.Hash) ([]byte, error) {
if code := sd.codeCache.Get(nil, codeHash.Bytes()); len(code) > 0 { if code := sd.codeCache.Get(nil, codeHash.Bytes()); len(code) > 0 {
return code, nil return code, nil
@ -75,6 +71,7 @@ func (sd *stateDatabase) ContractCode(_, codeHash common.Hash) ([]byte, error) {
return nil, errNotFound return nil, errNotFound
} }
// ContractCodeSize satisfies Database, it returns the length of the code for a provided codehash
func (sd *stateDatabase) ContractCodeSize(_, codeHash common.Hash) (int, error) { func (sd *stateDatabase) ContractCodeSize(_, codeHash common.Hash) (int, error) {
if cached, ok := sd.codeSizeCache.Get(codeHash); ok { if cached, ok := sd.codeSizeCache.Get(codeHash); ok {
return cached.(int), nil return cached.(int), nil
@ -83,10 +80,11 @@ func (sd *stateDatabase) ContractCodeSize(_, codeHash common.Hash) (int, error)
return len(code), err return len(code), err
} }
func (sd *stateDatabase) StateAccount(address common.Address) (*types.StateAccount, error) { // StateAccount satisfies Database, it returns the types.StateAccount for a provided address and block hash
func (sd *stateDatabase) StateAccount(addressHash, blockHash common.Hash) (*types.StateAccount, error) {
res := StateAccountResult{} res := StateAccountResult{}
key := crypto.Keccak256Hash(address.Bytes()) err := sd.pgdb.QueryRow(context.Background(), GetStateAccount, addressHash.Hex(), blockHash.Hex()).Scan(&res)
if err := sd.pgdb.QueryRow(context.Background(), GetStateAccount, key.Hex()).Scan(&res); err != nil { if err != nil {
return nil, errNotFound return nil, errNotFound
} }
if res.Removed { if res.Removed {
@ -103,9 +101,11 @@ func (sd *stateDatabase) StateAccount(address common.Address) (*types.StateAccou
}, nil }, nil
} }
func (sd *stateDatabase) StorageSlot(addressHash, slotHash common.Hash) ([]byte, error) { // StorageValue satisfies Database, it returns the storage value for the provided address, slot, and block hash
func (sd *stateDatabase) StorageValue(addressHash, slotHash, blockHash common.Hash) ([]byte, error) {
res := StorageSlotResult{} res := StorageSlotResult{}
if err := sd.pgdb.QueryRow(context.Background(), GetStorageSlot, addressHash.Hex(), slotHash.Hex()).Scan(&res); err != nil { err := sd.pgdb.QueryRow(context.Background(), GetStorageSlot, addressHash.Hex(), slotHash.Hex(), blockHash.Hex()).Scan(&res)
if err != nil {
return nil, errNotFound return nil, errNotFound
} }
if res.Removed { if res.Removed {

3
sql.go
View File

@ -21,6 +21,9 @@ const (
) )
WHERE state_leaf_key = $1 WHERE state_leaf_key = $1
AND storage_leaf_key = $2 AND storage_leaf_key = $2
AND header_cids.block_number <= (SELECT block_number
FROM eth.header_cids
WHERE block_hash = $3)
AND header_cids.block_hash = (SELECT canonical_header_hash(header_cids.block_number)) AND header_cids.block_hash = (SELECT canonical_header_hash(header_cids.block_number))
ORDER BY header_cids.block_number DESC ORDER BY header_cids.block_number DESC
LIMIT 1` LIMIT 1`

View File

@ -54,10 +54,11 @@ func (s Storage) Copy() Storage {
// First you need to obtain a state object. // First you need to obtain a state object.
// Account values can be accessed and modified through the object. // Account values can be accessed and modified through the object.
type stateObject struct { type stateObject struct {
address common.Address address common.Address
addrHash common.Hash // hash of ethereum address of the account addrHash common.Hash // hash of ethereum address of the account
data types.StateAccount blockHash common.Hash // hash of the block this state object exists at or is being applied on top of
db *StateDB data types.StateAccount
db *StateDB
// 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
@ -88,7 +89,7 @@ func (s *stateObject) empty() bool {
} }
// newObject creates a state object. // newObject creates a state object.
func newObject(db *StateDB, address common.Address, data types.StateAccount) *stateObject { func newObject(db *StateDB, address common.Address, data types.StateAccount, blockHash common.Hash) *stateObject {
if data.Balance == nil { if data.Balance == nil {
data.Balance = new(big.Int) data.Balance = new(big.Int)
} }
@ -102,6 +103,7 @@ func newObject(db *StateDB, address common.Address, data types.StateAccount) *st
db: db, db: db,
address: address, address: address,
addrHash: crypto.Keccak256Hash(address[:]), addrHash: crypto.Keccak256Hash(address[:]),
blockHash: blockHash,
data: data, data: data,
originStorage: make(Storage), originStorage: make(Storage),
pendingStorage: make(Storage), pendingStorage: make(Storage),
@ -161,7 +163,7 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
} }
// If no live objects are available, load from database // If no live objects are available, load from database
start := time.Now() start := time.Now()
enc, err := db.StorageSlot(s.addrHash, key) enc, err := db.StorageValue(s.addrHash, key, s.blockHash)
if metrics.EnabledExpensive { if metrics.EnabledExpensive {
s.db.StorageReads += time.Since(start) s.db.StorageReads += time.Since(start)
} }

View File

@ -50,9 +50,8 @@ type StateDB struct {
db Database db Database
hasher crypto.KeccakState hasher crypto.KeccakState
// originalRoot is the pre-state root, before any changes were made. // originBlockHash is the blockhash for the state we are working on top of
// It will be updated when the Commit is called. originBlockHash common.Hash
originalRoot common.Hash
snaps *snapshot.Tree snaps *snapshot.Tree
snapDestructs map[common.Hash]struct{} snapDestructs map[common.Hash]struct{}
@ -110,10 +109,10 @@ type StateDB struct {
} }
// New creates a new state from a given trie. // New creates a new state from a given trie.
func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) { func New(blockHash common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) {
sdb := &StateDB{ sdb := &StateDB{
db: db, db: db,
originalRoot: root, originBlockHash: blockHash,
snaps: snaps, snaps: snaps,
stateObjects: make(map[common.Address]*stateObject), stateObjects: make(map[common.Address]*stateObject),
stateObjectsPending: make(map[common.Address]struct{}), stateObjectsPending: make(map[common.Address]struct{}),
@ -348,7 +347,8 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
// TODO: REPLACE TRIE ACCESS HERE // TODO: REPLACE TRIE ACCESS HERE
// can add a fallback option to use ipfsethdb to do the trie access if direct access fails // can add a fallback option to use ipfsethdb to do the trie access if direct access fails
start := time.Now() start := time.Now()
data, err := s.db.StateAccount(addr) addrHash := crypto.Keccak256Hash(addr.Bytes())
data, err := s.db.StateAccount(addrHash, s.originBlockHash)
if metrics.EnabledExpensive { if metrics.EnabledExpensive {
s.AccountReads += time.Since(start) s.AccountReads += time.Since(start)
} }
@ -360,7 +360,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
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.originBlockHash)
s.setStateObject(obj) s.setStateObject(obj)
return obj return obj
} }
@ -382,7 +382,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, types.StateAccount{}) newobj = newObject(s, addr, types.StateAccount{}, s.originBlockHash)
if prev == nil { if prev == nil {
s.journal.append(createObjectChange{account: &addr}) s.journal.append(createObjectChange{account: &addr})
} else { } else {