diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index e410522ac..bb9909e2f 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -455,8 +455,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { b.mu.Lock() defer b.mu.Unlock() - - return b.pendingState.GetOrNewStateObject(account).Nonce(), nil + return b.pendingState.GetNonce(account), nil } // SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated @@ -617,8 +616,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM call.Value = new(big.Int) } // Set infinite balance to the fake caller account. - from := stateDB.GetOrNewStateObject(call.From) - from.SetBalance(math.MaxBig256) + stateDB.SetBalance(call.From, math.MaxBig256) // Execute the call. msg := callMsg{call} diff --git a/core/state/adapter.go b/core/state/adapter.go new file mode 100644 index 000000000..787846abd --- /dev/null +++ b/core/state/adapter.go @@ -0,0 +1,141 @@ +package state + +import ( + "encoding/json" + substate "github.com/Fantom-foundation/Substate" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state/snapshot" + "github.com/ethereum/go-ethereum/core/types" + "math/big" + "time" +) + +type StateDbInterface interface { + StartPrefetcher(namespace string) + StopPrefetcher() + Error() error + AddLog(log *types.Log) + GetLogs(hash common.Hash, blockHash common.Hash) []*types.Log + Logs() []*types.Log + AddPreimage(hash common.Hash, preimage []byte) + Preimages() map[common.Hash][]byte + AddRefund(gas uint64) + SubRefund(gas uint64) + Exist(addr common.Address) bool + Empty(addr common.Address) bool + GetBalance(addr common.Address) *big.Int + GetNonce(addr common.Address) uint64 + TxIndex() int + GetCode(addr common.Address) []byte + GetCodeSize(addr common.Address) int + GetCodeHash(addr common.Address) common.Hash + GetState(addr common.Address, hash common.Hash) common.Hash + GetProof(addr common.Address) ([][]byte, error) + GetProofByHash(addrHash common.Hash) ([][]byte, error) + GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) + GetCommittedState(addr common.Address, hash common.Hash) common.Hash + Database() Database + StorageTrie(addr common.Address) Trie + HasSuicided(addr common.Address) bool + AddBalance(addr common.Address, amount *big.Int) + SubBalance(addr common.Address, amount *big.Int) + SetBalance(addr common.Address, amount *big.Int) + SetNonce(addr common.Address, nonce uint64) + SetCode(addr common.Address, code []byte) + SetState(addr common.Address, key, value common.Hash) + SetStorage(addr common.Address, storage map[common.Hash]common.Hash) + Suicide(addr common.Address) bool + CreateAccount(addr common.Address) + ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error + Copy() StateDbInterface + Snapshot() int + RevertToSnapshot(revid int) + GetRefund() uint64 + Finalise(deleteEmptyObjects bool) + IntermediateRoot(deleteEmptyObjects bool) common.Hash + Prepare(thash common.Hash, ti int) + Commit(deleteEmptyObjects bool) (common.Hash, error) + PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) + AddAddressToAccessList(addr common.Address) + AddSlotToAccessList(addr common.Address, slot common.Hash) + AddressInAccessList(addr common.Address) bool + SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) + + RawDump(opts *DumpConfig) Dump + IteratorDump(opts *DumpConfig) IteratorDump + IterativeDump(opts *DumpConfig, output *json.Encoder) + Dump(opts *DumpConfig) []byte + DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []byte) + + GetAccountReads() time.Duration + GetAccountHashes() time.Duration + GetAccountUpdates() time.Duration + GetAccountCommits() time.Duration + GetStorageReads() time.Duration + GetStorageHashes() time.Duration + GetStorageUpdates() time.Duration + GetStorageCommits() time.Duration + GetSnapshotAccountReads() time.Duration + GetSnapshotStorageReads() time.Duration + GetSnapshotCommits() time.Duration + + SetPrehashedCode(addr common.Address, hash common.Hash, code []byte) + GetSubstatePostAlloc() substate.SubstateAlloc + BeginBlock(number uint64) + EndBlock(number uint64) + Release() +} + +type StateDB struct { + StateDbInterface + + // Measurements gathered during execution for debugging purposes + AccountReads time.Duration + AccountHashes time.Duration + AccountUpdates time.Duration + AccountCommits time.Duration + StorageReads time.Duration + StorageHashes time.Duration + StorageUpdates time.Duration + StorageCommits time.Duration + SnapshotAccountReads time.Duration + SnapshotStorageReads time.Duration + SnapshotCommits time.Duration +} + +func (s *StateDB) Copy() *StateDB { + return &StateDB{s.StateDbInterface.Copy(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +} + +func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { + hash, err := s.StateDbInterface.Commit(deleteEmptyObjects) + s.AccountReads = s.GetAccountReads() + s.AccountHashes = s.GetAccountHashes() + s.AccountUpdates = s.GetAccountUpdates() + s.AccountCommits = s.GetAccountCommits() + s.StorageReads = s.GetStorageReads() + s.StorageHashes = s.GetStorageHashes() + s.StorageUpdates = s.GetStorageUpdates() + s.StorageCommits = s.GetStorageCommits() + s.SnapshotAccountReads = s.GetSnapshotAccountReads() + s.SnapshotStorageReads = s.GetSnapshotStorageReads() + s.SnapshotCommits = s.GetSnapshotCommits() + return hash, err +} + +// New creates a new state from a given trie. +func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) { + return NewWithSnapLayers(root, db, snaps, 128) +} + +func NewWithSnapLayers(root common.Hash, db Database, snaps *snapshot.Tree, layers int) (*StateDB, error) { + sdb, err := NewLegacyWithSnapLayers(root, db, snaps, layers) + if err != nil { + return nil, err + } + return NewWrapper(sdb), nil +} + +func NewWrapper(inner StateDbInterface) *StateDB { + return &StateDB{inner, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +} diff --git a/core/state/dump.go b/core/state/dump.go index 00faa4ed6..6b48f5b64 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -124,7 +124,7 @@ func (d iterativeDump) OnRoot(root common.Hash) { // DumpToCollector iterates the state according to the given options and inserts // the items into a collector for aggregation or serialization. -func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []byte) { +func (s *LegacyStateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []byte) { // Sanitize the input to allow nil configs if conf == nil { conf = new(DumpConfig) @@ -201,7 +201,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] } // RawDump returns the entire state an a single large object -func (s *StateDB) RawDump(opts *DumpConfig) Dump { +func (s *LegacyStateDB) RawDump(opts *DumpConfig) Dump { dump := &Dump{ Accounts: make(map[common.Address]DumpAccount), } @@ -210,7 +210,7 @@ func (s *StateDB) RawDump(opts *DumpConfig) Dump { } // Dump returns a JSON string representing the entire state as a single json-object -func (s *StateDB) Dump(opts *DumpConfig) []byte { +func (s *LegacyStateDB) Dump(opts *DumpConfig) []byte { dump := s.RawDump(opts) json, err := json.MarshalIndent(dump, "", " ") if err != nil { @@ -220,12 +220,12 @@ func (s *StateDB) Dump(opts *DumpConfig) []byte { } // IterativeDump dumps out accounts as json-objects, delimited by linebreaks on stdout -func (s *StateDB) IterativeDump(opts *DumpConfig, output *json.Encoder) { +func (s *LegacyStateDB) IterativeDump(opts *DumpConfig, output *json.Encoder) { s.DumpToCollector(iterativeDump{output}, opts) } // IteratorDump dumps out a batch of accounts starts with the given start key -func (s *StateDB) IteratorDump(opts *DumpConfig) IteratorDump { +func (s *LegacyStateDB) IteratorDump(opts *DumpConfig) IteratorDump { iterator := &IteratorDump{ Accounts: make(map[common.Address]DumpAccount), } diff --git a/core/state/iterator.go b/core/state/iterator.go index 6a5c73d3d..1e9f501bc 100644 --- a/core/state/iterator.go +++ b/core/state/iterator.go @@ -28,7 +28,7 @@ import ( // NodeIterator is an iterator to traverse the entire state trie post-order, // including all of the contract code and contract state tries. type NodeIterator struct { - state *StateDB // State being iterated + state *LegacyStateDB // State being iterated stateIt trie.NodeIterator // Primary iterator for the global state trie dataIt trie.NodeIterator // Secondary iterator for the data trie of a contract @@ -44,7 +44,7 @@ type NodeIterator struct { } // NewNodeIterator creates an post-order state node iterator. -func NewNodeIterator(state *StateDB) *NodeIterator { +func NewNodeIterator(state *LegacyStateDB) *NodeIterator { return &NodeIterator{ state: state, } diff --git a/core/state/journal.go b/core/state/journal.go index c902f787e..b90ea4396 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -26,7 +26,7 @@ import ( // reverted on demand. type journalEntry interface { // revert undoes the changes introduced by this journal entry. - revert(*StateDB) + revert(*LegacyStateDB) // dirtied returns the Ethereum address modified by this journal entry. dirtied() *common.Address @@ -57,7 +57,7 @@ func (j *journal) append(entry journalEntry) { // revert undoes a batch of journalled modifications along with any reverted // dirty handling too. -func (j *journal) revert(statedb *StateDB, snapshot int) { +func (j *journal) revert(statedb *LegacyStateDB, snapshot int) { for i := len(j.entries) - 1; i >= snapshot; i-- { // Undo the changes made by the operation j.entries[i].revert(statedb) @@ -140,7 +140,7 @@ type ( } ) -func (ch createObjectChange) revert(s *StateDB) { +func (ch createObjectChange) revert(s *LegacyStateDB) { delete(s.stateObjects, *ch.account) delete(s.stateObjectsDirty, *ch.account) } @@ -149,7 +149,7 @@ func (ch createObjectChange) dirtied() *common.Address { return ch.account } -func (ch resetObjectChange) revert(s *StateDB) { +func (ch resetObjectChange) revert(s *LegacyStateDB) { s.setStateObject(ch.prev) if !ch.prevdestruct && s.snap != nil { delete(s.snapDestructs, ch.prev.addrHash) @@ -162,7 +162,7 @@ func (ch resetObjectChange) dirtied() *common.Address { return &ch.prev.address } -func (ch suicideChange) revert(s *StateDB) { +func (ch suicideChange) revert(s *LegacyStateDB) { obj := s.getStateObject(*ch.account) if obj != nil { obj.suicided = ch.prev @@ -176,14 +176,14 @@ func (ch suicideChange) dirtied() *common.Address { var ripemd = common.HexToAddress("0000000000000000000000000000000000000003") -func (ch touchChange) revert(s *StateDB) { +func (ch touchChange) revert(s *LegacyStateDB) { } func (ch touchChange) dirtied() *common.Address { return ch.account } -func (ch balanceChange) revert(s *StateDB) { +func (ch balanceChange) revert(s *LegacyStateDB) { s.getStateObject(*ch.account).setBalance(ch.prev) } @@ -191,7 +191,7 @@ func (ch balanceChange) dirtied() *common.Address { return ch.account } -func (ch nonceChange) revert(s *StateDB) { +func (ch nonceChange) revert(s *LegacyStateDB) { s.getStateObject(*ch.account).setNonce(ch.prev) } @@ -199,7 +199,7 @@ func (ch nonceChange) dirtied() *common.Address { return ch.account } -func (ch codeChange) revert(s *StateDB) { +func (ch codeChange) revert(s *LegacyStateDB) { s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) } @@ -207,7 +207,7 @@ func (ch codeChange) dirtied() *common.Address { return ch.account } -func (ch storageChange) revert(s *StateDB) { +func (ch storageChange) revert(s *LegacyStateDB) { s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) } @@ -215,7 +215,7 @@ func (ch storageChange) dirtied() *common.Address { return ch.account } -func (ch refundChange) revert(s *StateDB) { +func (ch refundChange) revert(s *LegacyStateDB) { s.refund = ch.prev } @@ -223,7 +223,7 @@ func (ch refundChange) dirtied() *common.Address { return nil } -func (ch addLogChange) revert(s *StateDB) { +func (ch addLogChange) revert(s *LegacyStateDB) { logs := s.logs[ch.txhash] if len(logs) == 1 { delete(s.logs, ch.txhash) @@ -237,7 +237,7 @@ func (ch addLogChange) dirtied() *common.Address { return nil } -func (ch addPreimageChange) revert(s *StateDB) { +func (ch addPreimageChange) revert(s *LegacyStateDB) { delete(s.preimages, ch.hash) } @@ -245,7 +245,7 @@ func (ch addPreimageChange) dirtied() *common.Address { return nil } -func (ch accessListAddAccountChange) revert(s *StateDB) { +func (ch accessListAddAccountChange) revert(s *LegacyStateDB) { /* One important invariant here, is that whenever a (addr, slot) is added, if the addr is not already present, the add causes two journal entries: @@ -262,7 +262,7 @@ func (ch accessListAddAccountChange) dirtied() *common.Address { return nil } -func (ch accessListAddSlotChange) revert(s *StateDB) { +func (ch accessListAddSlotChange) revert(s *LegacyStateDB) { s.accessList.DeleteSlot(*ch.address, *ch.slot) } diff --git a/core/state/state_object.go b/core/state/state_object.go index 1c7bb579d..990deee76 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -67,7 +67,7 @@ type stateObject struct { address common.Address addrHash common.Hash // hash of ethereum address of the account data Account - db *StateDB + db *LegacyStateDB // DB error. // State objects are used by the consensus core and VM which are @@ -111,7 +111,7 @@ type Account struct { } // newObject creates a state object. -func newObject(db *StateDB, address common.Address, data Account) *stateObject { +func newObject(db *LegacyStateDB, address common.Address, data Account) *stateObject { if data.Balance == nil { data.Balance = new(big.Int) } @@ -466,7 +466,7 @@ func (s *stateObject) setBalance(amount *big.Int) { s.data.Balance = amount } -func (s *stateObject) deepCopy(db *StateDB) *stateObject { +func (s *stateObject) deepCopy(db *LegacyStateDB) *stateObject { stateObject := newObject(db, s.address, s.data) if s.trie != nil { stateObject.trie = db.db.CopyTrie(s.trie) diff --git a/core/state/statedb.go b/core/state/statedb.go index 5bbdc9e2f..2b1f2711a 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -57,12 +57,12 @@ func (n *proofList) Delete(key []byte) error { panic("not supported") } -// StateDB structs within the ethereum protocol are used to store anything -// within the merkle trie. StateDBs take care of caching and storing +// LegacyStateDB structs within the ethereum protocol are used to store anything +// within the merkle trie. LegacyStateDBs take care of caching and storing // nested states. It's the general query interface to retrieve: // * Contracts // * Accounts -type StateDB struct { +type LegacyStateDB struct { db Database prefetcher *triePrefetcher originalRoot common.Hash // The pre-state root, before any changes were made @@ -85,7 +85,7 @@ type StateDB struct { // State objects are used by the consensus core and VM which are // unable to deal with database-level errors. Any error that occurs // during a database read is memoized here and will eventually be returned - // by StateDB.Commit. + // by LegacyStateDB.Commit. dbErr error // The refund counter, also used by state transitioning. @@ -126,17 +126,12 @@ type StateDB struct { SubstateBlockHashes map[uint64]common.Hash } -// New creates a new state from a given trie. -func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) { - return NewWithSnapLayers(root, db, snaps, 128) -} - -func NewWithSnapLayers(root common.Hash, db Database, snaps *snapshot.Tree, layers int) (*StateDB, error) { +func NewLegacyWithSnapLayers(root common.Hash, db Database, snaps *snapshot.Tree, layers int) (StateDbInterface, error) { tr, err := db.OpenTrie(root) if err != nil { return nil, err } - sdb := &StateDB{ + sdb := &LegacyStateDB{ db: db, trie: tr, originalRoot: root, @@ -172,7 +167,7 @@ func NewWithSnapLayers(root common.Hash, db Database, snaps *snapshot.Tree, laye // StartPrefetcher initializes a new trie prefetcher to pull in nodes from the // state trie concurrently while the state is mutated so that when we reach the // commit phase, most of the needed data is already hot. -func (s *StateDB) StartPrefetcher(namespace string) { +func (s *LegacyStateDB) StartPrefetcher(namespace string) { if s.prefetcher != nil { s.prefetcher.close() s.prefetcher = nil @@ -184,7 +179,7 @@ func (s *StateDB) StartPrefetcher(namespace string) { // StopPrefetcher terminates a running prefetcher and reports any leftover stats // from the gathered metrics. -func (s *StateDB) StopPrefetcher() { +func (s *LegacyStateDB) StopPrefetcher() { if s.prefetcher != nil { s.prefetcher.close() s.prefetcher = nil @@ -192,17 +187,17 @@ func (s *StateDB) StopPrefetcher() { } // setError remembers the first non-nil error it is called with. -func (s *StateDB) setError(err error) { +func (s *LegacyStateDB) setError(err error) { if s.dbErr == nil { s.dbErr = err } } -func (s *StateDB) Error() error { +func (s *LegacyStateDB) Error() error { return s.dbErr } -func (s *StateDB) AddLog(log *types.Log) { +func (s *LegacyStateDB) AddLog(log *types.Log) { s.journal.append(addLogChange{txhash: s.thash}) log.TxHash = s.thash @@ -212,7 +207,7 @@ func (s *StateDB) AddLog(log *types.Log) { s.logSize++ } -func (s *StateDB) GetLogs(hash common.Hash, blockHash common.Hash) []*types.Log { +func (s *LegacyStateDB) GetLogs(hash common.Hash, blockHash common.Hash) []*types.Log { logs := s.logs[hash] for _, l := range logs { l.BlockHash = blockHash @@ -220,7 +215,7 @@ func (s *StateDB) GetLogs(hash common.Hash, blockHash common.Hash) []*types.Log return logs } -func (s *StateDB) Logs() []*types.Log { +func (s *LegacyStateDB) Logs() []*types.Log { var logs []*types.Log for _, lgs := range s.logs { logs = append(logs, lgs...) @@ -229,7 +224,7 @@ func (s *StateDB) Logs() []*types.Log { } // AddPreimage records a SHA3 preimage seen by the VM. -func (s *StateDB) AddPreimage(hash common.Hash, preimage []byte) { +func (s *LegacyStateDB) AddPreimage(hash common.Hash, preimage []byte) { if _, ok := s.preimages[hash]; !ok { s.journal.append(addPreimageChange{hash: hash}) pi := make([]byte, len(preimage)) @@ -239,19 +234,19 @@ func (s *StateDB) AddPreimage(hash common.Hash, preimage []byte) { } // Preimages returns a list of SHA3 preimages that have been submitted. -func (s *StateDB) Preimages() map[common.Hash][]byte { +func (s *LegacyStateDB) Preimages() map[common.Hash][]byte { return s.preimages } // AddRefund adds gas to the refund counter -func (s *StateDB) AddRefund(gas uint64) { +func (s *LegacyStateDB) AddRefund(gas uint64) { s.journal.append(refundChange{prev: s.refund}) s.refund += gas } // SubRefund removes gas from the refund counter. // This method will panic if the refund counter goes below zero -func (s *StateDB) SubRefund(gas uint64) { +func (s *LegacyStateDB) SubRefund(gas uint64) { s.journal.append(refundChange{prev: s.refund}) if gas > s.refund { panic(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", gas, s.refund)) @@ -261,19 +256,19 @@ func (s *StateDB) SubRefund(gas uint64) { // Exist reports whether the given account address exists in the state. // Notably this also returns true for suicided accounts. -func (s *StateDB) Exist(addr common.Address) bool { +func (s *LegacyStateDB) Exist(addr common.Address) bool { return s.getStateObject(addr) != nil } // Empty returns whether the state object is either non-existent // or empty according to the EIP161 specification (balance = nonce = code = 0) -func (s *StateDB) Empty(addr common.Address) bool { +func (s *LegacyStateDB) Empty(addr common.Address) bool { so := s.getStateObject(addr) return so == nil || so.empty() } // GetBalance retrieves the balance from the given address or 0 if object not found -func (s *StateDB) GetBalance(addr common.Address) *big.Int { +func (s *LegacyStateDB) GetBalance(addr common.Address) *big.Int { stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.Balance() @@ -281,7 +276,7 @@ func (s *StateDB) GetBalance(addr common.Address) *big.Int { return common.Big0 } -func (s *StateDB) GetNonce(addr common.Address) uint64 { +func (s *LegacyStateDB) GetNonce(addr common.Address) uint64 { stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.Nonce() @@ -291,11 +286,11 @@ func (s *StateDB) GetNonce(addr common.Address) uint64 { } // TxIndex returns the current transaction index set by Prepare. -func (s *StateDB) TxIndex() int { +func (s *LegacyStateDB) TxIndex() int { return s.txIndex } -func (s *StateDB) GetCode(addr common.Address) []byte { +func (s *LegacyStateDB) GetCode(addr common.Address) []byte { stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.Code(s.db) @@ -303,7 +298,7 @@ func (s *StateDB) GetCode(addr common.Address) []byte { return nil } -func (s *StateDB) GetCodeSize(addr common.Address) int { +func (s *LegacyStateDB) GetCodeSize(addr common.Address) int { stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.CodeSize(s.db) @@ -311,7 +306,7 @@ func (s *StateDB) GetCodeSize(addr common.Address) int { return 0 } -func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { +func (s *LegacyStateDB) GetCodeHash(addr common.Address) common.Hash { stateObject := s.getStateObject(addr) if stateObject == nil { return common.Hash{} @@ -320,7 +315,7 @@ func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { } // GetState retrieves a value from the given account's storage trie. -func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash { +func (s *LegacyStateDB) GetState(addr common.Address, hash common.Hash) common.Hash { stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.GetState(s.db, hash) @@ -329,19 +324,19 @@ func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash { } // GetProof returns the Merkle proof for a given account. -func (s *StateDB) GetProof(addr common.Address) ([][]byte, error) { +func (s *LegacyStateDB) GetProof(addr common.Address) ([][]byte, error) { return s.GetProofByHash(crypto.Keccak256Hash(addr.Bytes())) } // GetProofByHash returns the Merkle proof for a given account. -func (s *StateDB) GetProofByHash(addrHash common.Hash) ([][]byte, error) { +func (s *LegacyStateDB) GetProofByHash(addrHash common.Hash) ([][]byte, error) { var proof proofList err := s.trie.Prove(addrHash[:], 0, &proof) return proof, err } // GetStorageProof returns the Merkle proof for given storage slot. -func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) { +func (s *LegacyStateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) { var proof proofList trie := s.StorageTrie(a) if trie == nil { @@ -352,7 +347,7 @@ func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, } // GetCommittedState retrieves a value from the given account's committed storage trie. -func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { +func (s *LegacyStateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.GetCommittedState(s.db, hash) @@ -361,13 +356,13 @@ func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) commo } // Database retrieves the low level database supporting the lower level trie ops. -func (s *StateDB) Database() Database { +func (s *LegacyStateDB) Database() Database { return s.db } // StorageTrie returns the storage trie of an account. // The return value is a copy and is nil for non-existent accounts. -func (s *StateDB) StorageTrie(addr common.Address) Trie { +func (s *LegacyStateDB) StorageTrie(addr common.Address) Trie { stateObject := s.getStateObject(addr) if stateObject == nil { return nil @@ -377,7 +372,7 @@ func (s *StateDB) StorageTrie(addr common.Address) Trie { return cpy.getTrie(s.db) } -func (s *StateDB) HasSuicided(addr common.Address) bool { +func (s *LegacyStateDB) HasSuicided(addr common.Address) bool { stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.suicided @@ -390,7 +385,7 @@ func (s *StateDB) HasSuicided(addr common.Address) bool { */ // AddBalance adds amount to the account associated with addr. -func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) { +func (s *LegacyStateDB) AddBalance(addr common.Address, amount *big.Int) { stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { stateObject.AddBalance(amount) @@ -398,42 +393,42 @@ func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) { } // SubBalance subtracts amount from the account associated with addr. -func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) { +func (s *LegacyStateDB) SubBalance(addr common.Address, amount *big.Int) { stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { stateObject.SubBalance(amount) } } -func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) { +func (s *LegacyStateDB) SetBalance(addr common.Address, amount *big.Int) { stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { stateObject.SetBalance(amount) } } -func (s *StateDB) SetNonce(addr common.Address, nonce uint64) { +func (s *LegacyStateDB) SetNonce(addr common.Address, nonce uint64) { stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { stateObject.SetNonce(nonce) } } -func (s *StateDB) SetCode(addr common.Address, code []byte) { +func (s *LegacyStateDB) SetCode(addr common.Address, code []byte) { stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { stateObject.SetCode(crypto.Keccak256Hash(code), code) } } -func (s *StateDB) SetPrehashedCode(addr common.Address, hash common.Hash, code []byte) { +func (s *LegacyStateDB) SetPrehashedCode(addr common.Address, hash common.Hash, code []byte) { stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { stateObject.SetCode(hash, code) } } -func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { +func (s *LegacyStateDB) SetState(addr common.Address, key, value common.Hash) { stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { stateObject.SetState(s.db, key, value) @@ -442,7 +437,7 @@ func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { // SetStorage replaces the entire storage for the specified account with given // storage. This function should only be used for debugging. -func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common.Hash) { +func (s *LegacyStateDB) SetStorage(addr common.Address, storage map[common.Hash]common.Hash) { stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { stateObject.SetStorage(storage) @@ -454,7 +449,7 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common // // The account's state object is still available until the state is committed, // getStateObject will return a non-nil account after Suicide. -func (s *StateDB) Suicide(addr common.Address) bool { +func (s *LegacyStateDB) Suicide(addr common.Address) bool { stateObject := s.getStateObject(addr) if stateObject == nil { return false @@ -475,7 +470,7 @@ func (s *StateDB) Suicide(addr common.Address) bool { // // updateStateObject writes the given object to the trie. -func (s *StateDB) updateStateObject(obj *stateObject) { +func (s *LegacyStateDB) updateStateObject(obj *stateObject) { // Track the amount of time wasted on updating the account from the trie if metrics.EnabledExpensive { defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) @@ -501,7 +496,7 @@ func (s *StateDB) updateStateObject(obj *stateObject) { } // deleteStateObject removes the given object from the state trie. -func (s *StateDB) deleteStateObject(obj *stateObject) { +func (s *LegacyStateDB) deleteStateObject(obj *stateObject) { // Track the amount of time wasted on deleting the account from the trie if metrics.EnabledExpensive { defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) @@ -516,7 +511,7 @@ func (s *StateDB) deleteStateObject(obj *stateObject) { // getStateObject retrieves a state object given by the address, returning nil if // the object is not found or was deleted in this execution context. If you need // to differentiate between non-existent/just-deleted, use getDeletedStateObject. -func (s *StateDB) getStateObject(addr common.Address) *stateObject { +func (s *LegacyStateDB) getStateObject(addr common.Address) *stateObject { if obj := s.getDeletedStateObject(addr); obj != nil && !obj.deleted { if substate.RecordReplay { @@ -544,7 +539,7 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject { // nil for a deleted state object, it returns the actual object with the deleted // flag set. This is needed by the state journal to revert to the correct s- // destructed object instead of wiping all knowledge about the state object. -func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { +func (s *LegacyStateDB) getDeletedStateObject(addr common.Address) *stateObject { // Prefer live objects if any is available if obj := s.stateObjects[addr]; obj != nil { return obj @@ -602,12 +597,12 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { return obj } -func (s *StateDB) setStateObject(object *stateObject) { +func (s *LegacyStateDB) setStateObject(object *stateObject) { s.stateObjects[object.Address()] = object } // GetOrNewStateObject retrieves a state object or create a new state object if nil. -func (s *StateDB) GetOrNewStateObject(addr common.Address) *stateObject { +func (s *LegacyStateDB) GetOrNewStateObject(addr common.Address) *stateObject { stateObject := s.getStateObject(addr) if stateObject == nil { stateObject, _ = s.createObject(addr) @@ -617,7 +612,7 @@ func (s *StateDB) GetOrNewStateObject(addr common.Address) *stateObject { // createObject creates a new state object. If there is an existing account with // 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 *LegacyStateDB) createObject(addr common.Address) (newobj, prev *stateObject) { prev = s.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that! var prevdestruct bool @@ -650,14 +645,14 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) // 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1) // // Carrying over the balance ensures that Ether doesn't disappear. -func (s *StateDB) CreateAccount(addr common.Address) { +func (s *LegacyStateDB) CreateAccount(addr common.Address) { newObj, prev := s.createObject(addr) if prev != nil { newObj.setBalance(prev.data.Balance) } } -func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error { +func (db *LegacyStateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error { so := db.getStateObject(addr) if so == nil { return nil @@ -688,9 +683,9 @@ func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common // Copy creates a deep, independent copy of the state. // Snapshots of the copied state cannot be applied to the copy. -func (s *StateDB) Copy() *StateDB { +func (s *LegacyStateDB) Copy() StateDbInterface { // Copy all the basic fields, initialize the memory ones - state := &StateDB{ + state := &LegacyStateDB{ db: s.db, trie: s.db.CopyTrie(s.trie), stateObjects: make(map[common.Address]*stateObject, len(s.journal.dirties)), @@ -804,7 +799,7 @@ func (s *StateDB) Copy() *StateDB { } // Snapshot returns an identifier for the current revision of the state. -func (s *StateDB) Snapshot() int { +func (s *LegacyStateDB) Snapshot() int { id := s.nextRevisionId s.nextRevisionId++ s.validRevisions = append(s.validRevisions, revision{id, s.journal.length()}) @@ -812,7 +807,7 @@ func (s *StateDB) Snapshot() int { } // RevertToSnapshot reverts all state changes made since the given revision. -func (s *StateDB) RevertToSnapshot(revid int) { +func (s *LegacyStateDB) RevertToSnapshot(revid int) { // Find the snapshot in the stack of valid snapshots. idx := sort.Search(len(s.validRevisions), func(i int) bool { return s.validRevisions[i].id >= revid @@ -828,14 +823,14 @@ func (s *StateDB) RevertToSnapshot(revid int) { } // GetRefund returns the current value of the refund counter. -func (s *StateDB) GetRefund() uint64 { +func (s *LegacyStateDB) GetRefund() uint64 { return s.refund } // Finalise finalises the state by removing the s destructed objects and clears // the journal as well as the refunds. Finalise, however, will not push any updates // into the tries just yet. Only IntermediateRoot or Commit will do that. -func (s *StateDB) Finalise(deleteEmptyObjects bool) { +func (s *LegacyStateDB) Finalise(deleteEmptyObjects bool) { if substate.RecordReplay { // copy original storage values to Prestate and Poststate @@ -910,12 +905,12 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { // IntermediateRoot computes the current root hash of the state trie. // It is called in between transactions to get the root hash that // goes into transaction receipts. -func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { +func (s *LegacyStateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // Finalise all the dirty storage states and write them into the tries s.Finalise(deleteEmptyObjects) // If there was a trie prefetcher operating, it gets aborted and irrevocably - // modified after we start retrieving tries. Remove it from the statedb after + // modified after we start retrieving tries. Remove it from the LegacyStateDB after // this round of use. // // This is weird pre-byzantium since the first tx runs with a prefetcher and @@ -970,7 +965,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // Prepare sets the current transaction hash and index which are // used when the EVM emits new state logs. -func (s *StateDB) Prepare(thash common.Hash, ti int) { +func (s *LegacyStateDB) Prepare(thash common.Hash, ti int) { s.thash = thash s.txIndex = ti @@ -987,7 +982,7 @@ func (s *StateDB) Prepare(thash common.Hash, ti int) { s.accessList = newAccessList() } -func (s *StateDB) clearJournalAndRefund() { +func (s *LegacyStateDB) clearJournalAndRefund() { if len(s.journal.entries) > 0 { s.journal = newJournal() s.refund = 0 @@ -996,7 +991,7 @@ func (s *StateDB) clearJournalAndRefund() { } // Commit writes the state to the underlying in-memory trie database. -func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { +func (s *LegacyStateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { if s.dbErr != nil { return common.Hash{}, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr) } @@ -1078,7 +1073,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { // - Add the contents of the optional tx access list (2930) // // This method should only be called if Berlin/2929+2930 is applicable at the current number. -func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { +func (s *LegacyStateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { s.AddAddressToAccessList(sender) if dst != nil { s.AddAddressToAccessList(*dst) @@ -1096,14 +1091,14 @@ func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, } // AddAddressToAccessList adds the given address to the access list -func (s *StateDB) AddAddressToAccessList(addr common.Address) { +func (s *LegacyStateDB) AddAddressToAccessList(addr common.Address) { if s.accessList.AddAddress(addr) { s.journal.append(accessListAddAccountChange{&addr}) } } // AddSlotToAccessList adds the given (address, slot)-tuple to the access list -func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { +func (s *LegacyStateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { addrMod, slotMod := s.accessList.AddSlot(addr, slot) if addrMod { // In practice, this should not happen, since there is no way to enter the @@ -1121,15 +1116,61 @@ func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { } // AddressInAccessList returns true if the given address is in the access list. -func (s *StateDB) AddressInAccessList(addr common.Address) bool { +func (s *LegacyStateDB) AddressInAccessList(addr common.Address) bool { return s.accessList.ContainsAddress(addr) } // SlotInAccessList returns true if the given (address, slot)-tuple is in the access list. -func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { +func (s *LegacyStateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { return s.accessList.Contains(addr, slot) } -func (s *StateDB) GetSubstatePostAlloc() substate.SubstateAlloc { +func (s *LegacyStateDB) GetSubstatePostAlloc() substate.SubstateAlloc { return s.SubstatePostAlloc } + +func (s *LegacyStateDB) BeginBlock(number uint64) { + // not used by LegacyStateDB +} + +func (s *LegacyStateDB) EndBlock(number uint64) { + // not used by LegacyStateDB +} + +func (s *LegacyStateDB) Release() { + // not used by LegacyStateDB +} + +func (s *LegacyStateDB) GetAccountReads() time.Duration { + return s.AccountReads +} +func (s *LegacyStateDB) GetAccountHashes() time.Duration { + return s.AccountHashes +} +func (s *LegacyStateDB) GetAccountUpdates() time.Duration { + return s.AccountUpdates +} +func (s *LegacyStateDB) GetAccountCommits() time.Duration { + return s.AccountCommits +} +func (s *LegacyStateDB) GetStorageReads() time.Duration { + return s.StorageReads +} +func (s *LegacyStateDB) GetStorageHashes() time.Duration { + return s.StorageHashes +} +func (s *LegacyStateDB) GetStorageUpdates() time.Duration { + return s.StorageUpdates +} +func (s *LegacyStateDB) GetStorageCommits() time.Duration { + return s.StorageCommits +} +func (s *LegacyStateDB) GetSnapshotAccountReads() time.Duration { + return s.SnapshotAccountReads +} +func (s *LegacyStateDB) GetSnapshotStorageReads() time.Duration { + return s.SnapshotStorageReads +} +func (s *LegacyStateDB) GetSnapshotCommits() time.Duration { + return s.SnapshotCommits +} diff --git a/core/types/transaction.go b/core/types/transaction.go index 793d58857..a08c1c3d8 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -569,6 +569,25 @@ func (t *TransactionsByPriceAndNonce) Pop() { heap.Pop(&t.heads) } +// GetCountToPop provides the amount of txs, which will be removed if Pop is called +func (t *TransactionsByPriceAndNonce) GetCountToPop() int { + acc, _ := Sender(t.signer, t.heads[0].tx) + if txs, ok := t.txs[acc]; ok { + return len(txs) + 1 + } + return 0 +} + +// Len provides the total amount of txs in the structure +func (t *TransactionsByPriceAndNonce) Len() int { + length := 0 + for _, head := range t.heads { + acc, _ := Sender(t.signer, head.tx) + length += len(t.txs[acc]) + 1 + } + return length +} + func (t *TransactionsByPriceAndNonce) Copy() *TransactionsByPriceAndNonce { txsCopy := make(map[common.Address]Transactions, len(t.txs)) for k, v := range t.txs { diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 33522c873..e47fb5367 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -534,20 +534,28 @@ type CachedSigner struct { cache SenderCache } -func WrapWithCachedSigner(signer Signer, cache SenderCache) *CachedSigner { - return &CachedSigner{ +func WrapWithCachedSigner(signer Signer, cache SenderCache) CachedSigner { + return CachedSigner{ Signer: signer, cache: cache, } } + +func (cs CachedSigner) Equal(s2 Signer) bool { + cs2, ok := s2.(CachedSigner) + if ok { + // unwrap the signer + return cs.Signer.Equal(cs2.Signer) + } + return cs.Signer.Equal(s2) +} + func (cs CachedSigner) Sender(tx *Transaction) (common.Address, error) { - if tx.from.Load() == nil { - // try to load the sender from the global cache - cached := cs.cache.Get(tx.Hash()) - if cached != nil && cached.Signer.Equal(cs.Signer) { - return cached.From, nil - } + // try to load the sender from the global cache + cached := cs.cache.Get(tx.Hash()) + if cached != nil && cached.Signer.Equal(cs.Signer) { + return cached.From, nil } from, err := cs.Signer.Sender(tx) if err != nil { diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index 58c95071b..cc1f94a4a 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -527,3 +527,37 @@ func assertEqual(orig *Transaction, cpy *Transaction) error { } return nil } + +func TestTransactionsByPriceAndNonceLen(t *testing.T) { + keys := make([]*ecdsa.PrivateKey, 5) + for i := 0; i < len(keys); i++ { + keys[i], _ = crypto.GenerateKey() + } + signer := HomesteadSigner{} + groups := map[common.Address]Transactions{} + for _, key := range keys { + addr := crypto.PubkeyToAddress(key.PublicKey) + for nonce := uint64(0); nonce < 5; nonce++ { + tx, _ := SignTx(NewTransaction(nonce, common.Address{}, big.NewInt(100), 100, big.NewInt(1), nil), signer, key) + groups[addr] = append(groups[addr], tx) + } + } + txset := NewTransactionsByPriceAndNonce(signer, groups, nil) + if txset.Len() != 25 { + t.Errorf("intial len is %d", txset.Len()) + } + if txset.GetCountToPop() != 5 { + t.Errorf("intial amount to pop is %d", txset.GetCountToPop()) + } + txset.Pop() + if txset.Len() != 20 { + t.Errorf("length after first pop is %d", txset.Len()) + } + txset.Shift() + if txset.Len() != 19 { + t.Errorf("length after shift is %d", txset.Len()) + } + if txset.GetCountToPop() != 4 { + t.Errorf("amount to pop after shift is %d", txset.GetCountToPop()) + } +} diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 787f1a967..5e1189315 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -448,9 +448,10 @@ func opBlockhash(pc *uint64, interpreter *GethEVMInterpreter, scope *ScopeContex if substate.RecordReplay { // convert vm.StateDB to state.StateDB and save block hash defer func() { - statedb, ok := interpreter.evm.StateDB.(*state.StateDB) - if ok { - statedb.SubstateBlockHashes[num64] = common.BytesToHash(num.Bytes()) + if statedb, ok := interpreter.evm.StateDB.(*state.StateDB); ok { + if legacy, ok := statedb.StateDbInterface.(*state.LegacyStateDB); ok { + legacy.SubstateBlockHashes[num64] = common.BytesToHash(num.Bytes()) + } } }() } diff --git a/log/format.go b/log/format.go index baf8fddac..765b423d7 100644 --- a/log/format.go +++ b/log/format.go @@ -35,8 +35,16 @@ func PrintOrigins(print bool) { } else { atomic.StoreUint32(&locationEnabled, 0) } + if print { + stackEnabled.Store(true) + } } +// stackEnabled is an atomic flag controlling whether the log handler needs +// to store the callsite stack. This is needed in case any handler wants to +// print locations (locationEnabled), use vmodule, or print full stacks (BacktraceAt). +var stackEnabled atomic.Bool + // locationEnabled is an atomic flag controlling whether the terminal formatter // should append the log locations too when printing entries. var locationEnabled uint32 diff --git a/log/handler_glog.go b/log/handler_glog.go index 9b1d4efaf..a71b1981b 100644 --- a/log/handler_glog.go +++ b/log/handler_glog.go @@ -140,6 +140,10 @@ func (h *GlogHandler) Vmodule(ruleset string) error { h.siteCache = make(map[uintptr]Lvl) atomic.StoreUint32(&h.override, uint32(len(filter))) + // Enable location storage (globally) + if len(h.patterns) > 0 { + stackEnabled.Store(true) + } return nil } @@ -173,6 +177,8 @@ func (h *GlogHandler) BacktraceAt(location string) error { h.location = location atomic.StoreUint32(&h.backtrace, uint32(len(location))) + // Enable location storage (globally) + stackEnabled.Store(true) return nil } diff --git a/log/logger.go b/log/logger.go index 276d6969e..2e66d0841 100644 --- a/log/logger.go +++ b/log/logger.go @@ -131,19 +131,22 @@ type logger struct { } func (l *logger) write(msg string, lvl Lvl, ctx []interface{}, skip int) { - l.h.Log(&Record{ + record := &Record{ Time: time.Now(), Lvl: lvl, Msg: msg, Ctx: newContext(l.ctx, ctx), - Call: stack.Caller(skip), KeyNames: RecordKeyNames{ Time: timeKey, Msg: msgKey, Lvl: lvlKey, Ctx: ctxKey, }, - }) + } + if stackEnabled.Load() { + record.Call = stack.Caller(skip) + } + l.h.Log(record) } func (l *logger) New(ctx ...interface{}) Logger { diff --git a/log/logger_test.go b/log/logger_test.go new file mode 100644 index 000000000..2e59b3fdf --- /dev/null +++ b/log/logger_test.go @@ -0,0 +1,67 @@ +package log + +import ( + "bytes" + "os" + "strings" + "testing" +) + +// TestLoggingWithTrace checks that if BackTraceAt is set, then the +// gloghandler is capable of spitting out a stacktrace +func TestLoggingWithTrace(t *testing.T) { + defer stackEnabled.Store(stackEnabled.Load()) + out := new(bytes.Buffer) + logger := New() + { + glog := NewGlogHandler(StreamHandler(out, TerminalFormat(false))) + glog.Verbosity(LvlTrace) + if err := glog.BacktraceAt("logger_test.go:24"); err != nil { + t.Fatal(err) + } + logger.SetHandler(glog) + } + logger.Trace("a message", "foo", "bar") // Will be bumped to INFO + have := out.String() + if !strings.HasPrefix(have, "INFO") { + t.Fatalf("backtraceat should bump level to info: %s", have) + } + // The timestamp is locale-dependent, so we want to trim that off + // "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..." + have = strings.Split(have, "]")[1] + wantPrefix := " a message\n\ngoroutine" + if !strings.HasPrefix(have, wantPrefix) { + t.Errorf("\nhave: %q\nwant: %q\n", have, wantPrefix) + } +} + +// TestLoggingWithVmodule checks that vmodule works. +func TestLoggingWithVmodule(t *testing.T) { + defer stackEnabled.Store(stackEnabled.Load()) + out := new(bytes.Buffer) + logger := New() + { + glog := NewGlogHandler(StreamHandler(out, TerminalFormat(false))) + glog.Verbosity(LvlCrit) + logger.SetHandler(glog) + logger.Warn("This should not be seen", "ignored", "true") + glog.Vmodule("logger_test.go=5") + } + logger.Trace("a message", "foo", "bar") + have := out.String() + // The timestamp is locale-dependent, so we want to trim that off + // "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..." + have = strings.Split(have, "]")[1] + want := " a message foo=bar\n" + if have != want { + t.Errorf("\nhave: %q\nwant: %q\n", have, want) + } +} + +func BenchmarkTraceLogging(b *testing.B) { + Root().SetHandler(LvlFilterHandler(LvlInfo, StreamHandler(os.Stderr, TerminalFormat(true)))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + Trace("a message", "v", i) + } +} diff --git a/metrics/counter.go b/metrics/counter.go index 2f78c90d5..55e1c5954 100644 --- a/metrics/counter.go +++ b/metrics/counter.go @@ -38,13 +38,13 @@ func NewCounter() Counter { if !Enabled { return NilCounter{} } - return &StandardCounter{0} + return &StandardCounter{} } // NewCounterForced constructs a new StandardCounter and returns it no matter if // the global switch is enabled or not. func NewCounterForced() Counter { - return &StandardCounter{0} + return &StandardCounter{} } // NewRegisteredCounter constructs and registers a new StandardCounter. @@ -115,27 +115,27 @@ func (NilCounter) Snapshot() Counter { return NilCounter{} } // StandardCounter is the standard implementation of a Counter and uses the // sync/atomic package to manage a single int64 value. type StandardCounter struct { - count int64 + count atomic.Int64 } // Clear sets the counter to zero. func (c *StandardCounter) Clear() { - atomic.StoreInt64(&c.count, 0) + c.count.Store(0) } // Count returns the current count. func (c *StandardCounter) Count() int64 { - return atomic.LoadInt64(&c.count) + return c.count.Load() } // Dec decrements the counter by the given amount. func (c *StandardCounter) Dec(i int64) { - atomic.AddInt64(&c.count, -i) + c.count.Add(-i) } // Inc increments the counter by the given amount. func (c *StandardCounter) Inc(i int64) { - atomic.AddInt64(&c.count, i) + c.count.Add(i) } // Snapshot returns a read-only copy of the counter. diff --git a/metrics/ewma.go b/metrics/ewma.go index 039286493..ed95cba19 100644 --- a/metrics/ewma.go +++ b/metrics/ewma.go @@ -75,7 +75,7 @@ func (NilEWMA) Update(n int64) {} // of uncounted events and processes them on each tick. It uses the // sync/atomic package to manage uncounted events. type StandardEWMA struct { - uncounted int64 // /!\ this should be the first member to ensure 64-bit alignment + uncounted atomic.Int64 alpha float64 rate float64 init bool @@ -97,8 +97,8 @@ func (a *StandardEWMA) Snapshot() EWMA { // Tick ticks the clock to update the moving average. It assumes it is called // every five seconds. func (a *StandardEWMA) Tick() { - count := atomic.LoadInt64(&a.uncounted) - atomic.AddInt64(&a.uncounted, -count) + count := a.uncounted.Load() + a.uncounted.Add(-count) instantRate := float64(count) / float64(5*time.Second) a.mutex.Lock() defer a.mutex.Unlock() @@ -112,5 +112,5 @@ func (a *StandardEWMA) Tick() { // Update adds n uncounted events. func (a *StandardEWMA) Update(n int64) { - atomic.AddInt64(&a.uncounted, n) + a.uncounted.Add(n) } diff --git a/metrics/gauge.go b/metrics/gauge.go index b6b2758b0..81137d7f7 100644 --- a/metrics/gauge.go +++ b/metrics/gauge.go @@ -25,7 +25,7 @@ func NewGauge() Gauge { if !Enabled { return NilGauge{} } - return &StandardGauge{0} + return &StandardGauge{} } // NewRegisteredGauge constructs and registers a new StandardGauge. @@ -101,7 +101,7 @@ func (NilGauge) Value() int64 { return 0 } // StandardGauge is the standard implementation of a Gauge and uses the // sync/atomic package to manage a single int64 value. type StandardGauge struct { - value int64 + value atomic.Int64 } // Snapshot returns a read-only copy of the gauge. @@ -111,22 +111,22 @@ func (g *StandardGauge) Snapshot() Gauge { // Update updates the gauge's value. func (g *StandardGauge) Update(v int64) { - atomic.StoreInt64(&g.value, v) + g.value.Store(v) } // Value returns the gauge's current value. func (g *StandardGauge) Value() int64 { - return atomic.LoadInt64(&g.value) + return g.value.Load() } // Dec decrements the gauge's current value by the given amount. func (g *StandardGauge) Dec(i int64) { - atomic.AddInt64(&g.value, -i) + g.value.Add(-i) } // Inc increments the gauge's current value by the given amount. func (g *StandardGauge) Inc(i int64) { - atomic.AddInt64(&g.value, i) + g.value.Add(i) } // FunctionalGauge returns value from given function diff --git a/metrics/meter.go b/metrics/meter.go index 60ae919d0..e8564d6a5 100644 --- a/metrics/meter.go +++ b/metrics/meter.go @@ -101,11 +101,7 @@ func NewRegisteredMeterForced(name string, r Registry) Meter { // MeterSnapshot is a read-only copy of another Meter. type MeterSnapshot struct { - // WARNING: The `temp` field is accessed atomically. - // On 32 bit platforms, only 64-bit aligned fields can be atomic. The struct is - // guaranteed to be so aligned, so take advantage of that. For more information, - // see https://golang.org/pkg/sync/atomic/#pkg-note-BUG. - temp int64 + temp atomic.Int64 count int64 rate1, rate5, rate15, rateMean float64 } @@ -173,7 +169,7 @@ type StandardMeter struct { snapshot *MeterSnapshot a1, a5, a15 EWMA startTime time.Time - stopped uint32 + stopped atomic.Bool } func newStandardMeter() *StandardMeter { @@ -188,8 +184,8 @@ func newStandardMeter() *StandardMeter { // Stop stops the meter, Mark() will be a no-op if you use it after being stopped. func (m *StandardMeter) Stop() { - stopped := atomic.SwapUint32(&m.stopped, 1) - if stopped != 1 { + stopped := m.stopped.Swap(true) + if !stopped { arbiter.Lock() delete(arbiter.meters, m) arbiter.Unlock() @@ -207,7 +203,7 @@ func (m *StandardMeter) Count() int64 { // Mark records the occurrence of n events. func (m *StandardMeter) Mark(n int64) { - atomic.AddInt64(&m.snapshot.temp, n) + m.snapshot.temp.Add(n) } // Rate1 returns the one-minute moving average rate of events per second. @@ -241,7 +237,14 @@ func (m *StandardMeter) RateMean() float64 { // Snapshot returns a read-only copy of the meter. func (m *StandardMeter) Snapshot() Meter { m.lock.RLock() - snapshot := *m.snapshot + snapshot := MeterSnapshot{ + count: m.snapshot.count, + rate1: m.snapshot.rate1, + rate5: m.snapshot.rate5, + rate15: m.snapshot.rate15, + rateMean: m.snapshot.rateMean, + } + snapshot.temp.Store(m.snapshot.temp.Load()) m.lock.RUnlock() return &snapshot } @@ -257,7 +260,7 @@ func (m *StandardMeter) updateSnapshot() { func (m *StandardMeter) updateMeter() { // should only run with write lock held on m.lock - n := atomic.SwapInt64(&m.snapshot.temp, 0) + n := m.snapshot.temp.Swap(0) m.snapshot.count += n m.a1.Update(n) m.a5.Update(n)