forked from mirror/go-ethereum
Merge pull request #19810 from karalabe/txpool-noncer
core: kill off managed state, use own tiny noncer for txpoolChrisChinchilla-patch-3
commit
8c249cb82f
@ -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)) |
||||
} |
||||
} |
@ -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 |
||||
} |
Loading…
Reference in new issue