core: kill off managed state, use own tiny noncer for txpool
This commit is contained in:
parent
5873c01c3d
commit
a966425a1d
@ -1,143 +0,0 @@
|
||||
// Copyright 2015 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 state
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
type account struct {
|
||||
stateObject *stateObject
|
||||
nstart uint64
|
||||
nonces []bool
|
||||
}
|
||||
|
||||
type ManagedState struct {
|
||||
*StateDB
|
||||
|
||||
mu sync.RWMutex
|
||||
|
||||
accounts map[common.Address]*account
|
||||
}
|
||||
|
||||
// ManagedState returns a new managed state with the statedb as it's backing layer
|
||||
func ManageState(statedb *StateDB) *ManagedState {
|
||||
return &ManagedState{
|
||||
StateDB: statedb.Copy(),
|
||||
accounts: make(map[common.Address]*account),
|
||||
}
|
||||
}
|
||||
|
||||
// SetState sets the backing layer of the managed state
|
||||
func (ms *ManagedState) SetState(statedb *StateDB) {
|
||||
ms.mu.Lock()
|
||||
defer ms.mu.Unlock()
|
||||
ms.StateDB = statedb
|
||||
}
|
||||
|
||||
// RemoveNonce removed the nonce from the managed state and all future pending nonces
|
||||
func (ms *ManagedState) RemoveNonce(addr common.Address, n uint64) {
|
||||
if ms.hasAccount(addr) {
|
||||
ms.mu.Lock()
|
||||
defer ms.mu.Unlock()
|
||||
|
||||
account := ms.getAccount(addr)
|
||||
if n-account.nstart <= uint64(len(account.nonces)) {
|
||||
reslice := make([]bool, n-account.nstart)
|
||||
copy(reslice, account.nonces[:n-account.nstart])
|
||||
account.nonces = reslice
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewNonce returns the new canonical nonce for the managed account
|
||||
func (ms *ManagedState) NewNonce(addr common.Address) uint64 {
|
||||
ms.mu.Lock()
|
||||
defer ms.mu.Unlock()
|
||||
|
||||
account := ms.getAccount(addr)
|
||||
for i, nonce := range account.nonces {
|
||||
if !nonce {
|
||||
return account.nstart + uint64(i)
|
||||
}
|
||||
}
|
||||
account.nonces = append(account.nonces, true)
|
||||
|
||||
return uint64(len(account.nonces)-1) + account.nstart
|
||||
}
|
||||
|
||||
// GetNonce returns the canonical nonce for the managed or unmanaged account.
|
||||
//
|
||||
// Because GetNonce mutates the DB, we must take a write lock.
|
||||
func (ms *ManagedState) GetNonce(addr common.Address) uint64 {
|
||||
ms.mu.Lock()
|
||||
defer ms.mu.Unlock()
|
||||
|
||||
if ms.hasAccount(addr) {
|
||||
account := ms.getAccount(addr)
|
||||
return uint64(len(account.nonces)) + account.nstart
|
||||
} else {
|
||||
return ms.StateDB.GetNonce(addr)
|
||||
}
|
||||
}
|
||||
|
||||
// SetNonce sets the new canonical nonce for the managed state
|
||||
func (ms *ManagedState) SetNonce(addr common.Address, nonce uint64) {
|
||||
ms.mu.Lock()
|
||||
defer ms.mu.Unlock()
|
||||
|
||||
so := ms.GetOrNewStateObject(addr)
|
||||
so.SetNonce(nonce)
|
||||
|
||||
ms.accounts[addr] = newAccount(so)
|
||||
}
|
||||
|
||||
// HasAccount returns whether the given address is managed or not
|
||||
func (ms *ManagedState) HasAccount(addr common.Address) bool {
|
||||
ms.mu.RLock()
|
||||
defer ms.mu.RUnlock()
|
||||
return ms.hasAccount(addr)
|
||||
}
|
||||
|
||||
func (ms *ManagedState) hasAccount(addr common.Address) bool {
|
||||
_, ok := ms.accounts[addr]
|
||||
return ok
|
||||
}
|
||||
|
||||
// populate the managed state
|
||||
func (ms *ManagedState) getAccount(addr common.Address) *account {
|
||||
if account, ok := ms.accounts[addr]; !ok {
|
||||
so := ms.GetOrNewStateObject(addr)
|
||||
ms.accounts[addr] = newAccount(so)
|
||||
} else {
|
||||
// Always make sure the state account nonce isn't actually higher
|
||||
// than the tracked one.
|
||||
so := ms.StateDB.getStateObject(addr)
|
||||
if so != nil && uint64(len(account.nonces))+account.nstart < so.Nonce() {
|
||||
ms.accounts[addr] = newAccount(so)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ms.accounts[addr]
|
||||
}
|
||||
|
||||
func newAccount(so *stateObject) *account {
|
||||
return &account{so, so.Nonce(), nil}
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
// Copyright 2015 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 state
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
)
|
||||
|
||||
var addr = common.BytesToAddress([]byte("test"))
|
||||
|
||||
func create() (*ManagedState, *account) {
|
||||
statedb, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
ms := ManageState(statedb)
|
||||
ms.StateDB.SetNonce(addr, 100)
|
||||
ms.accounts[addr] = newAccount(ms.StateDB.getStateObject(addr))
|
||||
return ms, ms.accounts[addr]
|
||||
}
|
||||
|
||||
func TestNewNonce(t *testing.T) {
|
||||
ms, _ := create()
|
||||
|
||||
nonce := ms.NewNonce(addr)
|
||||
if nonce != 100 {
|
||||
t.Error("expected nonce 100. got", nonce)
|
||||
}
|
||||
|
||||
nonce = ms.NewNonce(addr)
|
||||
if nonce != 101 {
|
||||
t.Error("expected nonce 101. got", nonce)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
ms, account := create()
|
||||
|
||||
nn := make([]bool, 10)
|
||||
for i := range nn {
|
||||
nn[i] = true
|
||||
}
|
||||
account.nonces = append(account.nonces, nn...)
|
||||
|
||||
i := uint64(5)
|
||||
ms.RemoveNonce(addr, account.nstart+i)
|
||||
if len(account.nonces) != 5 {
|
||||
t.Error("expected", i, "'th index to be false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReuse(t *testing.T) {
|
||||
ms, account := create()
|
||||
|
||||
nn := make([]bool, 10)
|
||||
for i := range nn {
|
||||
nn[i] = true
|
||||
}
|
||||
account.nonces = append(account.nonces, nn...)
|
||||
|
||||
i := uint64(5)
|
||||
ms.RemoveNonce(addr, account.nstart+i)
|
||||
nonce := ms.NewNonce(addr)
|
||||
if nonce != 105 {
|
||||
t.Error("expected nonce to be 105. got", nonce)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoteNonceChange(t *testing.T) {
|
||||
ms, account := create()
|
||||
nn := make([]bool, 10)
|
||||
for i := range nn {
|
||||
nn[i] = true
|
||||
}
|
||||
account.nonces = append(account.nonces, nn...)
|
||||
ms.NewNonce(addr)
|
||||
|
||||
ms.StateDB.stateObjects[addr].data.Nonce = 200
|
||||
nonce := ms.NewNonce(addr)
|
||||
if nonce != 200 {
|
||||
t.Error("expected nonce after remote update to be", 200, "got", nonce)
|
||||
}
|
||||
ms.NewNonce(addr)
|
||||
ms.NewNonce(addr)
|
||||
ms.NewNonce(addr)
|
||||
ms.StateDB.stateObjects[addr].data.Nonce = 200
|
||||
nonce = ms.NewNonce(addr)
|
||||
if nonce != 204 {
|
||||
t.Error("expected nonce after remote update to be", 204, "got", nonce)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetNonce(t *testing.T) {
|
||||
ms, _ := create()
|
||||
|
||||
var addr common.Address
|
||||
ms.SetNonce(addr, 10)
|
||||
|
||||
if ms.GetNonce(addr) != 10 {
|
||||
t.Error("Expected nonce of 10, got", ms.GetNonce(addr))
|
||||
}
|
||||
|
||||
addr[0] = 1
|
||||
ms.StateDB.SetNonce(addr, 1)
|
||||
|
||||
if ms.GetNonce(addr) != 1 {
|
||||
t.Error("Expected nonce of 1, got", ms.GetNonce(addr))
|
||||
}
|
||||
}
|
53
core/tx_noncer.go
Normal file
53
core/tx_noncer.go
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2019 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 core
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
)
|
||||
|
||||
// txNoncer is a tiny virtual state database to manage the executable nonces of
|
||||
// accounts in the pool, falling back to reading from a real state database if
|
||||
// an account is unknown.
|
||||
type txNoncer struct {
|
||||
fallback *state.StateDB
|
||||
nonces map[common.Address]uint64
|
||||
}
|
||||
|
||||
// newTxNoncer creates a new virtual state database to track the pool nonces.
|
||||
func newTxNoncer(statedb *state.StateDB) *txNoncer {
|
||||
return &txNoncer{
|
||||
fallback: statedb.Copy(),
|
||||
nonces: make(map[common.Address]uint64),
|
||||
}
|
||||
}
|
||||
|
||||
// get returns the current nonce of an account, falling back to a real state
|
||||
// database if the account is unknown.
|
||||
func (txn *txNoncer) get(addr common.Address) uint64 {
|
||||
if _, ok := txn.nonces[addr]; !ok {
|
||||
txn.nonces[addr] = txn.fallback.GetNonce(addr)
|
||||
}
|
||||
return txn.nonces[addr]
|
||||
}
|
||||
|
||||
// set inserts a new virtual nonce into the virtual state database to be returned
|
||||
// whenever the pool requests it instead of reaching into the real state database.
|
||||
func (txn *txNoncer) set(addr common.Address, nonce uint64) {
|
||||
txn.nonces[addr] = nonce
|
||||
}
|
@ -218,7 +218,7 @@ type TxPool struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
currentState *state.StateDB // Current state in the blockchain head
|
||||
pendingState *state.ManagedState // Pending state tracking virtual nonces
|
||||
pendingNonces *txNoncer // Pending state tracking virtual nonces
|
||||
currentMaxGas uint64 // Current gas limit for transaction caps
|
||||
|
||||
locals *accountSet // Set of local transaction to exempt from eviction rules
|
||||
@ -417,12 +417,13 @@ func (pool *TxPool) SetGasPrice(price *big.Int) {
|
||||
log.Info("Transaction pool price threshold updated", "price", price)
|
||||
}
|
||||
|
||||
// State returns the virtual managed state of the transaction pool.
|
||||
func (pool *TxPool) State() *state.ManagedState {
|
||||
// Nonce returns the next nonce of an account, with all transactions executable
|
||||
// by the pool already applied on top.
|
||||
func (pool *TxPool) Nonce(addr common.Address) uint64 {
|
||||
pool.mu.RLock()
|
||||
defer pool.mu.RUnlock()
|
||||
|
||||
return pool.pendingState
|
||||
return pool.pendingNonces.get(addr)
|
||||
}
|
||||
|
||||
// Stats retrieves the current pool stats, namely the number of pending and the
|
||||
@ -713,7 +714,7 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
|
||||
}
|
||||
// Set the potentially new pending nonce and notify any subsystems of the new tx
|
||||
pool.beats[addr] = time.Now()
|
||||
pool.pendingState.SetNonce(addr, tx.Nonce()+1)
|
||||
pool.pendingNonces.set(addr, tx.Nonce()+1)
|
||||
|
||||
return true
|
||||
}
|
||||
@ -853,8 +854,8 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) {
|
||||
pool.enqueueTx(tx.Hash(), tx)
|
||||
}
|
||||
// Update the account nonce if needed
|
||||
if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce {
|
||||
pool.pendingState.SetNonce(addr, nonce)
|
||||
if nonce := tx.Nonce(); pool.pendingNonces.get(addr) > nonce {
|
||||
pool.pendingNonces.set(addr, nonce)
|
||||
}
|
||||
// Reduce the pending counter
|
||||
pendingCounter.Dec(int64(1 + len(invalids)))
|
||||
@ -990,7 +991,7 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
|
||||
|
||||
// Nonces were reset, discard any events that became stale
|
||||
for addr := range events {
|
||||
events[addr].Forward(pool.pendingState.GetNonce(addr))
|
||||
events[addr].Forward(pool.pendingNonces.get(addr))
|
||||
if events[addr].Len() == 0 {
|
||||
delete(events, addr)
|
||||
}
|
||||
@ -1023,7 +1024,7 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
|
||||
// Update all accounts to the latest known pending nonce
|
||||
for addr, list := range pool.pending {
|
||||
txs := list.Flatten() // Heavy but will be cached and is needed by the miner anyway
|
||||
pool.pendingState.SetNonce(addr, txs[len(txs)-1].Nonce()+1)
|
||||
pool.pendingNonces.set(addr, txs[len(txs)-1].Nonce()+1)
|
||||
}
|
||||
pool.mu.Unlock()
|
||||
|
||||
@ -1112,7 +1113,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
|
||||
return
|
||||
}
|
||||
pool.currentState = statedb
|
||||
pool.pendingState = state.ManageState(statedb)
|
||||
pool.pendingNonces = newTxNoncer(statedb)
|
||||
pool.currentMaxGas = newHead.GasLimit
|
||||
|
||||
// Inject any transactions discarded due to reorgs
|
||||
@ -1151,7 +1152,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
|
||||
queuedNofundsMeter.Mark(int64(len(drops)))
|
||||
|
||||
// Gather all executable transactions and promote them
|
||||
readies := list.Ready(pool.pendingState.GetNonce(addr))
|
||||
readies := list.Ready(pool.pendingNonces.get(addr))
|
||||
for _, tx := range readies {
|
||||
hash := tx.Hash()
|
||||
if pool.promoteTx(addr, hash, tx) {
|
||||
@ -1231,8 +1232,8 @@ func (pool *TxPool) truncatePending() {
|
||||
pool.all.Remove(hash)
|
||||
|
||||
// Update the account nonce to the dropped transaction
|
||||
if nonce := tx.Nonce(); pool.pendingState.GetNonce(offenders[i]) > nonce {
|
||||
pool.pendingState.SetNonce(offenders[i], nonce)
|
||||
if nonce := tx.Nonce(); pool.pendingNonces.get(offenders[i]) > nonce {
|
||||
pool.pendingNonces.set(offenders[i], nonce)
|
||||
}
|
||||
log.Trace("Removed fairness-exceeding pending transaction", "hash", hash)
|
||||
}
|
||||
@ -1260,8 +1261,8 @@ func (pool *TxPool) truncatePending() {
|
||||
pool.all.Remove(hash)
|
||||
|
||||
// Update the account nonce to the dropped transaction
|
||||
if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce {
|
||||
pool.pendingState.SetNonce(addr, nonce)
|
||||
if nonce := tx.Nonce(); pool.pendingNonces.get(addr) > nonce {
|
||||
pool.pendingNonces.set(addr, nonce)
|
||||
}
|
||||
log.Trace("Removed fairness-exceeding pending transaction", "hash", hash)
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ func validateTxPoolInternals(pool *TxPool) error {
|
||||
last = nonce
|
||||
}
|
||||
}
|
||||
if nonce := pool.pendingState.GetNonce(addr); nonce != last+1 {
|
||||
if nonce := pool.Nonce(addr); nonce != last+1 {
|
||||
return fmt.Errorf("pending nonce mismatch: have %v, want %v", nonce, last+1)
|
||||
}
|
||||
}
|
||||
@ -195,14 +195,14 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) {
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
nonce := pool.State().GetNonce(address)
|
||||
nonce := pool.Nonce(address)
|
||||
if nonce != 0 {
|
||||
t.Fatalf("Invalid nonce, want 0, got %d", nonce)
|
||||
}
|
||||
|
||||
pool.addRemotesSync([]*types.Transaction{tx0, tx1})
|
||||
|
||||
nonce = pool.State().GetNonce(address)
|
||||
nonce = pool.Nonce(address)
|
||||
if nonce != 2 {
|
||||
t.Fatalf("Invalid nonce, want 2, got %d", nonce)
|
||||
}
|
||||
@ -215,7 +215,7 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Could not fetch pending transactions: %v", err)
|
||||
}
|
||||
nonce = pool.State().GetNonce(address)
|
||||
nonce = pool.Nonce(address)
|
||||
if nonce != 2 {
|
||||
t.Fatalf("Invalid nonce, want 2, got %d", nonce)
|
||||
}
|
||||
@ -451,7 +451,7 @@ func TestTransactionNonceRecovery(t *testing.T) {
|
||||
// simulate some weird re-order of transactions and missing nonce(s)
|
||||
pool.currentState.SetNonce(addr, n-1)
|
||||
<-pool.requestReset(nil, nil)
|
||||
if fn := pool.pendingState.GetNonce(addr); fn != n-1 {
|
||||
if fn := pool.Nonce(addr); fn != n-1 {
|
||||
t.Errorf("expected nonce to be %d, got %d", n-1, fn)
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +185,7 @@ func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash)
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
||||
return b.eth.txPool.State().GetNonce(addr), nil
|
||||
return b.eth.txPool.Nonce(addr), nil
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) Stats() (pending int, queued int) {
|
||||
|
Loading…
Reference in New Issue
Block a user