diff options
Diffstat (limited to 'state')
-rw-r--r-- | state/managed_state.go | 84 | ||||
-rw-r--r-- | state/managed_state_test.go | 89 | ||||
-rw-r--r-- | state/state_object.go | 33 | ||||
-rw-r--r-- | state/statedb.go | 18 |
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) |