mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-09 06:33:16 +00:00
VMAppState bridge to vm.
This commit is contained in:
@@ -30,10 +30,12 @@ func SignBytes(o Signable) []byte {
|
||||
// on the blockchain.
|
||||
// Serialized by binary.[read|write]Reflect
|
||||
type Account struct {
|
||||
Address []byte
|
||||
PubKey PubKey
|
||||
Sequence uint
|
||||
Balance uint64
|
||||
Address []byte
|
||||
PubKey PubKey
|
||||
Sequence uint
|
||||
Balance uint64
|
||||
Code []byte // VM code
|
||||
StorageRoot []byte // VM storage merkle root.
|
||||
}
|
||||
|
||||
func (account *Account) Copy() *Account {
|
||||
@@ -42,7 +44,7 @@ func (account *Account) Copy() *Account {
|
||||
}
|
||||
|
||||
func (account *Account) String() string {
|
||||
return fmt.Sprintf("Account{%X:%v}", account.Address, account.PubKey)
|
||||
return fmt.Sprintf("Account{%X:%v C:%v S:%X}", account.Address, account.PubKey, len(account.Code), account.StorageRoot)
|
||||
}
|
||||
|
||||
func AccountEncoder(o interface{}, w io.Writer, n *int64, err *error) {
|
||||
|
||||
@@ -26,7 +26,7 @@ Tx (Transaction) is an atomic operation on the ledger state.
|
||||
|
||||
Account Txs:
|
||||
- SendTx Send coins to address
|
||||
- CallTx Send a msg to a contract that runs in the vm
|
||||
- CallTx Send a msg to a contract that runs in the vm
|
||||
|
||||
Validation Txs:
|
||||
- BondTx New validator posts a bond
|
||||
|
||||
@@ -6,6 +6,15 @@ func Fingerprint(slice []byte) []byte {
|
||||
return fingerprint
|
||||
}
|
||||
|
||||
func IsZeros(slice []byte) bool {
|
||||
for _, byt := range slice {
|
||||
if byt != byte(0) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func RightPadBytes(slice []byte, l int) []byte {
|
||||
if l < len(slice) {
|
||||
return slice
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
. "github.com/tendermint/tendermint/common"
|
||||
dbm "github.com/tendermint/tendermint/db"
|
||||
"github.com/tendermint/tendermint/merkle"
|
||||
"github.com/tendermint/tendermint/vm"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -97,6 +98,12 @@ func (s *State) Save() {
|
||||
s.DB.Set(stateKey, buf.Bytes())
|
||||
}
|
||||
|
||||
// NOTE: the valSets are not Copy()'d.
|
||||
// See TODOs in Copy() below.
|
||||
func (s *State) Reset(checkpoint *State) {
|
||||
*s = *checkpoint
|
||||
}
|
||||
|
||||
func (s *State) Copy() *State {
|
||||
return &State{
|
||||
DB: s.DB,
|
||||
@@ -104,9 +111,9 @@ func (s *State) Copy() *State {
|
||||
LastBlockHash: s.LastBlockHash,
|
||||
LastBlockParts: s.LastBlockParts,
|
||||
LastBlockTime: s.LastBlockTime,
|
||||
BondedValidators: s.BondedValidators.Copy(),
|
||||
LastBondedValidators: s.LastBondedValidators.Copy(),
|
||||
UnbondingValidators: s.UnbondingValidators.Copy(),
|
||||
BondedValidators: s.BondedValidators.Copy(), // TODO remove need for Copy() here.
|
||||
LastBondedValidators: s.LastBondedValidators.Copy(), // That is, make updates to the validator set
|
||||
UnbondingValidators: s.UnbondingValidators.Copy(), // copy the valSet lazily.
|
||||
accounts: s.accounts.Copy(),
|
||||
validatorInfos: s.validatorInfos.Copy(),
|
||||
}
|
||||
@@ -268,7 +275,9 @@ func (s *State) ExecTx(tx_ blk.Tx, runCall bool) error {
|
||||
return nil
|
||||
|
||||
case *blk.CallTx:
|
||||
accounts := map[string]*account.Account{}
|
||||
accounts := make(map[string]*account.Account)
|
||||
|
||||
// validate input
|
||||
inAcc := s.GetAccount(tx.Input.Address)
|
||||
if inAcc == nil {
|
||||
return blk.ErrTxInvalidAddress
|
||||
@@ -284,7 +293,7 @@ func (s *State) ExecTx(tx_ blk.Tx, runCall bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// validate output address
|
||||
// validate output
|
||||
if len(tx.Address) != 20 {
|
||||
return blk.ErrTxInvalidAddress
|
||||
}
|
||||
@@ -292,19 +301,54 @@ func (s *State) ExecTx(tx_ blk.Tx, runCall bool) error {
|
||||
if outAcc == nil {
|
||||
return blk.ErrTxInvalidAddress
|
||||
}
|
||||
if inTotal < tx.Fee {
|
||||
return blk.ErrTxInsufficientFunds
|
||||
}
|
||||
accounts[string(tx.Address)] = outAcc
|
||||
|
||||
inTotal -= tx.Fee
|
||||
// Remember unmodified state in case call fails.
|
||||
checkpoint := s.Copy()
|
||||
|
||||
// Good! Adjust accounts
|
||||
s.AdjustByInputs(accounts, []*blk.TxInput{tx.Input})
|
||||
outAcc.Balance += inTotal
|
||||
// Good! Adjust accounts and maybe actually run the tx.
|
||||
value := inTotal - tx.Fee
|
||||
s.AdjustByInputs(accounts, []*blk.TxInput{tx.Input}) // adjusts inAcc.
|
||||
outAcc.Balance += value
|
||||
s.UpdateAccounts(accounts)
|
||||
|
||||
if runCall {
|
||||
// TODO: Run the contract call!
|
||||
// TODO: refund some gas
|
||||
appState := NewVMAppState(s)
|
||||
params := vm.Params{
|
||||
BlockHeight: uint64(s.LastBlockHeight),
|
||||
BlockHash: vm.BytesToWord(s.LastBlockHash),
|
||||
BlockTime: s.LastBlockTime.Unix(),
|
||||
GasLimit: 10000000,
|
||||
}
|
||||
caller := toVMAccount(inAcc)
|
||||
callee := toVMAccount(outAcc)
|
||||
appState.AddAccount(caller) // because we adjusted by input above.
|
||||
appState.AddAccount(callee) // because we adjusted by input above.
|
||||
vmach := vm.NewVM(appState, params, caller.Address)
|
||||
gas := tx.GasLimit
|
||||
ret, err_ := vmach.Call(caller, callee, outAcc.Code, tx.Data, value, &gas)
|
||||
if err_ != nil {
|
||||
// Failure
|
||||
// Revert the state while charging gas for the user.
|
||||
s.Reset(checkpoint)
|
||||
callee.Balance -= tx.Fee
|
||||
s.UpdateAccounts(accounts)
|
||||
} else {
|
||||
// Success
|
||||
appState.Sync()
|
||||
}
|
||||
// Create a receipt from the ret and whether errored.
|
||||
log.Info("VM call complete", "caller", caller, "callee", callee, "return", ret, "err", err_)
|
||||
} else {
|
||||
// The mempool does not call txs until
|
||||
// the proposer determines the order of txs.
|
||||
// So mempool will skip the actual .Call(),
|
||||
// and only deduct from the caller's balance.
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case *blk.BondTx:
|
||||
@@ -706,6 +750,11 @@ func (s *State) UpdateAccounts(accounts map[string]*account.Account) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *State) RemoveAccount(address []byte) bool {
|
||||
_, removed := s.accounts.Remove(address)
|
||||
return removed
|
||||
}
|
||||
|
||||
// The returned ValidatorInfo is a copy, so mutating it
|
||||
// has no side effects.
|
||||
func (s *State) GetValidatorInfo(address []byte) *ValidatorInfo {
|
||||
|
||||
217
state/vm_app_state.go
Normal file
217
state/vm_app_state.go
Normal file
@@ -0,0 +1,217 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
ac "github.com/tendermint/tendermint/account"
|
||||
"github.com/tendermint/tendermint/binary"
|
||||
. "github.com/tendermint/tendermint/common"
|
||||
"github.com/tendermint/tendermint/merkle"
|
||||
"github.com/tendermint/tendermint/vm"
|
||||
)
|
||||
|
||||
// Converts state.Account to vm.Account struct.
|
||||
func toVMAccount(acc *ac.Account) *vm.Account {
|
||||
return &vm.Account{
|
||||
Address: vm.BytesToWord(acc.Address),
|
||||
Balance: acc.Balance,
|
||||
Code: acc.Code, // This is crazy.
|
||||
Nonce: uint64(acc.Sequence),
|
||||
StorageRoot: vm.BytesToWord(acc.StorageRoot),
|
||||
}
|
||||
}
|
||||
|
||||
// Converts vm.Account to state.Account struct.
|
||||
func toStateAccount(acc *vm.Account) *ac.Account {
|
||||
return &ac.Account{
|
||||
Address: acc.Address.Address(),
|
||||
Balance: acc.Balance,
|
||||
Code: acc.Code,
|
||||
Sequence: uint(acc.Nonce),
|
||||
StorageRoot: acc.StorageRoot.Bytes(),
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
type AccountInfo struct {
|
||||
account *vm.Account
|
||||
deleted bool
|
||||
}
|
||||
|
||||
type VMAppState struct {
|
||||
state *State
|
||||
|
||||
accounts map[string]AccountInfo
|
||||
storage map[string]vm.Word
|
||||
logs []*vm.Log
|
||||
}
|
||||
|
||||
func NewVMAppState(state *State) *VMAppState {
|
||||
return &VMAppState{
|
||||
state: state,
|
||||
accounts: make(map[string]AccountInfo),
|
||||
storage: make(map[string]vm.Word),
|
||||
logs: make([]*vm.Log, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func unpack(accInfo AccountInfo) (*vm.Account, bool) {
|
||||
return accInfo.account, accInfo.deleted
|
||||
}
|
||||
|
||||
// Used to add the origin of the tx to VMAppState.
|
||||
func (vas *VMAppState) AddAccount(account *vm.Account) error {
|
||||
if _, ok := vas.accounts[account.Address.String()]; ok {
|
||||
return Errorf("Account already exists: %X", account.Address)
|
||||
} else {
|
||||
vas.accounts[account.Address.String()] = AccountInfo{account, false}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (vas *VMAppState) GetAccount(addr vm.Word) (*vm.Account, error) {
|
||||
account, deleted := unpack(vas.accounts[addr.String()])
|
||||
if deleted {
|
||||
return nil, Errorf("Account was deleted: %X", addr)
|
||||
} else if account != nil {
|
||||
return account, nil
|
||||
} else {
|
||||
acc := vas.state.GetAccount(addr.Address())
|
||||
if acc == nil {
|
||||
return nil, Errorf("Invalid account addr: %X", addr)
|
||||
}
|
||||
return toVMAccount(acc), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (vas *VMAppState) UpdateAccount(account *vm.Account) error {
|
||||
accountInfo, ok := vas.accounts[account.Address.String()]
|
||||
if !ok {
|
||||
vas.accounts[account.Address.String()] = AccountInfo{account, false}
|
||||
return nil
|
||||
}
|
||||
account, deleted := unpack(accountInfo)
|
||||
if deleted {
|
||||
return Errorf("Account was deleted: %X", account.Address)
|
||||
} else {
|
||||
vas.accounts[account.Address.String()] = AccountInfo{account, false}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (vas *VMAppState) DeleteAccount(account *vm.Account) error {
|
||||
accountInfo, ok := vas.accounts[account.Address.String()]
|
||||
if !ok {
|
||||
vas.accounts[account.Address.String()] = AccountInfo{account, true}
|
||||
return nil
|
||||
}
|
||||
account, deleted := unpack(accountInfo)
|
||||
if deleted {
|
||||
return Errorf("Account was already deleted: %X", account.Address)
|
||||
} else {
|
||||
vas.accounts[account.Address.String()] = AccountInfo{account, true}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (vas *VMAppState) CreateAccount(addr vm.Word) (*vm.Account, error) {
|
||||
account, deleted := unpack(vas.accounts[addr.String()])
|
||||
if deleted || account == nil {
|
||||
account = &vm.Account{
|
||||
Address: addr,
|
||||
Balance: 0,
|
||||
Code: nil,
|
||||
Nonce: 0,
|
||||
StorageRoot: vm.Zero,
|
||||
}
|
||||
vas.accounts[addr.String()] = AccountInfo{account, false}
|
||||
return account, nil
|
||||
} else {
|
||||
return nil, Errorf("Account already exists: %X", addr)
|
||||
}
|
||||
}
|
||||
|
||||
func (vas *VMAppState) GetStorage(addr vm.Word, key vm.Word) (vm.Word, error) {
|
||||
account, deleted := unpack(vas.accounts[addr.String()])
|
||||
if account == nil {
|
||||
return vm.Zero, Errorf("Invalid account addr: %X", addr)
|
||||
} else if deleted {
|
||||
return vm.Zero, Errorf("Account was deleted: %X", addr)
|
||||
}
|
||||
|
||||
value, ok := vas.storage[addr.String()+key.String()]
|
||||
if ok {
|
||||
return value, nil
|
||||
} else {
|
||||
return vm.Zero, nil
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Set value to zero to delete from the trie.
|
||||
func (vas *VMAppState) SetStorage(addr vm.Word, key vm.Word, value vm.Word) (bool, error) {
|
||||
account, deleted := unpack(vas.accounts[addr.String()])
|
||||
if account == nil {
|
||||
return false, Errorf("Invalid account addr: %X", addr)
|
||||
} else if deleted {
|
||||
return false, Errorf("Account was deleted: %X", addr)
|
||||
}
|
||||
|
||||
_, ok := vas.storage[addr.String()+key.String()]
|
||||
vas.storage[addr.String()+key.String()] = value
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
func (vas *VMAppState) Sync() {
|
||||
|
||||
// Determine order for accounts
|
||||
addrStrs := []string{}
|
||||
for addrStr := range vas.accounts {
|
||||
addrStrs = append(addrStrs, addrStr)
|
||||
}
|
||||
sort.Strings(addrStrs)
|
||||
|
||||
// Update or delete accounts.
|
||||
for _, addrStr := range addrStrs {
|
||||
account, deleted := unpack(vas.accounts[addrStr])
|
||||
if deleted {
|
||||
removed := vas.state.RemoveAccount(account.Address.Address())
|
||||
if !removed {
|
||||
panic(Fmt("Could not remove account to be deleted: %X", account.Address))
|
||||
}
|
||||
} else {
|
||||
if account == nil {
|
||||
panic(Fmt("Account should not be nil for addr: %X", account.Address))
|
||||
}
|
||||
vas.state.UpdateAccount(toStateAccount(account))
|
||||
}
|
||||
}
|
||||
|
||||
// Update or delete storage items.
|
||||
storage := merkle.NewIAVLTree(
|
||||
binary.BasicCodec, // TODO change
|
||||
binary.BasicCodec, // TODO change
|
||||
1024, // TODO change.
|
||||
vas.state.DB,
|
||||
)
|
||||
|
||||
for addrKey, value := range vas.storage {
|
||||
addrKeyBytes := []byte(addrKey)
|
||||
addr := addrKeyBytes[:32]
|
||||
key := addrKeyBytes[32:]
|
||||
if value.IsZero() {
|
||||
_, removed := storage.Remove(key)
|
||||
if !removed {
|
||||
panic(Fmt("Storage could not be removed for addr: %X @ %X", addr, key))
|
||||
}
|
||||
} else {
|
||||
storage.Set(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO support logs, add them to the state somehow.
|
||||
}
|
||||
|
||||
func (vas *VMAppState) AddLog(log *vm.Log) {
|
||||
vas.logs = append(vas.logs, log)
|
||||
}
|
||||
20
vm/common.go
20
vm/common.go
@@ -4,24 +4,6 @@ import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
var (
|
||||
Zero = Word{0}
|
||||
One = Word{1}
|
||||
)
|
||||
|
||||
type Word [32]byte
|
||||
|
||||
func (w Word) String() string { return string(w[:]) }
|
||||
func (w Word) Copy() Word { return w }
|
||||
func (w Word) Bytes() []byte { return w[:] } // copied.
|
||||
func (w Word) IsZero() bool {
|
||||
accum := byte(0)
|
||||
for _, byt := range w {
|
||||
accum |= byt
|
||||
}
|
||||
return accum == 0
|
||||
}
|
||||
|
||||
func Uint64ToWord(i uint64) Word {
|
||||
word := Word{}
|
||||
PutUint64(word[:], i)
|
||||
@@ -44,8 +26,6 @@ func RightPadWord(bz []byte) (word Word) {
|
||||
return
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
func GetUint64(word Word) uint64 {
|
||||
return binary.LittleEndian.Uint64(word[:])
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ type FakeAppState struct {
|
||||
|
||||
func (fas *FakeAppState) GetAccount(addr Word) (*Account, error) {
|
||||
account := fas.accounts[addr.String()]
|
||||
if account == nil {
|
||||
if account != nil {
|
||||
return account, nil
|
||||
} else {
|
||||
return nil, Errorf("Invalid account addr: %v", addr)
|
||||
@@ -43,15 +43,15 @@ func (fas *FakeAppState) DeleteAccount(account *Account) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (fas *FakeAppState) CreateAccount(addr Word, balance uint64) (*Account, error) {
|
||||
func (fas *FakeAppState) CreateAccount(addr Word) (*Account, error) {
|
||||
account := fas.accounts[addr.String()]
|
||||
if account == nil {
|
||||
return &Account{
|
||||
Address: addr,
|
||||
Balance: balance,
|
||||
Code: nil,
|
||||
Nonce: 0,
|
||||
StateRoot: Zero,
|
||||
Address: addr,
|
||||
Balance: 0,
|
||||
Code: nil,
|
||||
Nonce: 0,
|
||||
StorageRoot: Zero,
|
||||
}, nil
|
||||
} else {
|
||||
return nil, Errorf("Invalid account addr: %v", addr)
|
||||
@@ -83,16 +83,6 @@ func (fas *FakeAppState) SetStorage(addr Word, key Word, value Word) (bool, erro
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
func (fas *FakeAppState) RemoveStorage(addr Word, key Word) error {
|
||||
_, ok := fas.accounts[addr.String()]
|
||||
if !ok {
|
||||
return Errorf("Invalid account addr: %v", addr)
|
||||
}
|
||||
|
||||
delete(fas.storage, addr.String()+key.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fas *FakeAppState) AddLog(log *Log) {
|
||||
fas.logs = append(fas.logs, log)
|
||||
}
|
||||
@@ -103,20 +93,20 @@ func main() {
|
||||
storage: make(map[string]Word),
|
||||
logs: nil,
|
||||
}
|
||||
params := VMParams{
|
||||
params := Params{
|
||||
BlockHeight: 0,
|
||||
BlockHash: Zero,
|
||||
BlockTime: 0,
|
||||
GasLimit: 0,
|
||||
}
|
||||
ourVm := NewVM(appState, params)
|
||||
ourVm := NewVM(appState, params, Zero)
|
||||
|
||||
// Create accounts
|
||||
account1, err := appState.CreateAccount(Uint64ToWord(100), 0)
|
||||
account1, err := appState.CreateAccount(Uint64ToWord(100))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
account2, err := appState.CreateAccount(Uint64ToWord(101), 0)
|
||||
account2, err := appState.CreateAccount(Uint64ToWord(101))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
43
vm/types.go
43
vm/types.go
@@ -6,12 +6,33 @@ const (
|
||||
defaultDataStackCapacity = 10
|
||||
)
|
||||
|
||||
var (
|
||||
Zero = Word{0}
|
||||
One = Word{1}
|
||||
)
|
||||
|
||||
type Word [32]byte
|
||||
|
||||
func (w Word) String() string { return string(w[:]) }
|
||||
func (w Word) Copy() Word { return w }
|
||||
func (w Word) Bytes() []byte { return w[:] } // copied.
|
||||
func (w Word) Address() []byte { return w[:20] }
|
||||
func (w Word) IsZero() bool {
|
||||
accum := byte(0)
|
||||
for _, byt := range w {
|
||||
accum |= byt
|
||||
}
|
||||
return accum == 0
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
type Account struct {
|
||||
Address Word
|
||||
Balance uint64
|
||||
Code []byte
|
||||
Nonce uint64
|
||||
StateRoot Word
|
||||
Address Word
|
||||
Balance uint64
|
||||
Code []byte
|
||||
Nonce uint64
|
||||
StorageRoot Word
|
||||
}
|
||||
|
||||
type Log struct {
|
||||
@@ -27,13 +48,19 @@ type AppState interface {
|
||||
GetAccount(addr Word) (*Account, error)
|
||||
UpdateAccount(*Account) error
|
||||
DeleteAccount(*Account) error
|
||||
CreateAccount(addr Word, balance uint64) (*Account, error)
|
||||
CreateAccount(addr Word) (*Account, error)
|
||||
|
||||
// Storage
|
||||
GetStorage(Word, Word) (Word, error)
|
||||
SetStorage(Word, Word, Word) (bool, error)
|
||||
RemoveStorage(Word, Word) error
|
||||
SetStorage(Word, Word, Word) (bool, error) // Setting to Zero is deleting.
|
||||
|
||||
// Logs
|
||||
AddLog(*Log)
|
||||
}
|
||||
|
||||
type Params struct {
|
||||
BlockHeight uint64
|
||||
BlockHash Word
|
||||
BlockTime int64
|
||||
GasLimit uint64
|
||||
}
|
||||
|
||||
84
vm/vm.go
84
vm/vm.go
@@ -31,40 +31,25 @@ const (
|
||||
|
||||
type VM struct {
|
||||
appState AppState
|
||||
params VMParams
|
||||
params Params
|
||||
origin Word
|
||||
|
||||
callDepth int
|
||||
}
|
||||
|
||||
type VMParams struct {
|
||||
BlockHeight uint64
|
||||
BlockHash Word
|
||||
BlockTime int64
|
||||
GasLimit uint64
|
||||
CallStackLimit uint64
|
||||
Origin Word
|
||||
}
|
||||
|
||||
func NewVM(appState AppState, params VMParams) *VM {
|
||||
func NewVM(appState AppState, params Params, origin Word) *VM {
|
||||
return &VM{
|
||||
appState: appState,
|
||||
params: params,
|
||||
origin: origin,
|
||||
callDepth: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// When running a transaction, the caller the pays for the fees.
|
||||
|
||||
// Check caller's account balance vs feeLimit and value
|
||||
if caller.Balance < (feeLimit + value) {
|
||||
return nil, ErrInsufficientBalance
|
||||
}
|
||||
caller.Balance -= (feeLimit + value)
|
||||
|
||||
*/
|
||||
|
||||
// gas: the maximum gas that will be run.
|
||||
// value: The value transfered from caller to callee.
|
||||
// NOTE: The value should already have been transferred to the callee.
|
||||
// NOTE: When Call() fails, the value should be returned to the caller.
|
||||
// gas: The maximum gas that will be run.
|
||||
// When the function returns, *gas will be the amount of remaining gas.
|
||||
func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, gas *uint64) (output []byte, err error) {
|
||||
|
||||
@@ -282,17 +267,16 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga
|
||||
return nil, firstErr(err, ErrInsufficientGas)
|
||||
}
|
||||
account, err_ := vm.appState.GetAccount(addr) // TODO ensure that 20byte lengths are supported.
|
||||
if err = firstErr(err, err_); err != nil {
|
||||
return nil, err
|
||||
if err_ != nil {
|
||||
return nil, firstErr(err, err_)
|
||||
}
|
||||
balance := account.Balance
|
||||
stack.Push64(balance)
|
||||
fmt.Printf(" => %v (%X)\n", balance, addr)
|
||||
|
||||
case ORIGIN: // 0x32
|
||||
origin := vm.params.Origin
|
||||
stack.Push(origin)
|
||||
fmt.Printf(" => %X\n", origin)
|
||||
stack.Push(vm.origin)
|
||||
fmt.Printf(" => %X\n", vm.origin)
|
||||
|
||||
case CALLER: // 0x33
|
||||
stack.Push(caller.Address)
|
||||
@@ -360,8 +344,8 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga
|
||||
return nil, firstErr(err, ErrInsufficientGas)
|
||||
}
|
||||
account, err_ := vm.appState.GetAccount(addr)
|
||||
if err = firstErr(err, err_); err != nil {
|
||||
return nil, err
|
||||
if err_ != nil {
|
||||
return nil, firstErr(err, err_)
|
||||
}
|
||||
code := account.Code
|
||||
l := uint64(len(code))
|
||||
@@ -374,8 +358,8 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga
|
||||
return nil, firstErr(err, ErrInsufficientGas)
|
||||
}
|
||||
account, err_ := vm.appState.GetAccount(addr)
|
||||
if err = firstErr(err, err_); err != nil {
|
||||
return nil, err
|
||||
if err_ != nil {
|
||||
return nil, firstErr(err, err_)
|
||||
}
|
||||
code := account.Code
|
||||
memOff := stack.Pop64()
|
||||
@@ -383,11 +367,11 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga
|
||||
length := stack.Pop64()
|
||||
data, ok := subslice(code, codeOff, length)
|
||||
if !ok {
|
||||
return nil, ErrCodeOutOfBounds
|
||||
return nil, firstErr(err, ErrCodeOutOfBounds)
|
||||
}
|
||||
dest, ok := subslice(memory, memOff, length)
|
||||
if !ok {
|
||||
return nil, ErrMemoryOutOfBounds
|
||||
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
||||
}
|
||||
copy(dest, data)
|
||||
fmt.Printf(" => [%v, %v, %v] %X\n", memOff, codeOff, length, data)
|
||||
@@ -422,7 +406,7 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga
|
||||
offset := stack.Pop64()
|
||||
data, ok := subslice(memory, offset, 32)
|
||||
if !ok {
|
||||
return nil, ErrMemoryOutOfBounds
|
||||
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
||||
}
|
||||
stack.Push(RightPadWord(data))
|
||||
fmt.Printf(" => 0x%X\n", data)
|
||||
@@ -431,7 +415,7 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga
|
||||
offset, data := stack.Pop64(), stack.Pop()
|
||||
dest, ok := subslice(memory, offset, 32)
|
||||
if !ok {
|
||||
return nil, ErrMemoryOutOfBounds
|
||||
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
||||
}
|
||||
copy(dest, data[:])
|
||||
fmt.Printf(" => 0x%X\n", data)
|
||||
@@ -439,7 +423,7 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga
|
||||
case MSTORE8: // 0x53
|
||||
offset, val := stack.Pop64(), byte(stack.Pop64()&0xFF)
|
||||
if len(memory) <= int(offset) {
|
||||
return nil, ErrMemoryOutOfBounds
|
||||
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
||||
}
|
||||
memory[offset] = val
|
||||
fmt.Printf(" => [%v] 0x%X\n", offset, val)
|
||||
@@ -540,8 +524,6 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga
|
||||
// Check balance
|
||||
if caller.Balance < value {
|
||||
return nil, firstErr(err, ErrInsufficientBalance)
|
||||
} else {
|
||||
caller.Balance -= value
|
||||
}
|
||||
|
||||
// Create a new address
|
||||
@@ -551,13 +533,17 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga
|
||||
|
||||
// TODO charge for gas to create account _ the code length * GasCreateByte
|
||||
|
||||
newAccount, err := vm.appState.CreateAccount(addr, value)
|
||||
newAccount, err := vm.appState.CreateAccount(addr)
|
||||
if err != nil {
|
||||
stack.Push(Zero)
|
||||
fmt.Printf(" (*) 0x0 %v\n", err)
|
||||
} else {
|
||||
if err_ := transfer(callee, newAccount, value); err_ != nil {
|
||||
return nil, err_ // prob never happens...
|
||||
}
|
||||
// Run the input to get the contract code.
|
||||
// The code as well as the input to the code are the same.
|
||||
// Will it halt? Yes.
|
||||
ret, err_ := vm.Call(callee, newAccount, input, input, value, gas)
|
||||
if err_ != nil {
|
||||
caller.Balance += value // Return the balance
|
||||
@@ -592,11 +578,11 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga
|
||||
// Begin execution
|
||||
var ret []byte
|
||||
var err error
|
||||
|
||||
// If addr is in nativeContracts
|
||||
if nativeContract := nativeContracts[addr]; nativeContract != nil {
|
||||
// Native contract
|
||||
ret, err = nativeContract(args, &gasLimit)
|
||||
} else {
|
||||
// EVM contract
|
||||
if ok = useGas(gas, GasGetAccount); !ok {
|
||||
return nil, firstErr(err, ErrInsufficientGas)
|
||||
}
|
||||
@@ -607,6 +593,9 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga
|
||||
if op == CALLCODE {
|
||||
ret, err = vm.Call(callee, callee, account.Code, args, value, gas)
|
||||
} else {
|
||||
if err := transfer(callee, account, value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret, err = vm.Call(callee, account, account.Code, args, value, gas)
|
||||
}
|
||||
}
|
||||
@@ -642,6 +631,7 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga
|
||||
if ok = useGas(gas, GasGetAccount); !ok {
|
||||
return nil, firstErr(err, ErrInsufficientGas)
|
||||
}
|
||||
// TODO if the receiver is Zero, then make it the fee.
|
||||
receiver, err_ := vm.appState.GetAccount(addr)
|
||||
if err = firstErr(err, err_); err != nil {
|
||||
return nil, err
|
||||
@@ -719,3 +709,13 @@ func createAddress(creatorAddr Word, nonce uint64) Word {
|
||||
PutUint64(temp[32:], nonce)
|
||||
return RightPadWord(sha3.Sha3(temp)[:20])
|
||||
}
|
||||
|
||||
func transfer(from, to *Account, amount uint64) error {
|
||||
if from.Balance < amount {
|
||||
return ErrInsufficientBalance
|
||||
} else {
|
||||
from.Balance -= amount
|
||||
to.Balance += amount
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user