forked from cerc-io/laconicd-deprecated
239 lines
5.5 KiB
Go
239 lines
5.5 KiB
Go
package statedb
|
|
|
|
import (
|
|
"bytes"
|
|
"math/big"
|
|
"sort"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
)
|
|
|
|
var emptyCodeHash = crypto.Keccak256(nil)
|
|
|
|
// Account is the Ethereum consensus representation of accounts.
|
|
// These objects are stored in the storage of auth module.
|
|
type Account struct {
|
|
Nonce uint64
|
|
Balance *big.Int
|
|
CodeHash []byte
|
|
}
|
|
|
|
// NewEmptyAccount returns an empty account.
|
|
func NewEmptyAccount() *Account {
|
|
return &Account{
|
|
Balance: new(big.Int),
|
|
CodeHash: emptyCodeHash,
|
|
}
|
|
}
|
|
|
|
// IsContract returns if the account contains contract code.
|
|
func (acct Account) IsContract() bool {
|
|
return !bytes.Equal(acct.CodeHash, emptyCodeHash)
|
|
}
|
|
|
|
// Storage represents in-memory cache/buffer of contract storage.
|
|
type Storage map[common.Hash]common.Hash
|
|
|
|
// SortedKeys sort the keys for deterministic iteration
|
|
func (s Storage) SortedKeys() []common.Hash {
|
|
keys := make([]common.Hash, len(s))
|
|
i := 0
|
|
// #nosec G705
|
|
for k := range s {
|
|
keys[i] = k
|
|
i++
|
|
}
|
|
sort.Slice(keys, func(i, j int) bool {
|
|
return bytes.Compare(keys[i].Bytes(), keys[j].Bytes()) < 0
|
|
})
|
|
return keys
|
|
}
|
|
|
|
// stateObject is the state of an acount
|
|
type stateObject struct {
|
|
db *StateDB
|
|
|
|
account Account
|
|
code []byte
|
|
|
|
// state storage
|
|
originStorage Storage
|
|
dirtyStorage Storage
|
|
|
|
address common.Address
|
|
|
|
// flags
|
|
dirtyCode bool
|
|
suicided bool
|
|
}
|
|
|
|
// newObject creates a state object.
|
|
func newObject(db *StateDB, address common.Address, account Account) *stateObject {
|
|
if account.Balance == nil {
|
|
account.Balance = new(big.Int)
|
|
}
|
|
if account.CodeHash == nil {
|
|
account.CodeHash = emptyCodeHash
|
|
}
|
|
return &stateObject{
|
|
db: db,
|
|
address: address,
|
|
account: account,
|
|
originStorage: make(Storage),
|
|
dirtyStorage: make(Storage),
|
|
}
|
|
}
|
|
|
|
// empty returns whether the account is considered empty.
|
|
func (s *stateObject) empty() bool {
|
|
return s.account.Nonce == 0 && s.account.Balance.Sign() == 0 && bytes.Equal(s.account.CodeHash, emptyCodeHash)
|
|
}
|
|
|
|
func (s *stateObject) markSuicided() {
|
|
s.suicided = true
|
|
}
|
|
|
|
// AddBalance adds amount to s's balance.
|
|
// It is used to add funds to the destination account of a transfer.
|
|
func (s *stateObject) AddBalance(amount *big.Int) {
|
|
if amount.Sign() == 0 {
|
|
return
|
|
}
|
|
s.SetBalance(new(big.Int).Add(s.Balance(), amount))
|
|
}
|
|
|
|
// SubBalance removes amount from s's balance.
|
|
// It is used to remove funds from the origin account of a transfer.
|
|
func (s *stateObject) SubBalance(amount *big.Int) {
|
|
if amount.Sign() == 0 {
|
|
return
|
|
}
|
|
s.SetBalance(new(big.Int).Sub(s.Balance(), amount))
|
|
}
|
|
|
|
// SetBalance update account balance.
|
|
func (s *stateObject) SetBalance(amount *big.Int) {
|
|
s.db.journal.append(balanceChange{
|
|
account: &s.address,
|
|
prev: new(big.Int).Set(s.account.Balance),
|
|
})
|
|
s.setBalance(amount)
|
|
}
|
|
|
|
func (s *stateObject) setBalance(amount *big.Int) {
|
|
s.account.Balance = amount
|
|
}
|
|
|
|
//
|
|
// Attribute accessors
|
|
//
|
|
|
|
// Returns the address of the contract/account
|
|
func (s *stateObject) Address() common.Address {
|
|
return s.address
|
|
}
|
|
|
|
// Code returns the contract code associated with this object, if any.
|
|
func (s *stateObject) Code() []byte {
|
|
if s.code != nil {
|
|
return s.code
|
|
}
|
|
if bytes.Equal(s.CodeHash(), emptyCodeHash) {
|
|
return nil
|
|
}
|
|
code := s.db.keeper.GetCode(s.db.ctx, common.BytesToHash(s.CodeHash()))
|
|
s.code = code
|
|
return code
|
|
}
|
|
|
|
// CodeSize returns the size of the contract code associated with this object,
|
|
// or zero if none.
|
|
func (s *stateObject) CodeSize() int {
|
|
return len(s.Code())
|
|
}
|
|
|
|
// SetCode set contract code to account
|
|
func (s *stateObject) SetCode(codeHash common.Hash, code []byte) {
|
|
prevcode := s.Code()
|
|
s.db.journal.append(codeChange{
|
|
account: &s.address,
|
|
prevhash: s.CodeHash(),
|
|
prevcode: prevcode,
|
|
})
|
|
s.setCode(codeHash, code)
|
|
}
|
|
|
|
func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
|
|
s.code = code
|
|
s.account.CodeHash = codeHash[:]
|
|
s.dirtyCode = true
|
|
}
|
|
|
|
// SetCode set nonce to account
|
|
func (s *stateObject) SetNonce(nonce uint64) {
|
|
s.db.journal.append(nonceChange{
|
|
account: &s.address,
|
|
prev: s.account.Nonce,
|
|
})
|
|
s.setNonce(nonce)
|
|
}
|
|
|
|
func (s *stateObject) setNonce(nonce uint64) {
|
|
s.account.Nonce = nonce
|
|
}
|
|
|
|
// CodeHash returns the code hash of account
|
|
func (s *stateObject) CodeHash() []byte {
|
|
return s.account.CodeHash
|
|
}
|
|
|
|
// Balance returns the balance of account
|
|
func (s *stateObject) Balance() *big.Int {
|
|
return s.account.Balance
|
|
}
|
|
|
|
// Nonce returns the nonce of account
|
|
func (s *stateObject) Nonce() uint64 {
|
|
return s.account.Nonce
|
|
}
|
|
|
|
// GetCommittedState query the committed state
|
|
func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
|
|
if value, cached := s.originStorage[key]; cached {
|
|
return value
|
|
}
|
|
// If no live objects are available, load it from keeper
|
|
value := s.db.keeper.GetState(s.db.ctx, s.Address(), key)
|
|
s.originStorage[key] = value
|
|
return value
|
|
}
|
|
|
|
// GetState query the current state (including dirty state)
|
|
func (s *stateObject) GetState(key common.Hash) common.Hash {
|
|
if value, dirty := s.dirtyStorage[key]; dirty {
|
|
return value
|
|
}
|
|
return s.GetCommittedState(key)
|
|
}
|
|
|
|
// SetState sets the contract state
|
|
func (s *stateObject) SetState(key common.Hash, value common.Hash) {
|
|
// If the new value is the same as old, don't set
|
|
prev := s.GetState(key)
|
|
if prev == value {
|
|
return
|
|
}
|
|
// New value is different, update and journal the change
|
|
s.db.journal.append(storageChange{
|
|
account: &s.address,
|
|
key: key,
|
|
prevalue: prev,
|
|
})
|
|
s.setState(key, value)
|
|
}
|
|
|
|
func (s *stateObject) setState(key, value common.Hash) {
|
|
s.dirtyStorage[key] = value
|
|
}
|