aboutsummaryrefslogtreecommitdiffstats
path: root/state
diff options
context:
space:
mode:
Diffstat (limited to 'state')
-rw-r--r--state/managed_state.go84
-rw-r--r--state/managed_state_test.go89
-rw-r--r--state/state_object.go33
-rw-r--r--state/statedb.go18
4 files changed, 200 insertions, 24 deletions
diff --git a/state/managed_state.go b/state/managed_state.go
new file mode 100644
index 000000000..aff0206b2
--- /dev/null
+++ b/state/managed_state.go
@@ -0,0 +1,84 @@
+package state
+
+import "sync"
+
+type account struct {
+ stateObject *StateObject
+ nstart uint64
+ nonces []bool
+}
+
+type ManagedState struct {
+ *StateDB
+
+ mu sync.RWMutex
+
+ accounts map[string]*account
+}
+
+func ManageState(statedb *StateDB) *ManagedState {
+ return &ManagedState{
+ StateDB: statedb,
+ accounts: make(map[string]*account),
+ }
+}
+
+func (ms *ManagedState) SetState(statedb *StateDB) {
+ ms.mu.Lock()
+ defer ms.mu.Unlock()
+ ms.StateDB = statedb
+}
+
+func (ms *ManagedState) RemoveNonce(addr []byte, 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
+ }
+ }
+}
+
+func (ms *ManagedState) NewNonce(addr []byte) uint64 {
+ ms.mu.RLock()
+ defer ms.mu.RUnlock()
+
+ 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)) + account.nstart
+}
+
+func (ms *ManagedState) hasAccount(addr []byte) bool {
+ _, ok := ms.accounts[string(addr)]
+ return ok
+}
+
+func (ms *ManagedState) getAccount(addr []byte) *account {
+ if account, ok := ms.accounts[string(addr)]; !ok {
+ so := ms.GetOrNewStateObject(addr)
+ ms.accounts[string(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[string(addr)] = newAccount(so)
+ }
+
+ }
+
+ return ms.accounts[string(addr)]
+}
+
+func newAccount(so *StateObject) *account {
+ return &account{so, so.nonce - 1, nil}
+}
diff --git a/state/managed_state_test.go b/state/managed_state_test.go
new file mode 100644
index 000000000..f819d8ad3
--- /dev/null
+++ b/state/managed_state_test.go
@@ -0,0 +1,89 @@
+package state
+
+import (
+ "testing"
+
+ "github.com/ethereum/go-ethereum/ethutil"
+)
+
+var addr = ethutil.Address([]byte("test"))
+
+func create() (*ManagedState, *account) {
+ ms := ManageState(&StateDB{stateObjects: make(map[string]*StateObject)})
+ so := &StateObject{address: addr, nonce: 100}
+ ms.StateDB.stateObjects[string(addr)] = so
+ ms.accounts[string(addr)] = newAccount(so)
+
+ return ms, ms.accounts[string(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...)
+ nonce := ms.NewNonce(addr)
+
+ ms.StateDB.stateObjects[string(addr)].nonce = 200
+ nonce = ms.NewNonce(addr)
+ if nonce != 200 {
+ t.Error("expected nonce after remote update to be", 201, "got", nonce)
+ }
+ ms.NewNonce(addr)
+ ms.NewNonce(addr)
+ ms.NewNonce(addr)
+ ms.StateDB.stateObjects[string(addr)].nonce = 200
+ nonce = ms.NewNonce(addr)
+ if nonce != 204 {
+ t.Error("expected nonce after remote update to be", 201, "got", nonce)
+ }
+}
diff --git a/state/state_object.go b/state/state_object.go
index ccbfea391..dccbe8dad 100644
--- a/state/state_object.go
+++ b/state/state_object.go
@@ -38,19 +38,27 @@ func (self Storage) Copy() Storage {
}
type StateObject struct {
+ // State database for storing state changes
db ethutil.Database
- // Address of the object
+ // The state object
+ State *StateDB
+
+ // Address belonging to this account
address []byte
- // Shared attributes
- balance *big.Int
+ // The balance of the account
+ balance *big.Int
+ // The nonce of the account
+ nonce uint64
+ // The code hash if code is present (i.e. a contract)
codeHash []byte
- nonce uint64
- // Contract related attributes
- State *StateDB
- code Code
+ // The code for this account
+ code Code
+ // Temporarily initialisation code
initCode Code
-
+ // Cached storage (flushed when updated)
storage Storage
+ // Temporary prepaid gas, reward after transition
+ prepaid *big.Int
// Total gas pool is the total amount of gas currently
// left if this object is the coinbase. Gas is directly
@@ -77,6 +85,7 @@ func NewStateObject(addr []byte, db ethutil.Database) *StateObject {
object.State = New(nil, db) //New(trie.New(ethutil.Config.Db, ""))
object.storage = make(Storage)
object.gasPool = new(big.Int)
+ object.prepaid = new(big.Int)
return object
}
@@ -103,6 +112,7 @@ func NewStateObjectFromBytes(address, data []byte, db ethutil.Database) *StateOb
object.State = New(extobject.Root, db)
object.storage = make(map[string]*ethutil.Value)
object.gasPool = new(big.Int)
+ object.prepaid = new(big.Int)
object.code, _ = db.Get(extobject.CodeHash)
return object
@@ -230,8 +240,6 @@ func (self *StateObject) BuyGas(gas, price *big.Int) error {
rGas := new(big.Int).Set(gas)
rGas.Mul(rGas, price)
- self.AddBalance(rGas)
-
self.dirty = true
return nil
@@ -239,11 +247,6 @@ func (self *StateObject) BuyGas(gas, price *big.Int) error {
func (self *StateObject) RefundGas(gas, price *big.Int) {
self.gasPool.Add(self.gasPool, gas)
-
- rGas := new(big.Int).Set(gas)
- rGas.Mul(rGas, price)
-
- self.balance.Sub(self.balance, rGas)
}
func (self *StateObject) Copy() *StateObject {
diff --git a/state/statedb.go b/state/statedb.go
index 0a4156461..a0dc7732f 100644
--- a/state/statedb.go
+++ b/state/statedb.go
@@ -54,7 +54,7 @@ func (self *StateDB) Refund(addr []byte, gas *big.Int) {
// Retrieve the balance from the given address or 0 if object not found
func (self *StateDB) GetBalance(addr []byte) *big.Int {
- stateObject := self.GetOrNewStateObject(addr)
+ stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.balance
}
@@ -63,14 +63,14 @@ func (self *StateDB) GetBalance(addr []byte) *big.Int {
}
func (self *StateDB) AddBalance(addr []byte, amount *big.Int) {
- stateObject := self.GetOrNewStateObject(addr)
+ stateObject := self.GetStateObject(addr)
if stateObject != nil {
stateObject.AddBalance(amount)
}
}
func (self *StateDB) GetNonce(addr []byte) uint64 {
- stateObject := self.GetOrNewStateObject(addr)
+ stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.nonce
}
@@ -79,7 +79,7 @@ func (self *StateDB) GetNonce(addr []byte) uint64 {
}
func (self *StateDB) GetCode(addr []byte) []byte {
- stateObject := self.GetOrNewStateObject(addr)
+ stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.code
}
@@ -88,7 +88,7 @@ func (self *StateDB) GetCode(addr []byte) []byte {
}
func (self *StateDB) GetState(a, b []byte) []byte {
- stateObject := self.GetOrNewStateObject(a)
+ stateObject := self.GetStateObject(a)
if stateObject != nil {
return stateObject.GetState(b).Bytes()
}
@@ -97,28 +97,28 @@ func (self *StateDB) GetState(a, b []byte) []byte {
}
func (self *StateDB) SetNonce(addr []byte, nonce uint64) {
- stateObject := self.GetOrNewStateObject(addr)
+ stateObject := self.GetStateObject(addr)
if stateObject != nil {
stateObject.SetNonce(nonce)
}
}
func (self *StateDB) SetCode(addr, code []byte) {
- stateObject := self.GetOrNewStateObject(addr)
+ stateObject := self.GetStateObject(addr)
if stateObject != nil {
stateObject.SetCode(code)
}
}
func (self *StateDB) SetState(addr, key []byte, value interface{}) {
- stateObject := self.GetOrNewStateObject(addr)
+ stateObject := self.GetStateObject(addr)
if stateObject != nil {
stateObject.SetState(key, ethutil.NewValue(value))
}
}
func (self *StateDB) Delete(addr []byte) bool {
- stateObject := self.GetOrNewStateObject(addr)
+ stateObject := self.GetStateObject(addr)
if stateObject != nil {
stateObject.MarkForDeletion()
stateObject.balance = new(big.Int)