Skip to content

Commit

Permalink
Fix: avoid update the stateObjects at conflict check phase
Browse files Browse the repository at this point in the history
There can be a issue that the object updated by mainDB.GetNonce etc is
obseleted.

The fix use statedb.getStateObjectNoUpdate to avoid touching the
stateObjects of mainDB.

Case: TestBlockchain/ValidBlocks/bcEIP1559/intrinsic.json
  • Loading branch information
sunny2022da committed Aug 6, 2024
1 parent 40b2193 commit 2ab5df6
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 15 deletions.
32 changes: 26 additions & 6 deletions core/state/parallel_statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -1248,7 +1248,13 @@ func (slotDB *ParallelStateDB) IsParallelReadsValid(isStage2 bool) bool {
}
}
}
nonceMain := mainDB.GetNonce(addr)

/* can not use mainDB.GetNonce() because we do not want to record the stateObject */
var nonceMain uint64 = 0
mainObj := mainDB.getStateObjectNoUpdate(addr)
if mainObj != nil {
nonceMain = mainObj.Nonce()
}
if nonceSlot != nonceMain {
log.Debug("IsSlotDBReadsValid nonce read is invalid", "addr", addr,
"nonceSlot", nonceSlot, "nonceMain", nonceMain, "SlotIndex", slotDB.parallel.SlotIndex,
Expand All @@ -1267,7 +1273,12 @@ func (slotDB *ParallelStateDB) IsParallelReadsValid(isStage2 bool) bool {
}
}

balanceMain := mainDB.GetBalance(addr)
balanceMain := common.Big0
mainObj := mainDB.getStateObjectNoUpdate(addr)
if mainObj != nil {
balanceMain = mainObj.Balance()
}

if balanceSlot.Cmp(balanceMain) != 0 {
log.Debug("IsSlotDBReadsValid balance read is invalid", "addr", addr,
"balanceSlot", balanceSlot, "balanceMain", balanceMain, "SlotIndex", slotDB.parallel.SlotIndex,
Expand Down Expand Up @@ -1353,7 +1364,11 @@ func (slotDB *ParallelStateDB) IsParallelReadsValid(isStage2 bool) bool {

// check code
for addr, codeSlot := range slotDB.parallel.codeReadsInSlot {
codeMain := mainDB.GetCode(addr)
var codeMain []byte = nil
object := mainDB.getStateObjectNoUpdate(addr)
if object != nil {
codeMain = object.Code()
}
if !bytes.Equal(codeSlot, codeMain) {
log.Debug("IsSlotDBReadsValid code read is invalid", "addr", addr,
"len codeSlot", len(codeSlot), "len codeMain", len(codeMain), "SlotIndex", slotDB.parallel.SlotIndex,
Expand All @@ -1363,7 +1378,11 @@ func (slotDB *ParallelStateDB) IsParallelReadsValid(isStage2 bool) bool {
}
// check codeHash
for addr, codeHashSlot := range slotDB.parallel.codeHashReadsInSlot {
codeHashMain := mainDB.GetCodeHash(addr)
codeHashMain := common.Hash{}
object := mainDB.getStateObjectNoUpdate(addr)
if object != nil {
codeHashMain = common.BytesToHash(object.CodeHash())
}
if !bytes.Equal(codeHashSlot.Bytes(), codeHashMain.Bytes()) {
log.Debug("IsSlotDBReadsValid codehash read is invalid", "addr", addr,
"codeHashSlot", codeHashSlot, "codeHashMain", codeHashMain, "SlotIndex", slotDB.parallel.SlotIndex,
Expand All @@ -1374,7 +1393,7 @@ func (slotDB *ParallelStateDB) IsParallelReadsValid(isStage2 bool) bool {
// addr state check
for addr, stateSlot := range slotDB.parallel.addrStateReadsInSlot {
stateMain := false // addr not exist
if mainDB.getStateObject(addr) != nil {
if mainDB.getStateObjectNoUpdate(addr) != nil {
stateMain = true // addr exist in main DB
}
if stateSlot != stateMain {
Expand All @@ -1387,7 +1406,7 @@ func (slotDB *ParallelStateDB) IsParallelReadsValid(isStage2 bool) bool {
}
// snapshot destructs check
for addr, destructRead := range slotDB.parallel.addrSnapDestructsReadsInSlot {
mainObj := mainDB.getStateObject(addr)
mainObj := mainDB.getStateObjectNoUpdate(addr)
if mainObj == nil {
log.Debug("IsSlotDBReadsValid snapshot destructs read invalid, address should exist",
"addr", addr, "destruct", destructRead,
Expand Down Expand Up @@ -1536,6 +1555,7 @@ func (s *ParallelStateDB) FinaliseForParallel(deleteEmptyObjects bool, mainDB *S
obj.finalise(true) // Prefetch slots in the background
} else {
obj.fixUpOriginAndResetPendingStorage()
obj.finalise(false)
}
}

Expand Down
8 changes: 4 additions & 4 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ func newObject(dbItf StateDBer, isParallel bool, address common.Address, acct *t
address: address,
addrHash: crypto.Keccak256Hash(address[:]),
origin: origin,
data: *acct,
data: *acct.Copy(),
isParallel: isParallel,
originStorage: newStorage(isParallel),
pendingStorage: newStorage(isParallel),
Expand All @@ -267,7 +267,7 @@ func newObject(dbItf StateDBer, isParallel bool, address common.Address, acct *t
}

// dirty data when create a new account
if acct == nil {
if created {
s.dirtyBalance = new(big.Int).Set(acct.Balance)
s.dirtyNonce = new(uint64)
*s.dirtyNonce = acct.Nonce
Expand Down Expand Up @@ -834,7 +834,7 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
address: s.address,
addrHash: s.addrHash,
origin: s.origin,
data: s.data,
data: *s.data.Copy(),
isParallel: s.isParallel,
}
if s.trie != nil {
Expand Down Expand Up @@ -972,7 +972,7 @@ func (s *stateObject) Root() common.Hash {
func (s *stateObject) fixUpOriginAndResetPendingStorage() {
if s.db.isParallel && s.db.parallel.isSlotDB {
mainDB := s.db.parallel.baseStateDB
origObj := mainDB.getStateObject(s.address)
origObj := mainDB.getStateObjectNoUpdate(s.address)
mainDB.accountStorageParallelLock.RLock()
if origObj != nil && origObj.originStorage.Length() != 0 {
s.originStorage = origObj.originStorage.Copy()
Expand Down
39 changes: 34 additions & 5 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ func (s *StateObjectSyncMap) StoreStateObject(addr common.Address, stateObject *
func (s *StateDB) loadStateObj(addr common.Address) (*stateObject, bool) {

if s.isParallel {
s.parallelStateAccessLock.Lock()
defer s.parallelStateAccessLock.Unlock()
ret, ok := s.parallel.stateObjects.LoadStateObject(addr)
return ret, ok
}
Expand All @@ -90,9 +92,9 @@ func (s *StateDB) storeStateObj(addr common.Address, stateObject *stateObject) {
// When a state object is stored into s.parallel.stateObjects,
// it belongs to base StateDB, it is confirmed and valid.
// TODO-dav: remove the lock/unlock?
stateObject.db.parallelStateAccessLock.Lock()
s.parallel.stateObjects.Store(addr, stateObject)
stateObject.db.parallelStateAccessLock.Unlock()
s.parallelStateAccessLock.Lock()
s.parallel.stateObjects.StoreStateObject(addr, stateObject)
s.parallelStateAccessLock.Unlock()
} else {
s.stateObjects[addr] = stateObject
}
Expand Down Expand Up @@ -819,6 +821,33 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject {
return nil
}

// getStateObjectNoUpdate is similar with getStateObject except that it does not
// update stateObjects records.
func (s *StateDB) getStateObjectNoUpdate(addr common.Address) *stateObject {
obj := s.getDeletedStateObjectNoUpdate(addr)
if obj != nil && !obj.deleted {
return obj
}
return nil
}

func (s *StateDB) getDeletedStateObjectNoUpdate(addr common.Address) *stateObject {
s.RecordRead(types.AccountStateKey(addr, types.AccountSelf), struct{}{})

// Prefer live objects if any is available
if obj, _ := s.getStateObjectFromStateObjects(addr); obj != nil {
return obj
}

data, ok := s.getStateObjectFromSnapshotOrTrie(addr)
if !ok {
return nil
}
// Insert into the live set
obj := newObject(s, s.isParallel, addr, data)
return obj
}

func (s *StateDB) GetStateObjectFromSnapshotOrTrie(addr common.Address) (data *types.StateAccount, ok bool) {
return s.getStateObjectFromSnapshotOrTrie(addr)
}
Expand Down Expand Up @@ -881,11 +910,11 @@ func (s *StateDB) getStateObjectFromSnapshotOrTrie(addr common.Address) (data *t

// If snapshot unavailable or reading from it failed, load from the database
if data == nil {
s.trieParallelLock.Lock()
defer s.trieParallelLock.Unlock()
var trie Trie
if s.isParallel {
// hold lock for parallel
s.trieParallelLock.Lock()
defer s.trieParallelLock.Unlock()
if s.parallel.isSlotDB {
if s.parallel.baseStateDB == nil {
return nil, false
Expand Down

0 comments on commit 2ab5df6

Please sign in to comment.