diff --git a/state/state_object.go b/state/state_object.go index 12444304..1ce9b1f4 100644 --- a/state/state_object.go +++ b/state/state_object.go @@ -52,21 +52,9 @@ type ( suicided bool deleted bool } - - // // Account is the Ethereum consensus representation of accounts. - // // These objects are stored in the main account trie. - // Account struct { - // Nonce uint64 - // Balance *big.Int - // Root ethcmn.Hash // merkle root of the storage trie - // CodeHash []byte - // } ) func newObject(db *CommitStateDB, accProto auth.Account) *stateObject { - // if acc.Balance == nil { - // data.Balance = new(big.Int) - // } acc, ok := accProto.(*types.Account) if !ok { panic(fmt.Sprintf("invalid account type for state object: %T", acc)) @@ -85,13 +73,9 @@ func newObject(db *CommitStateDB, accProto auth.Account) *stateObject { } } -// Address returns the address of the state object. -func (so stateObject) Address() ethcmn.Address { - return so.address -} - -// GetState retrieves a value from the account storage trie. -func (so *stateObject) GetState(_ Database, key ethcmn.Hash) ethcmn.Hash { +// GetState retrieves a value from the account storage trie. Note, the key must +// be prefixed with the address. +func (so *stateObject) GetState(_ ethstate.Database, key ethcmn.Hash) ethcmn.Hash { // if we have a dirty value for this state entry, return it value, dirty := so.dirtyStorage[key] if dirty { @@ -99,11 +83,33 @@ func (so *stateObject) GetState(_ Database, key ethcmn.Hash) ethcmn.Hash { } // otherwise return the entry's original value - return so.getCommittedState(key) + return so.getCommittedState(so.stateDB.ctx, key) } -// SetState updates a value in account storage. -func (so *stateObject) SetState(db Database, key, value ethcmn.Hash) { +// GetCommittedState retrieves a value from the committed account storage trie. +// Note, the must be prefixed with the address. +func (so *stateObject) getCommittedState(ctx sdk.Context, key ethcmn.Hash) ethcmn.Hash { + // if we have the original value cached, return that + value, cached := so.originStorage[key] + if cached { + return value + } + + // otherwise load the value from the KVStore + store := ctx.KVStore(so.stateDB.storageKey) + rawValue := store.Get(key.Bytes()) + + if len(rawValue) > 0 { + value.SetBytes(rawValue) + } + + so.originStorage[key] = value + return value +} + +// SetState updates a value in account storage. Note, the key must be prefixed +// with the address. +func (so *stateObject) SetState(db ethstate.Database, key, value ethcmn.Hash) { // if the new value is the same as old, don't set prev := so.GetState(db, key) if prev == value { @@ -120,6 +126,50 @@ func (so *stateObject) SetState(db Database, key, value ethcmn.Hash) { so.setState(key, value) } +func (so *stateObject) setState(key, value ethcmn.Hash) { + so.dirtyStorage[key] = value +} + +// Code returns the contract code associated with this object, if any. +func (so *stateObject) Code(_ ethstate.Database) []byte { + if so.code != nil { + return so.code + } + + if bytes.Equal(so.CodeHash(), emptyCodeHash) { + return nil + } + + store := so.stateDB.ctx.KVStore(so.stateDB.codeKey) + code := store.Get(so.CodeHash()) + + if len(code) == 0 { + so.setError(fmt.Errorf("failed to get code hash %x for address: %x", so.CodeHash(), so.Address())) + } + + so.code = code + return code +} + +// SetCode sets the state object's code. +func (so *stateObject) SetCode(codeHash ethcmn.Hash, code []byte) { + prevcode := so.Code(nil) + + so.stateDB.journal.append(codeChange{ + account: &so.address, + prevhash: so.CodeHash(), + prevcode: prevcode, + }) + + so.setCode(codeHash, code) +} + +func (so *stateObject) setCode(codeHash ethcmn.Hash, code []byte) { + so.code = code + so.account.CodeHash = codeHash.Bytes() + so.dirtyCode = true +} + // AddBalance adds an amount to a state object's balance. It is used to add // funds to the destination account of a transfer. func (so *stateObject) AddBalance(amount *big.Int) { @@ -139,6 +189,20 @@ func (so *stateObject) AddBalance(amount *big.Int) { so.SetBalance(newBalance.BigInt()) } +// SubBalance removes an amount from the stateObject's balance. It is used to +// remove funds from the origin account of a transfer. +func (so *stateObject) SubBalance(amount *big.Int) { + amt := sdk.NewIntFromBigInt(amount) + + if amt.Sign() == 0 { + return + } + + newBalance := so.account.Balance().Sub(amt) + so.SetBalance(newBalance.BigInt()) +} + +// SetBalance sets the state object's balance. func (so *stateObject) SetBalance(amount *big.Int) { amt := sdk.NewIntFromBigInt(amount) @@ -150,74 +214,46 @@ func (so *stateObject) SetBalance(amount *big.Int) { so.setBalance(amt) } -// SubBalance removes amount from c's balance. -// It is used to remove funds from the origin account of a transfer. -func (so *stateObject) SubBalance(amount *big.Int) { - if amount.Sign() == 0 { - return - } - - c.SetBalance(new(big.Int).Sub(c.Balance(), amount)) -} - -// func (so *stateObject) Balance() *big.Int { - -// } - -// func (so *stateObject) ReturnGas(gas *big.Int) { - -// } - -// func (so *stateObject) Address() ethcmn.Address { - -// } - -// func (so *stateObject) SetCode(codeHash ethcmn.Hash, code []byte) { - -// } - -// func (so *stateObject) SetNonce(nonce uint64) { - -// } - -// func (so *stateObject) Nonce() uint64 { - -// } - -// func (so *stateObject) Code(db Database) []byte { - -// } - -// func (so *stateObject) CodeHash() []byte { - -// } - func (so *stateObject) setBalance(amount sdk.Int) { so.account.SetBalance(amount) } -// GetCommittedState retrieves a value from the committed account storage trie. -func (so *stateObject) getCommittedState(key ethcmn.Hash) ethcmn.Hash { - // if we have the original value cached, return that - value, cached := so.originStorage[key] - if cached { - return value - } - - // otherwise load the value from the KVStore - store := so.stateDB.ctx.KVStore(so.stateDB.storageKey) - rawValue := store.Get(key.Bytes()) - - if len(rawValue) > 0 { - value.SetBytes(rawValue) - } - - so.originStorage[key] = value - return value +// Balance returns the state object's current balance. +func (so *stateObject) Balance() *big.Int { + return so.account.Balance().BigInt() } -func (so *stateObject) setState(key, value ethcmn.Hash) { - so.dirtyStorage[key] = value +// ReturnGas returns the gas back to the origin. Used by the Virtual machine or +// Closures. It performs a no-op. +func (so *stateObject) ReturnGas(gas *big.Int) {} + +// Address returns the address of the state object. +func (so stateObject) Address() ethcmn.Address { + return so.address +} + +// CodeHash returns the state object's code hash. +func (so *stateObject) CodeHash() []byte { + return so.account.CodeHash +} + +// Nonce returns the state object's current nonce (sequence number). +func (so *stateObject) Nonce() uint64 { + return uint64(so.account.Sequence) +} + +// SetNonce sets the state object's nonce (sequence number). +func (so *stateObject) SetNonce(nonce uint64) { + so.stateDB.journal.append(nonceChange{ + account: &so.address, + prev: so.account.Sequence, + }) + + so.setNonce(int64(nonce)) +} + +func (so *stateObject) setNonce(nonce int64) { + so.account.Sequence = nonce } // setError remembers the first non-nil error it is called with. @@ -245,3 +281,14 @@ func (so *stateObject) touch() { so.stateDB.journal.dirty(so.address) } } + +// prefixStorageKey prefixes a storage key with the state object's address. +func (so stateObject) prefixStorageKey(key []byte) []byte { + prefix := so.Address().Bytes() + compositeKey := make([]byte, len(prefix)+len(key)) + + copy(compositeKey, prefix) + copy(compositeKey[len(prefix):], key) + + return compositeKey +}