You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

204 lines
5.1 KiB

package state
import (
ac "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/common"
ptypes "github.com/tendermint/tendermint/permission/types" // for GlobalPermissionAddress ...
"github.com/tendermint/tendermint/vm"
"github.com/tendermint/tendermint/vm/sha3"
)
type TxCache struct {
backend *BlockCache
accounts map[Word256]vmAccountInfo
storages map[Tuple256]Word256
logs []*vm.Log
}
func NewTxCache(backend *BlockCache) *TxCache {
return &TxCache{
backend: backend,
accounts: make(map[Word256]vmAccountInfo),
storages: make(map[Tuple256]Word256),
logs: make([]*vm.Log, 0),
}
}
//-------------------------------------
// TxCache.account
func (cache *TxCache) GetAccount(addr Word256) *vm.Account {
acc, removed := vmUnpack(cache.accounts[addr])
if removed {
return nil
} else if acc == nil {
acc2 := cache.backend.GetAccount(addr.Postfix(20))
if acc2 != nil {
return toVMAccount(acc2)
}
}
return acc
}
func (cache *TxCache) UpdateAccount(acc *vm.Account) {
addr := acc.Address
// SANITY CHECK
_, removed := vmUnpack(cache.accounts[addr])
if removed {
panic("UpdateAccount on a removed account")
}
// SANITY CHECK END
cache.accounts[addr] = vmAccountInfo{acc, false}
}
func (cache *TxCache) RemoveAccount(acc *vm.Account) {
addr := acc.Address
// SANITY CHECK
_, removed := vmUnpack(cache.accounts[addr])
if removed {
panic("RemoveAccount on a removed account")
}
// SANITY CHECK END
cache.accounts[addr] = vmAccountInfo{acc, true}
}
// Creates a 20 byte address and bumps the creator's nonce.
func (cache *TxCache) CreateAccount(creator *vm.Account) *vm.Account {
// Generate an address
nonce := creator.Nonce
creator.Nonce += 1
addr := LeftPadWord256(NewContractAddress(creator.Address.Postfix(20), int(nonce)))
// Create account from address.
account, removed := vmUnpack(cache.accounts[addr])
if removed || account == nil {
account = &vm.Account{
Address: addr,
Balance: 0,
Code: nil,
Nonce: 0,
StorageRoot: Zero256,
Permissions: cache.GetAccount(ptypes.GlobalPermissionsAddress256).Permissions,
Other: nil,
}
cache.accounts[addr] = vmAccountInfo{account, false}
return account
} else {
// NONCE HANDLING SANITY CHECK OR SHA3 IS BROKEN
panic(Fmt("Could not create account, address already exists: %X", addr))
}
}
// TxCache.account
//-------------------------------------
// TxCache.storage
func (cache *TxCache) GetStorage(addr Word256, key Word256) Word256 {
// Check cache
value, ok := cache.storages[Tuple256{addr, key}]
if ok {
return value
}
// Load from backend
return cache.backend.GetStorage(addr, key)
}
// NOTE: Set value to zero to removed from the trie.
func (cache *TxCache) SetStorage(addr Word256, key Word256, value Word256) {
// SANITY CHECK
_, removed := vmUnpack(cache.accounts[addr])
if removed {
panic("SetStorage() on a removed account")
}
// SANITY CHECK END
cache.storages[Tuple256{addr, key}] = value
}
// TxCache.storage
//-------------------------------------
// These updates do not have to be in deterministic order,
// the backend is responsible for ordering updates.
func (cache *TxCache) Sync() {
// Remove or update storage
for addrKey, value := range cache.storages {
addr, key := Tuple256Split(addrKey)
cache.backend.SetStorage(addr, key, value)
}
// Remove or update accounts
for addr, accInfo := range cache.accounts {
acc, removed := vmUnpack(accInfo)
if removed {
cache.backend.RemoveAccount(addr.Postfix(20))
} else {
cache.backend.UpdateAccount(toStateAccount(acc))
}
}
// TODO support logs, add them to the cache somehow.
}
func (cache *TxCache) AddLog(log *vm.Log) {
cache.logs = append(cache.logs, log)
}
//-----------------------------------------------------------------------------
// Convenience function to return address of new contract
func NewContractAddress(caller []byte, nonce int) []byte {
temp := make([]byte, 32+8)
copy(temp, caller)
PutInt64BE(temp[32:], int64(nonce))
return sha3.Sha3(temp)[:20]
}
// Converts backend.Account to vm.Account struct.
func toVMAccount(acc *ac.Account) *vm.Account {
return &vm.Account{
Address: LeftPadWord256(acc.Address),
Balance: acc.Balance,
Code: acc.Code, // This is crazy.
Nonce: int64(acc.Sequence),
StorageRoot: LeftPadWord256(acc.StorageRoot),
Permissions: acc.Permissions.Copy(),
Other: acc.PubKey,
}
}
// Converts vm.Account to backend.Account struct.
func toStateAccount(acc *vm.Account) *ac.Account {
pubKey, ok := acc.Other.(ac.PubKey)
if !ok {
pubKey = nil
}
var storageRoot []byte
if acc.StorageRoot.IsZero() {
storageRoot = nil
} else {
storageRoot = acc.StorageRoot.Bytes()
}
return &ac.Account{
Address: acc.Address.Postfix(20),
PubKey: pubKey,
Balance: acc.Balance,
Code: acc.Code,
Sequence: int(acc.Nonce),
StorageRoot: storageRoot,
Permissions: acc.Permissions,
}
}
type vmAccountInfo struct {
account *vm.Account
removed bool
}
func vmUnpack(accInfo vmAccountInfo) (*vm.Account, bool) {
return accInfo.account, accInfo.removed
}