From 635d6ca90edeac4a15f0af697ab1af6714f41983 Mon Sep 17 00:00:00 2001 From: matthew-alexander-partior Date: Thu, 15 Aug 2024 09:06:03 +0000 Subject: [PATCH 1/2] feat: added RW mutex for StateDB Copy --- core/state/journal.go | 3 +++ core/state/statedb.go | 19 ++++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/core/state/journal.go b/core/state/journal.go index c612aa9853..b063f01572 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -247,6 +247,9 @@ func (ch refundChange) dirtied() *common.Address { } func (ch addLogChange) revert(s *StateDB) { + s.mutex.Lock() + defer s.mutex.Unlock() + logs := s.logs[ch.txhash] if len(logs) == 1 { delete(s.logs, ch.txhash) diff --git a/core/state/statedb.go b/core/state/statedb.go index 2bddc39497..d5fd145406 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -75,7 +75,7 @@ type StateDB struct { snapAccounts map[common.Hash][]byte snapStorage map[common.Hash]map[common.Hash][]byte - mutex sync.Mutex + mutex sync.RWMutex journalMutex sync.Mutex // Quorum - a trie to hold extra account information that cannot be stored in the accounts trie @@ -202,7 +202,12 @@ func (s *StateDB) Error() error { } func (s *StateDB) AddLog(log *types.Log) { + s.journalMutex.Lock() s.journal.append(addLogChange{txhash: s.thash}) + s.journalMutex.Unlock() + + s.mutex.Lock() + defer s.mutex.Unlock() log.TxHash = s.thash log.BlockHash = s.bhash @@ -213,10 +218,14 @@ func (s *StateDB) AddLog(log *types.Log) { } func (s *StateDB) GetLogs(hash common.Hash) []*types.Log { + s.mutex.RLock() + defer s.mutex.RUnlock() return s.logs[hash] } func (s *StateDB) Logs() []*types.Log { + s.mutex.RLock() + defer s.mutex.RUnlock() var logs []*types.Log for _, lgs := range s.logs { logs = append(logs, lgs...) @@ -340,12 +349,12 @@ func (s *StateDB) Reset(root common.Hash) error { s.stateObjects = make(map[common.Address]*stateObject) s.stateObjectsPending = make(map[common.Address]struct{}) s.stateObjectsDirty = make(map[common.Address]struct{}) + s.logs = make(map[common.Hash][]*types.Log) + s.logSize = 0 s.mutex.Unlock() s.thash = common.Hash{} s.bhash = common.Hash{} s.txIndex = 0 - s.logs = make(map[common.Hash][]*types.Log) - s.logSize = 0 s.preimages = make(map[common.Hash][]byte) s.clearJournalAndRefund() @@ -816,6 +825,7 @@ func (s *StateDB) Copy() *StateDB { } journal.mutex.Unlock() + s.mutex.RLock() // Copy all the basic fields, initialize the memory ones state := &StateDB{ db: s.db, @@ -833,7 +843,6 @@ func (s *StateDB) Copy() *StateDB { accountExtraDataTrie: s.db.CopyTrie(s.accountExtraDataTrie), } - s.mutex.Lock() // Copy the dirty states, logs, and preimages for _, addr := range dirties { // As documented [here](https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527), @@ -865,7 +874,6 @@ func (s *StateDB) Copy() *StateDB { } state.stateObjectsDirty[addr] = struct{}{} } - s.mutex.Unlock() for hash, logs := range s.logs { cpy := make([]*types.Log, len(logs)) for i, l := range logs { @@ -874,6 +882,7 @@ func (s *StateDB) Copy() *StateDB { } state.logs[hash] = cpy } + s.mutex.RUnlock() for hash, preimage := range s.preimages { state.preimages[hash] = preimage } From 3ada4d0773bdbb9738509e78190b73f2f01aba38 Mon Sep 17 00:00:00 2001 From: rodion Date: Tue, 20 Aug 2024 08:45:41 +0000 Subject: [PATCH 2/2] feat: replace with sync map --- core/state/journal.go | 19 ++++++++---- core/state/statedb.go | 71 +++++++++++++++++++++++++++---------------- 2 files changed, 57 insertions(+), 33 deletions(-) diff --git a/core/state/journal.go b/core/state/journal.go index b063f01572..2f4a9af7d0 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -21,6 +21,7 @@ import ( "sync" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" ) // journalEntry is a modification entry in the state change journal that can be @@ -247,14 +248,20 @@ func (ch refundChange) dirtied() *common.Address { } func (ch addLogChange) revert(s *StateDB) { - s.mutex.Lock() - defer s.mutex.Unlock() - - logs := s.logs[ch.txhash] + logsInterface, ok := s.logs.Load(ch.txhash) + if !ok { + return + } + + logs, ok := logsInterface.([]*types.Log) + if !ok { + return + } + if len(logs) == 1 { - delete(s.logs, ch.txhash) + s.logs.Delete(ch.txhash) } else { - s.logs[ch.txhash] = logs[:len(logs)-1] + s.logs.Store(ch.txhash, logs[:len(logs)-1]) } s.logSize-- } diff --git a/core/state/statedb.go b/core/state/statedb.go index d5fd145406..a9e775c568 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -75,7 +75,7 @@ type StateDB struct { snapAccounts map[common.Hash][]byte snapStorage map[common.Hash]map[common.Hash][]byte - mutex sync.RWMutex + mutex sync.Mutex journalMutex sync.Mutex // Quorum - a trie to hold extra account information that cannot be stored in the accounts trie @@ -98,7 +98,7 @@ type StateDB struct { thash, bhash common.Hash txIndex int - logs map[common.Hash][]*types.Log + logs sync.Map logSize uint preimages map[common.Hash][]byte @@ -142,6 +142,9 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) } // End Quorum - Privacy Enhancements + // Ensure mapping is type of map[common.Hash][]*types.Log + var logs sync.Map + sdb := &StateDB{ db: db, trie: tr, @@ -150,7 +153,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) stateObjects: make(map[common.Address]*stateObject), stateObjectsPending: make(map[common.Address]struct{}), stateObjectsDirty: make(map[common.Address]struct{}), - logs: make(map[common.Hash][]*types.Log), + logs: logs, preimages: make(map[common.Hash][]byte), journal: newJournal(), accessList: newAccessList(), @@ -206,30 +209,38 @@ func (s *StateDB) AddLog(log *types.Log) { s.journal.append(addLogChange{txhash: s.thash}) s.journalMutex.Unlock() - s.mutex.Lock() - defer s.mutex.Unlock() - log.TxHash = s.thash log.BlockHash = s.bhash log.TxIndex = uint(s.txIndex) log.Index = s.logSize - s.logs[s.thash] = append(s.logs[s.thash], log) + + var logs []*types.Log + txHashLogs, ok := s.logs.Load(s.thash) + if ok { + logs = txHashLogs.([]*types.Log) + } + s.logs.Store(s.thash, append(logs, log)) + s.logSize++ } func (s *StateDB) GetLogs(hash common.Hash) []*types.Log { - s.mutex.RLock() - defer s.mutex.RUnlock() - return s.logs[hash] + txLogs, ok := s.logs.Load(hash) + if !ok { + return make([]*types.Log, 0) + } + return txLogs.([]*types.Log) } func (s *StateDB) Logs() []*types.Log { - s.mutex.RLock() - defer s.mutex.RUnlock() var logs []*types.Log - for _, lgs := range s.logs { - logs = append(logs, lgs...) - } + + s.logs.Range(func(key, value interface{}) bool { + logValue := value.([]*types.Log) + logs = append(logs, logValue...) + return true + }) + return logs } @@ -349,7 +360,6 @@ func (s *StateDB) Reset(root common.Hash) error { s.stateObjects = make(map[common.Address]*stateObject) s.stateObjectsPending = make(map[common.Address]struct{}) s.stateObjectsDirty = make(map[common.Address]struct{}) - s.logs = make(map[common.Hash][]*types.Log) s.logSize = 0 s.mutex.Unlock() s.thash = common.Hash{} @@ -358,6 +368,12 @@ func (s *StateDB) Reset(root common.Hash) error { s.preimages = make(map[common.Hash][]byte) s.clearJournalAndRefund() + // Clear all entries + s.logs.Range(func(key, value interface{}) bool { + s.logs.Delete(key) + return true + }) + if s.snaps != nil { s.snapAccounts, s.snapDestructs, s.snapStorage = nil, nil, nil if s.snap = s.snaps.Snapshot(root); s.snap != nil { @@ -825,7 +841,9 @@ func (s *StateDB) Copy() *StateDB { } journal.mutex.Unlock() - s.mutex.RLock() + // Instantiate new copy of "logs" + var copyOfLogs sync.Map + // Copy all the basic fields, initialize the memory ones state := &StateDB{ db: s.db, @@ -834,7 +852,7 @@ func (s *StateDB) Copy() *StateDB { stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)), stateObjectsDirty: make(map[common.Address]struct{}, size), refund: s.refund, - logs: make(map[common.Hash][]*types.Log, len(s.logs)), + logs: copyOfLogs, logSize: s.logSize, preimages: make(map[common.Hash][]byte, len(s.preimages)), journal: newJournal(), @@ -843,6 +861,7 @@ func (s *StateDB) Copy() *StateDB { accountExtraDataTrie: s.db.CopyTrie(s.accountExtraDataTrie), } + s.mutex.Lock() // Copy the dirty states, logs, and preimages for _, addr := range dirties { // As documented [here](https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527), @@ -874,15 +893,13 @@ func (s *StateDB) Copy() *StateDB { } state.stateObjectsDirty[addr] = struct{}{} } - for hash, logs := range s.logs { - cpy := make([]*types.Log, len(logs)) - for i, l := range logs { - cpy[i] = new(types.Log) - *cpy[i] = *l - } - state.logs[hash] = cpy - } - s.mutex.RUnlock() + s.mutex.Unlock() + + s.logs.Range(func(key, value interface{}) bool { + state.logs.Store(key, value) + return true + }) + for hash, preimage := range s.preimages { state.preimages[hash] = preimage }