mirror of
https://github.com/fluencelabs/tendermint
synced 2025-05-16 16:41:20 +00:00
vm makeover
This commit is contained in:
parent
f03547007a
commit
c21369cebd
101
vm/common.go
101
vm/common.go
@ -1,73 +1,54 @@
|
|||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"encoding/binary"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
GasStorageGet = Big(50)
|
Zero = Word{0}
|
||||||
GasStorageAdd = Big(20000)
|
One = Word{1}
|
||||||
GasStorageMod = Big(5000)
|
|
||||||
GasLogBase = Big(375)
|
|
||||||
GasLogTopic = Big(375)
|
|
||||||
GasLogByte = Big(8)
|
|
||||||
GasCreate = Big(32000)
|
|
||||||
GasCreateByte = Big(200)
|
|
||||||
GasCall = Big(40)
|
|
||||||
GasCallValueTransfer = Big(9000)
|
|
||||||
GasStipend = Big(2300)
|
|
||||||
GasCallNewAccount = Big(25000)
|
|
||||||
GasReturn = Big(0)
|
|
||||||
GasStop = Big(0)
|
|
||||||
GasJumpDest = Big(1)
|
|
||||||
|
|
||||||
RefundStorage = Big(15000)
|
|
||||||
RefundSuicide = Big(24000)
|
|
||||||
|
|
||||||
GasMemWord = Big(3)
|
|
||||||
GasQuadCoeffDenom = Big(512)
|
|
||||||
GasContractByte = Big(200)
|
|
||||||
GasTransaction = Big(21000)
|
|
||||||
GasTxDataNonzeroByte = Big(68)
|
|
||||||
GasTxDataZeroByte = Big(4)
|
|
||||||
GasTx = Big(21000)
|
|
||||||
GasExp = Big(10)
|
|
||||||
GasExpByte = Big(10)
|
|
||||||
|
|
||||||
GasSha3Base = Big(30)
|
|
||||||
GasSha3Word = Big(6)
|
|
||||||
GasSha256Base = Big(60)
|
|
||||||
GasSha256Word = Big(12)
|
|
||||||
GasRipemdBase = Big(600)
|
|
||||||
GasRipemdWord = Big(12)
|
|
||||||
GasEcrecover = Big(3000)
|
|
||||||
GasIdentityBase = Big(15)
|
|
||||||
GasIdentityWord = Big(3)
|
|
||||||
GasCopyWord = Big(3)
|
|
||||||
|
|
||||||
Pow256 = BigPow(2, 256)
|
|
||||||
|
|
||||||
LogTyPretty byte = 0x1
|
|
||||||
LogTyDiff byte = 0x2
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const MaxCallDepth = 1025
|
type Word [32]byte
|
||||||
|
|
||||||
func calcMemSize(off, l *big.Int) *big.Int {
|
func (w Word) Copy() Word { return w }
|
||||||
if l.Cmp(Big0) == 0 {
|
func (w Word) Bytes() []byte { return w[:] } // copied.
|
||||||
return Big0
|
func (w Word) IsZero() bool {
|
||||||
|
accum := byte(0)
|
||||||
|
for _, byt := range w {
|
||||||
|
accum |= byt
|
||||||
}
|
}
|
||||||
|
return accum == 0
|
||||||
return new(big.Int).Add(off, l)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mainly used for print variables and passing to Print*
|
func Uint64ToWord(i uint64) Word {
|
||||||
func toValue(val *big.Int) interface{} {
|
word := Word{}
|
||||||
// Let's assume a string on right padded zero's
|
PutUint64(word[:], i)
|
||||||
b := val.Bytes()
|
return word
|
||||||
if b[0] != 0 && b[len(b)-1] == 0x0 && b[len(b)-2] == 0x0 {
|
}
|
||||||
return string(b)
|
|
||||||
}
|
func BytesToWord(bz []byte) Word {
|
||||||
|
word := Word{}
|
||||||
return val
|
copy(word[:], bz)
|
||||||
|
return word
|
||||||
|
}
|
||||||
|
|
||||||
|
func LeftPadWord(bz []byte) (word Word) {
|
||||||
|
copy(word[:], bz)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func RightPadWord(bz []byte) (word Word) {
|
||||||
|
copy(word[32-len(bz):], bz)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func GetUint64(word Word) uint64 {
|
||||||
|
return binary.LittleEndian.Uint64(word[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func PutUint64(dest []byte, i uint64) {
|
||||||
|
binary.LittleEndian.PutUint64(dest, i)
|
||||||
}
|
}
|
||||||
|
@ -1,141 +0,0 @@
|
|||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultDataStackCapacity = 10
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrCallStackOverflow = errors.New("CallStackOverflow")
|
|
||||||
ErrCallStackUnderflow = errors.New("CallStackUnderflow")
|
|
||||||
ErrInsufficientGas = errors.New("InsufficientGas")
|
|
||||||
)
|
|
||||||
|
|
||||||
type AppState interface {
|
|
||||||
|
|
||||||
// Accounts
|
|
||||||
GetAccount([]byte) *Account
|
|
||||||
UpdateAccount(*Account)
|
|
||||||
|
|
||||||
// Storage
|
|
||||||
GetStorage([]byte, []byte)
|
|
||||||
UpdateStorage([]byte, []byte)
|
|
||||||
RemoveStorage([]byte)
|
|
||||||
|
|
||||||
// Logs
|
|
||||||
AddLog(*Log)
|
|
||||||
}
|
|
||||||
|
|
||||||
type VMCall struct {
|
|
||||||
caller *Account
|
|
||||||
target *Account
|
|
||||||
code []byte
|
|
||||||
gasLimit uint64
|
|
||||||
gasUsed uint64
|
|
||||||
dataStack *Stack
|
|
||||||
memory *Memory
|
|
||||||
}
|
|
||||||
|
|
||||||
func (vmCall *VMCall) gasLeft() uint {
|
|
||||||
return vmCall.gasLimit - vmCall.gasUsed
|
|
||||||
}
|
|
||||||
|
|
||||||
type VMParams struct {
|
|
||||||
BlockHeight uint
|
|
||||||
BlockHash []byte
|
|
||||||
BlockTime int64
|
|
||||||
GasLimit uint64
|
|
||||||
GasPrice uint64
|
|
||||||
CallStackLimit uint
|
|
||||||
Origin []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
type VMEnvironment struct {
|
|
||||||
params VMParams
|
|
||||||
appState AppState
|
|
||||||
callStack []*VMCall
|
|
||||||
lastCall *VMCall
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewVMEnvironment(appState AppState, params VMParams) *VMEnvironment {
|
|
||||||
return &VMEnvironment{
|
|
||||||
params: params,
|
|
||||||
appState: appState,
|
|
||||||
callStack: make([]*VMCall, 0, params.CallStackLimit),
|
|
||||||
lastCall: nil,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX I think this is all wrong.
|
|
||||||
// Begin a new transaction (root call)
|
|
||||||
func (env *VMEnvironment) SetupTransaction(caller, target *Account, gasLimit, value uint64, input []byte) error {
|
|
||||||
// TODO charge gas for transaction
|
|
||||||
var gasUsed uint64 = 0
|
|
||||||
return env.setupCall(caller, target, gasUsed, gasLimit, value, input)
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX I think this is all wrong.
|
|
||||||
// env.lastCall.target (env.callStack[-1]) is calling target.
|
|
||||||
func (env *VMEnvironment) SetupCall(target *Account, gasLimit, value uint64, input []byte) error {
|
|
||||||
|
|
||||||
// Gas check
|
|
||||||
if env.lastCall.gasLeft() < gasLimit {
|
|
||||||
return ErrInsufficientGas
|
|
||||||
}
|
|
||||||
|
|
||||||
// Depth check
|
|
||||||
if len(env.callStack) == env.params.CallStackLimit {
|
|
||||||
return ErrCallStackOverflow
|
|
||||||
}
|
|
||||||
|
|
||||||
var gasUsed uint64 = 0
|
|
||||||
var caller = env.lastCall.target
|
|
||||||
return env.setupCall(caller, target, gasUsed, gasLimit, value, input)
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX I think this is all wrong.
|
|
||||||
func (env *VMEnvironment) setupCall(caller, target *Account, gasUsed, gasLimit uint64, input []byte) error {
|
|
||||||
|
|
||||||
// Incr nonces
|
|
||||||
caller.IncrNonce()
|
|
||||||
|
|
||||||
// TODO Charge TX and data gas
|
|
||||||
|
|
||||||
// Value transfer
|
|
||||||
if value != 0 {
|
|
||||||
// TODO Charge for gas
|
|
||||||
err := caller.SubBalance(value)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = target.AddBalance(value)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new VMCall
|
|
||||||
vmCall := &VMCall{
|
|
||||||
caller: caller,
|
|
||||||
target: target,
|
|
||||||
code: target.Code(),
|
|
||||||
gasLimit: gasLimit,
|
|
||||||
gasUsed: gasUsed,
|
|
||||||
dataStack: NewStack(defaultDataStackCapacity),
|
|
||||||
memory: NewMemory(),
|
|
||||||
}
|
|
||||||
env.callStack = append(env.callStack, vmCall)
|
|
||||||
env.lastCall = vmCall
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (env *VMEnvironment) CallStackDepth() int {
|
|
||||||
return len(env.callStack)
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/state"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Execution struct {
|
|
||||||
env Environment
|
|
||||||
address, input []byte
|
|
||||||
Gas, price, value *big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewExecution(env Environment, address, input []byte, gas, gasPrice, value *big.Int) *Execution {
|
|
||||||
return &Execution{env: env, address: address, input: input, Gas: gas, price: gasPrice, value: value}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Execution) Addr() []byte {
|
|
||||||
return self.address
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Execution) Call(codeAddr []byte, caller ContextRef) ([]byte, error) {
|
|
||||||
// Retrieve the executing code
|
|
||||||
code := self.env.State().GetCode(codeAddr)
|
|
||||||
|
|
||||||
return self.exec(code, codeAddr, caller)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Execution) exec(code, contextAddr []byte, caller ContextRef) (ret []byte, err error) {
|
|
||||||
env := self.env
|
|
||||||
evm := NewVm(env)
|
|
||||||
if env.Depth() == MaxCallDepth {
|
|
||||||
caller.ReturnGas(self.Gas, self.price)
|
|
||||||
|
|
||||||
return nil, DepthError{}
|
|
||||||
}
|
|
||||||
|
|
||||||
vsnapshot := env.State().Copy()
|
|
||||||
if len(self.address) == 0 {
|
|
||||||
// Generate a new address
|
|
||||||
nonce := env.State().GetNonce(caller.Address())
|
|
||||||
self.address = crypto.CreateAddress(caller.Address(), nonce)
|
|
||||||
env.State().SetNonce(caller.Address(), nonce+1)
|
|
||||||
}
|
|
||||||
|
|
||||||
from, to := env.State().GetStateObject(caller.Address()), env.State().GetOrNewStateObject(self.address)
|
|
||||||
err = env.Transfer(from, to, self.value)
|
|
||||||
if err != nil {
|
|
||||||
env.State().Set(vsnapshot)
|
|
||||||
|
|
||||||
caller.ReturnGas(self.Gas, self.price)
|
|
||||||
|
|
||||||
return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", self.value, from.Balance())
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshot := env.State().Copy()
|
|
||||||
start := time.Now()
|
|
||||||
ret, err = evm.Run(to, caller, code, self.value, self.Gas, self.price, self.input)
|
|
||||||
chainlogger.Debugf("vm took %v\n", time.Since(start))
|
|
||||||
if err != nil {
|
|
||||||
env.State().Set(snapshot)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Execution) Create(caller ContextRef) (ret []byte, err error, account *state.StateObject) {
|
|
||||||
ret, err = self.exec(self.input, nil, caller)
|
|
||||||
account = self.env.State().GetStateObject(self.address)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
18
vm/gas.go
Normal file
18
vm/gas.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package vm
|
||||||
|
|
||||||
|
const (
|
||||||
|
GasSha3 uint64 = 1
|
||||||
|
GasGetAccount uint64 = 1
|
||||||
|
GasStorageCreate uint64 = 1
|
||||||
|
GasStorageUpdate uint64 = 1
|
||||||
|
|
||||||
|
GasStackOp uint64 = 1
|
||||||
|
|
||||||
|
GasEcRecover uint64 = 1
|
||||||
|
GasSha256Word uint64 = 1
|
||||||
|
GasSha256Base uint64 = 1
|
||||||
|
GasRipemd160Word uint64 = 1
|
||||||
|
GasRipemd160Base uint64 = 1
|
||||||
|
GasIdentityWord uint64 = 1
|
||||||
|
GasIdentityBase uint64 = 1
|
||||||
|
)
|
90
vm/native.go
Normal file
90
vm/native.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.google.com/p/go.crypto/ripemd160"
|
||||||
|
"crypto/sha256"
|
||||||
|
"github.com/tendermint/tendermint/vm/secp256k1"
|
||||||
|
"github.com/tendermint/tendermint/vm/sha3"
|
||||||
|
|
||||||
|
. "github.com/tendermint/tendermint/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
var nativeContracts = make(map[Word]NativeContract)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
nativeContracts[Uint64ToWord(1)] = ecrecoverFunc
|
||||||
|
nativeContracts[Uint64ToWord(2)] = sha256Func
|
||||||
|
nativeContracts[Uint64ToWord(3)] = ripemd160Func
|
||||||
|
nativeContracts[Uint64ToWord(4)] = identityFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type NativeContract func(input []byte, gas *uint64) (output []byte, err error)
|
||||||
|
|
||||||
|
func ecrecoverFunc(input []byte, gas *uint64) (output []byte, err error) {
|
||||||
|
// Deduct gas
|
||||||
|
gasRequired := GasEcRecover
|
||||||
|
if *gas < gasRequired {
|
||||||
|
return nil, ErrInsufficientGas
|
||||||
|
} else {
|
||||||
|
*gas -= gasRequired
|
||||||
|
}
|
||||||
|
// Recover
|
||||||
|
hash := input[:32]
|
||||||
|
v := byte(input[32] - 27) // ignore input[33:64], v is small.
|
||||||
|
sig := append(input[64:], v)
|
||||||
|
|
||||||
|
recovered, err := secp256k1.RecoverPubkey(hash, sig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hashed := sha3.Sha3(recovered[1:])
|
||||||
|
return RightPadBytes(hashed, 32), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sha256Func(input []byte, gas *uint64) (output []byte, err error) {
|
||||||
|
// Deduct gas
|
||||||
|
gasRequired := uint64((len(input)+31)/32)*GasSha256Word + GasSha256Base
|
||||||
|
if *gas < gasRequired {
|
||||||
|
return nil, ErrInsufficientGas
|
||||||
|
} else {
|
||||||
|
*gas -= gasRequired
|
||||||
|
}
|
||||||
|
// Hash
|
||||||
|
hasher := sha256.New()
|
||||||
|
_, err = hasher.Write(input)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return hasher.Sum(nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ripemd160Func(input []byte, gas *uint64) (output []byte, err error) {
|
||||||
|
// Deduct gas
|
||||||
|
gasRequired := uint64((len(input)+31)/32)*GasRipemd160Word + GasRipemd160Base
|
||||||
|
if *gas < gasRequired {
|
||||||
|
return nil, ErrInsufficientGas
|
||||||
|
} else {
|
||||||
|
*gas -= gasRequired
|
||||||
|
}
|
||||||
|
// Hash
|
||||||
|
hasher := ripemd160.New()
|
||||||
|
_, err = hasher.Write(input)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return RightPadBytes(hasher.Sum(nil), 32), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func identityFunc(input []byte, gas *uint64) (output []byte, err error) {
|
||||||
|
// Deduct gas
|
||||||
|
gasRequired := uint64((len(input)+31)/32)*GasIdentityWord + GasIdentityBase
|
||||||
|
if *gas < gasRequired {
|
||||||
|
return nil, ErrInsufficientGas
|
||||||
|
} else {
|
||||||
|
*gas -= gasRequired
|
||||||
|
}
|
||||||
|
// Return identity
|
||||||
|
return input, nil
|
||||||
|
}
|
@ -1,74 +0,0 @@
|
|||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"code.google.com/p/go.crypto/ripemd160"
|
|
||||||
"crypto/sha256"
|
|
||||||
"math/big"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PrecompiledAccount struct {
|
|
||||||
Gas func(l int) *big.Int
|
|
||||||
fn func(in []byte) []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self PrecompiledAccount) Call(in []byte) []byte {
|
|
||||||
return self.fn(in)
|
|
||||||
}
|
|
||||||
|
|
||||||
var Precompiled = PrecompiledContracts()
|
|
||||||
|
|
||||||
// XXX Could set directly. Testing requires resetting and setting of pre compiled contracts.
|
|
||||||
func PrecompiledContracts() map[string]*PrecompiledAccount {
|
|
||||||
return map[string]*PrecompiledAccount{
|
|
||||||
// ECRECOVER
|
|
||||||
/*
|
|
||||||
string(LeftPadBytes([]byte{1}, 20)): &PrecompiledAccount{func(l int) *big.Int {
|
|
||||||
return GasEcrecover
|
|
||||||
}, ecrecoverFunc},
|
|
||||||
*/
|
|
||||||
|
|
||||||
// SHA256
|
|
||||||
string(LeftPadBytes([]byte{2}, 20)): &PrecompiledAccount{func(l int) *big.Int {
|
|
||||||
n := big.NewInt(int64(l+31) / 32)
|
|
||||||
n.Mul(n, GasSha256Word)
|
|
||||||
return n.Add(n, GasSha256Base)
|
|
||||||
}, sha256Func},
|
|
||||||
|
|
||||||
// RIPEMD160
|
|
||||||
string(LeftPadBytes([]byte{3}, 20)): &PrecompiledAccount{func(l int) *big.Int {
|
|
||||||
n := big.NewInt(int64(l+31) / 32)
|
|
||||||
n.Mul(n, GasRipemdWord)
|
|
||||||
return n.Add(n, GasRipemdBase)
|
|
||||||
}, ripemd160Func},
|
|
||||||
|
|
||||||
string(LeftPadBytes([]byte{4}, 20)): &PrecompiledAccount{func(l int) *big.Int {
|
|
||||||
n := big.NewInt(int64(l+31) / 32)
|
|
||||||
n.Mul(n, GasIdentityWord)
|
|
||||||
|
|
||||||
return n.Add(n, GasIdentityBase)
|
|
||||||
}, memCpy},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sha256Func(in []byte) []byte {
|
|
||||||
hasher := sha256.New()
|
|
||||||
n, err := hasher.Write(in)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return hasher.Sum(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ripemd160Func(in []byte) []byte {
|
|
||||||
hasher := ripemd160.New()
|
|
||||||
n, err := hasher.Write(in)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
res := hasher.Sum(nil)
|
|
||||||
return LeftPadBytes(res, 32)
|
|
||||||
}
|
|
||||||
|
|
||||||
func memCpy(in []byte) []byte {
|
|
||||||
return in
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package vm
|
|
||||||
|
|
||||||
import ()
|
|
||||||
|
|
||||||
type Receipt struct {
|
|
||||||
Index uint
|
|
||||||
Address []byte
|
|
||||||
Topics [][]byte
|
|
||||||
Data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Receipt) String() string {
|
|
||||||
return fmt.Sprintf("[A=%x T=%x D=%x]", self.Address, self.Topics, self.Data)
|
|
||||||
}
|
|
1
vm/secp256k1
Submodule
1
vm/secp256k1
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 8a939c2f861148885b43c58ce52a36882c516fd6
|
112
vm/stack.go
112
vm/stack.go
@ -1,26 +1,9 @@
|
|||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
ErrDataStackOverflow = errors.New("DataStackOverflow")
|
|
||||||
ErrDataStackUnderflow = errors.New("DataStackUnderflow")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Word [4]uint64
|
|
||||||
|
|
||||||
func Bytes2Uint64(bz []byte) uint64 {
|
|
||||||
return binary.LittleEndian.Uint64(bz)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Uint642Bytes(dest []byte, i uint64) {
|
|
||||||
binary.LittleEndian.PutUint64(dest, i)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not goroutine safe
|
// Not goroutine safe
|
||||||
type Stack struct {
|
type Stack struct {
|
||||||
data []Word
|
data []Word
|
||||||
@ -39,93 +22,88 @@ func NewStack(capacity int, gas *uint64, err *error) *Stack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) Push(d Word) error {
|
func (st *Stack) useGas(gasToUse uint64) {
|
||||||
|
if *st.gas > gasToUse {
|
||||||
|
*st.gas -= gasToUse
|
||||||
|
} else {
|
||||||
|
st.setErr(ErrInsufficientGas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *Stack) setErr(err error) {
|
||||||
|
if *st.err != nil {
|
||||||
|
*st.err = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *Stack) Push(d Word) {
|
||||||
|
st.useGas(GasStackOp)
|
||||||
if st.ptr == cap(st.data) {
|
if st.ptr == cap(st.data) {
|
||||||
return ErrDataStackOverflow
|
st.setErr(ErrDataStackOverflow)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
st.data[st.ptr] = d
|
st.data[st.ptr] = d
|
||||||
st.ptr++
|
st.ptr++
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) Push64(i uint64) error {
|
func (st *Stack) PushBytes(bz []byte) {
|
||||||
if st.ptr == cap(st.data) {
|
|
||||||
return ErrDataStackOverflow
|
|
||||||
}
|
|
||||||
st.data[st.ptr] = [4]uint64{i, 0, 0, 0}
|
|
||||||
st.ptr++
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *Stack) PushBytes(bz []byte) error {
|
|
||||||
if len(bz) != 32 {
|
if len(bz) != 32 {
|
||||||
panic("Invalid bytes size: expected 32")
|
panic("Invalid bytes size: expected 32")
|
||||||
}
|
}
|
||||||
if st.ptr == cap(st.data) {
|
st.Push(BytesToWord(bz))
|
||||||
return ErrDataStackOverflow
|
|
||||||
}
|
|
||||||
st.data[st.ptr] = [4]uint64{
|
|
||||||
Bytes2Uint64(bz[0:8]),
|
|
||||||
Bytes2Uint64(bz[8:16]),
|
|
||||||
Bytes2Uint64(bz[16:24]),
|
|
||||||
Bytes2Uint64(bz[24:32]),
|
|
||||||
}
|
|
||||||
st.ptr++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) Pop() (Word, error) {
|
func (st *Stack) Push64(i uint64) {
|
||||||
if st.ptr == 0 {
|
st.Push(Uint64ToWord(i))
|
||||||
return Zero, ErrDataStackUnderflow
|
|
||||||
}
|
|
||||||
st.ptr--
|
|
||||||
return st.data[st.ptr], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) Pop64() (uint64, error) {
|
func (st *Stack) Pop() Word {
|
||||||
|
st.useGas(GasStackOp)
|
||||||
if st.ptr == 0 {
|
if st.ptr == 0 {
|
||||||
return Zero, ErrDataStackUnderflow
|
st.setErr(ErrDataStackUnderflow)
|
||||||
|
return Zero
|
||||||
}
|
}
|
||||||
st.ptr--
|
st.ptr--
|
||||||
return st.data[st.ptr][0], nil
|
return st.data[st.ptr]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) PopBytes() ([]byte, error) {
|
func (st *Stack) PopBytes() []byte {
|
||||||
if st.ptr == 0 {
|
return st.Pop().Bytes()
|
||||||
return Zero, ErrDataStackUnderflow
|
}
|
||||||
}
|
|
||||||
st.ptr--
|
func (st *Stack) Pop64() uint64 {
|
||||||
res := make([]byte, 32)
|
return GetUint64(st.Pop())
|
||||||
copy(res[0:8], Uint642Bytes(st.data[st.ptr][0]))
|
|
||||||
copy(res[8:16], Uint642Bytes(st.data[st.ptr][1]))
|
|
||||||
copy(res[16:24], Uint642Bytes(st.data[st.ptr][2]))
|
|
||||||
copy(res[24:32], Uint642Bytes(st.data[st.ptr][3]))
|
|
||||||
return res, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) Len() int {
|
func (st *Stack) Len() int {
|
||||||
return st.ptr
|
return st.ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) Swap(n int) error {
|
func (st *Stack) Swap(n int) {
|
||||||
|
st.useGas(GasStackOp)
|
||||||
if st.ptr < n {
|
if st.ptr < n {
|
||||||
return ErrDataStackUnderflow
|
st.setErr(ErrDataStackUnderflow)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
st.data[st.ptr-n], st.data[st.ptr-1] = st.data[st.ptr-1], st.data[st.ptr-n]
|
st.data[st.ptr-n], st.data[st.ptr-1] = st.data[st.ptr-1], st.data[st.ptr-n]
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) Dup(n int) {
|
func (st *Stack) Dup(n int) {
|
||||||
|
st.useGas(GasStackOp)
|
||||||
|
if st.ptr < n {
|
||||||
|
st.setErr(ErrDataStackUnderflow)
|
||||||
|
return
|
||||||
|
}
|
||||||
st.Push(st.data[st.ptr-n])
|
st.Push(st.data[st.ptr-n])
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Not an opcode, costs no gas.
|
||||||
func (st *Stack) Peek() Word {
|
func (st *Stack) Peek() Word {
|
||||||
return st.data[st.ptr-1]
|
return st.data[st.ptr-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) Require(n int) error {
|
|
||||||
if st.ptr < n {
|
|
||||||
return ErrDataStackUnderflow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *Stack) Print() {
|
func (st *Stack) Print() {
|
||||||
fmt.Println("### stack ###")
|
fmt.Println("### stack ###")
|
||||||
if st.ptr > 0 {
|
if st.ptr > 0 {
|
||||||
|
39
vm/types.go
Normal file
39
vm/types.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package vm
|
||||||
|
|
||||||
|
import ()
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultDataStackCapacity = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
type Account struct {
|
||||||
|
Address Word
|
||||||
|
Balance uint64
|
||||||
|
Code []byte
|
||||||
|
Nonce uint64
|
||||||
|
StateRoot Word
|
||||||
|
}
|
||||||
|
|
||||||
|
type Log struct {
|
||||||
|
Address Word
|
||||||
|
Topics []Word
|
||||||
|
Data []byte
|
||||||
|
Height uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppState interface {
|
||||||
|
|
||||||
|
// Accounts
|
||||||
|
GetAccount(Word) (*Account, error)
|
||||||
|
UpdateAccount(*Account) error
|
||||||
|
DeleteAccount(*Account) error
|
||||||
|
CreateAccount(Word, uint64) (*Account, error)
|
||||||
|
|
||||||
|
// Storage
|
||||||
|
GetStorage(Word, Word) (Word, error)
|
||||||
|
SetStorage(Word, Word, Word) (bool, error)
|
||||||
|
RemoveStorage(Word, Word) error
|
||||||
|
|
||||||
|
// Logs
|
||||||
|
AddLog(*Log)
|
||||||
|
}
|
627
vm/vm.go
627
vm/vm.go
@ -1,84 +1,96 @@
|
|||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
|
|
||||||
sm "github.com/tendermint/tendermint/state"
|
. "github.com/tendermint/tendermint/common"
|
||||||
"github.com/tendermint/tendermint/vm/sha3"
|
"github.com/tendermint/tendermint/vm/sha3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Vm struct {
|
var (
|
||||||
VMEnvironment
|
ErrInsufficientBalance = errors.New("Insufficient balance")
|
||||||
|
ErrInvalidJumpDest = errors.New("Invalid jump dest")
|
||||||
|
ErrInsufficientGas = errors.New("Insuffient gas")
|
||||||
|
ErrMemoryOutOfBounds = errors.New("Memory out of bounds")
|
||||||
|
ErrCodeOutOfBounds = errors.New("Code out of bounds")
|
||||||
|
ErrInputOutOfBounds = errors.New("Input out of bounds")
|
||||||
|
ErrCallStackOverflow = errors.New("Call stack overflow")
|
||||||
|
ErrCallStackUnderflow = errors.New("Call stack underflow")
|
||||||
|
ErrDataStackOverflow = errors.New("Data stack overflow")
|
||||||
|
ErrDataStackUnderflow = errors.New("Data stack underflow")
|
||||||
|
ErrInvalidContract = errors.New("Invalid contract")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dataStackCapacity = 1024
|
||||||
|
callStackCapacity = 100 // TODO ensure usage.
|
||||||
|
memoryCapacity = 1024 * 1024 // 1 MB
|
||||||
|
)
|
||||||
|
|
||||||
|
type VM struct {
|
||||||
|
appState AppState
|
||||||
|
params VMParams
|
||||||
|
|
||||||
|
callDepth int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVM(appState AppState, params VMParams) *Vm {
|
type VMParams struct {
|
||||||
vmEnv := NewVMEnvironment(appState, params)
|
BlockHeight uint64
|
||||||
|
BlockHash Word
|
||||||
|
BlockTime int64
|
||||||
|
GasLimit uint64
|
||||||
|
GasPrice uint64
|
||||||
|
CallStackLimit uint64
|
||||||
|
Origin Word
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVM(appState AppState, params VMParams) *VM {
|
||||||
return &VM{
|
return &VM{
|
||||||
VMEnvironment: vmEnv,
|
appState: appState,
|
||||||
|
params: params,
|
||||||
|
callDepth: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// feeLimit: the maximum the caller is willing to pay for fees.
|
/*
|
||||||
// gasLimit: the maximum gas that will be run.
|
// When running a transaction, the caller the pays for the fees.
|
||||||
func (vm *Vm) RunTransaction(caller, target *Account, feeLimit, gasLimit, value uint64, input []byte) (output []byte, err error) {
|
|
||||||
|
|
||||||
if len(target.Code) == 0 {
|
|
||||||
panic("RunTransaction() requires target with code")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the gasLimit vs feeLimit
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
// Check caller's account balance vs feeLimit and value
|
// Check caller's account balance vs feeLimit and value
|
||||||
if caller.Balance < (feeLimit + value) {
|
if caller.Balance < (feeLimit + value) {
|
||||||
return nil, ErrInsufficientAccountBalance
|
return nil, ErrInsufficientBalance
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deduct balance from caller.
|
|
||||||
caller.Balance -= (feeLimit + value)
|
caller.Balance -= (feeLimit + value)
|
||||||
|
|
||||||
vm.SetupTransaction(caller, target, gasLimit, value, input)
|
*/
|
||||||
|
|
||||||
fmt.Printf("(%d) (%X) %X (code=%d) gas: %v (d) %X\n", vm.CallStackDepth(), caller.Address[:4], target.Address, len(target.Code), gasLimit, input)
|
// 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) {
|
||||||
|
|
||||||
/*
|
if len(callee.Code) == 0 {
|
||||||
if p := Precompiled[string(me.Address())]; p != nil {
|
panic("Call() requires callee with code")
|
||||||
return vm.RunPrecompiled(p, callData, context)
|
}
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
//-----------------------------------
|
fmt.Printf("(%d) (%X) %X (code=%d) gas: %v (d) %X\n", vm.callDepth, caller.Address[:4], callee.Address, len(callee.Code), *gas, input)
|
||||||
|
|
||||||
// By the time we're here, the related VMCall context is already appended onto VMEnvironment.callStack
|
|
||||||
var (
|
var (
|
||||||
code = target.Code
|
|
||||||
pc uint64 = 0
|
pc uint64 = 0
|
||||||
gas uint64 = call.gasLimit
|
stack = NewStack(dataStackCapacity, gas, &err)
|
||||||
err error = nil
|
memory = make([]byte, memoryCapacity)
|
||||||
stack = NewStack(defaultDataStackCapacity, &gas, &err)
|
ok = false // convenience
|
||||||
memory = NewMemory(&gas, &err)
|
|
||||||
|
|
||||||
// volatile, convenience
|
|
||||||
ok = false
|
|
||||||
|
|
||||||
// TODO review this code.
|
|
||||||
jump = func(from, to uint64) error {
|
|
||||||
dest := CodeGetOp(code, to)
|
|
||||||
if dest != JUMPDEST {
|
|
||||||
return ErrInvalidJumpDest
|
|
||||||
}
|
|
||||||
pc = to
|
|
||||||
fmt.Printf(" ~> %v\n", to)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var op = CodeGetOp(code, pc)
|
var op = codeGetOp(code, pc)
|
||||||
fmt.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.Len())
|
fmt.Printf("(pc) %-3d (op) %-14s (st) %-4d ", pc, op.String(), stack.Len())
|
||||||
|
|
||||||
switch op {
|
switch op {
|
||||||
|
|
||||||
|
case STOP: // 0x00
|
||||||
|
return nil, nil
|
||||||
|
|
||||||
case ADD: // 0x01
|
case ADD: // 0x01
|
||||||
x, y := stack.Pop64(), stack.Pop64()
|
x, y := stack.Pop64(), stack.Pop64()
|
||||||
stack.Push64(x + y)
|
stack.Push64(x + y)
|
||||||
@ -241,345 +253,418 @@ func (vm *Vm) RunTransaction(caller, target *Account, feeLimit, gasLimit, value
|
|||||||
|
|
||||||
case BYTE: // 0x1A
|
case BYTE: // 0x1A
|
||||||
idx, val := stack.Pop64(), stack.Pop()
|
idx, val := stack.Pop64(), stack.Pop()
|
||||||
res := 0
|
res := byte(0)
|
||||||
if idx < 32 {
|
if idx < 32 {
|
||||||
res = Uint642Bytes(val[idx/8])[idx%8]
|
res = val[idx]
|
||||||
}
|
}
|
||||||
stack.Push64(res)
|
stack.Push64(uint64(res))
|
||||||
fmt.Printf(" => 0x%X", res)
|
fmt.Printf(" => 0x%X\n", res)
|
||||||
|
|
||||||
case SHA3: // 0x20
|
case SHA3: // 0x20
|
||||||
if gas, ok = useGas(gas, GasSha3); !ok {
|
if ok = useGas(gas, GasSha3); !ok {
|
||||||
return ErrInsufficientGas
|
return nil, firstErr(err, ErrInsufficientGas)
|
||||||
}
|
}
|
||||||
offset, size := stack.Pop64(), stack.Pop64()
|
offset, size := stack.Pop64(), stack.Pop64()
|
||||||
data := sha3.Sha3(memory.Get(offset, size))
|
data, ok := subslice(memory, offset, size)
|
||||||
|
if !ok {
|
||||||
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
||||||
|
}
|
||||||
|
data = sha3.Sha3(data)
|
||||||
stack.PushBytes(data)
|
stack.PushBytes(data)
|
||||||
fmt.Printf(" => (%v) %X", size, data)
|
fmt.Printf(" => (%v) %X\n", size, data)
|
||||||
|
|
||||||
case ADDRESS: // 0x30
|
case ADDRESS: // 0x30
|
||||||
stack.PushBytes(RightPadBytes(context.Address(), 32))
|
stack.Push(callee.Address)
|
||||||
fmt.Printf(" => %X", RightPadBytes(context.Address(), 32))
|
fmt.Printf(" => %X\n", callee.Address)
|
||||||
|
|
||||||
case BALANCE: // 0x31
|
case BALANCE: // 0x31
|
||||||
addr := stack.PopBytes()
|
addr := stack.Pop()
|
||||||
if gas, ok = useGas(gas, GasGetAccount); !ok {
|
if ok = useGas(gas, GasGetAccount); !ok {
|
||||||
return ErrInsufficientGas
|
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
|
||||||
}
|
}
|
||||||
account := vm.GetAccount(addr) // TODO ensure that 20byte lengths are supported.
|
|
||||||
balance := account.Balance
|
balance := account.Balance
|
||||||
stack.Push64(balance)
|
stack.Push64(balance)
|
||||||
fmt.Printf(" => %v (%X)", balance, addr)
|
fmt.Printf(" => %v (%X)\n", balance, addr)
|
||||||
|
|
||||||
case ORIGIN: // 0x32
|
case ORIGIN: // 0x32
|
||||||
origin := vm.Origin()
|
origin := vm.params.Origin
|
||||||
stack.PushBytes(origin)
|
stack.Push(origin)
|
||||||
fmt.Printf(" => %X", origin)
|
fmt.Printf(" => %X\n", origin)
|
||||||
|
|
||||||
case CALLER: // 0x33
|
case CALLER: // 0x33
|
||||||
caller := vm.lastCall.caller
|
stack.Push(caller.Address)
|
||||||
stack.PushBytes(caller.Address)
|
fmt.Printf(" => %X\n", caller.Address)
|
||||||
fmt.Printf(" => %X", caller.Address)
|
|
||||||
|
|
||||||
case CALLVALUE: // 0x34
|
case CALLVALUE: // 0x34
|
||||||
stack.Push64(value)
|
stack.Push64(value)
|
||||||
fmt.Printf(" => %v", value)
|
fmt.Printf(" => %v\n", value)
|
||||||
|
|
||||||
case CALLDATALOAD: // 0x35
|
case CALLDATALOAD: // 0x35
|
||||||
offset := stack.Pop64()
|
offset := stack.Pop64()
|
||||||
data, _ := subslice(input, offset, 32)
|
data, ok := subslice(input, offset, 32)
|
||||||
stack.PushBytes(RightPadBytes(data), 32)
|
if !ok {
|
||||||
fmt.Printf(" => 0x%X", data)
|
return nil, firstErr(err, ErrInputOutOfBounds)
|
||||||
|
}
|
||||||
|
stack.Push(RightPadWord(data))
|
||||||
|
fmt.Printf(" => 0x%X\n", data)
|
||||||
|
|
||||||
case CALLDATASIZE: // 0x36
|
case CALLDATASIZE: // 0x36
|
||||||
stack.Push64(uint64(len(callData)))
|
stack.Push64(uint64(len(input)))
|
||||||
fmt.Printf(" => %d", len(callData))
|
fmt.Printf(" => %d\n", len(input))
|
||||||
|
|
||||||
case CALLDATACOPY: // 0x37
|
case CALLDATACOPY: // 0x37
|
||||||
memOff := stack.Pop64()
|
memOff := stack.Pop64()
|
||||||
inputOff := stack.Pop64()
|
inputOff := stack.Pop64()
|
||||||
length := stack.Pop64()
|
length := stack.Pop64()
|
||||||
data, ok := subslice(input, inputOff, length)
|
data, ok := subslice(input, inputOff, length)
|
||||||
if ok {
|
if !ok {
|
||||||
memory.Set(memOff, length, data)
|
return nil, firstErr(err, ErrInputOutOfBounds)
|
||||||
}
|
}
|
||||||
fmt.Printf(" => [%v, %v, %v] %X", memOff, inputOff, length, data)
|
dest, ok := subslice(memory, memOff, length)
|
||||||
|
if !ok {
|
||||||
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
||||||
|
}
|
||||||
|
copy(dest, data)
|
||||||
|
fmt.Printf(" => [%v, %v, %v] %X\n", memOff, inputOff, length, data)
|
||||||
|
|
||||||
case CODESIZE: // 0x38
|
case CODESIZE: // 0x38
|
||||||
l := uint64(len(code))
|
l := uint64(len(code))
|
||||||
stack.Push64(l)
|
stack.Push64(l)
|
||||||
fmt.Printf(" => %d", l)
|
fmt.Printf(" => %d\n", l)
|
||||||
|
|
||||||
case CODECOPY: // 0x39
|
case CODECOPY: // 0x39
|
||||||
memOff := stack.Pop64()
|
memOff := stack.Pop64()
|
||||||
codeOff := stack.Pop64()
|
codeOff := stack.Pop64()
|
||||||
length := stack.Pop64()
|
length := stack.Pop64()
|
||||||
data, ok := subslice(code, codeOff, length)
|
data, ok := subslice(code, codeOff, length)
|
||||||
if ok {
|
if !ok {
|
||||||
memory.Set(memOff, length, data)
|
return nil, firstErr(err, ErrCodeOutOfBounds)
|
||||||
}
|
}
|
||||||
fmt.Printf(" => [%v, %v, %v] %X", memOff, codeOff, length, data)
|
dest, ok := subslice(memory, memOff, length)
|
||||||
|
if !ok {
|
||||||
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
||||||
|
}
|
||||||
|
copy(dest, data)
|
||||||
|
fmt.Printf(" => [%v, %v, %v] %X\n", memOff, codeOff, length, data)
|
||||||
|
|
||||||
case GASPRICE: // 0x3A
|
case GASPRICE: // 0x3A
|
||||||
stack.Push64(vm.params.GasPrice)
|
stack.Push64(vm.params.GasPrice)
|
||||||
fmt.Printf(" => %X", vm.params.GasPrice)
|
fmt.Printf(" => %X\n", vm.params.GasPrice)
|
||||||
|
|
||||||
case EXTCODESIZE: // 0x3B
|
case EXTCODESIZE: // 0x3B
|
||||||
addr := stack.PopBytes()[:20]
|
addr := stack.Pop()
|
||||||
account := vm.GetAccount(addr)
|
if ok = useGas(gas, GasGetAccount); !ok {
|
||||||
|
return nil, firstErr(err, ErrInsufficientGas)
|
||||||
|
}
|
||||||
|
account, err_ := vm.appState.GetAccount(addr)
|
||||||
|
if err = firstErr(err, err_); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
code := account.Code
|
code := account.Code
|
||||||
l := uint64(len(code))
|
l := uint64(len(code))
|
||||||
stack.Push64(l)
|
stack.Push64(l)
|
||||||
fmt.Printf(" => %d", l)
|
fmt.Printf(" => %d\n", l)
|
||||||
|
|
||||||
case EXTCODECOPY: // 0x3C
|
case EXTCODECOPY: // 0x3C
|
||||||
addr := stack.PopBytes()[:20]
|
addr := stack.Pop()
|
||||||
account := vm.GetAccount(addr)
|
if ok = useGas(gas, GasGetAccount); !ok {
|
||||||
|
return nil, firstErr(err, ErrInsufficientGas)
|
||||||
|
}
|
||||||
|
account, err_ := vm.appState.GetAccount(addr)
|
||||||
|
if err = firstErr(err, err_); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
code := account.Code
|
code := account.Code
|
||||||
memOff := stack.Pop64()
|
memOff := stack.Pop64()
|
||||||
codeOff := stack.Pop64()
|
codeOff := stack.Pop64()
|
||||||
length := stack.Pop64()
|
length := stack.Pop64()
|
||||||
data, ok := subslice(code, codeOff, length)
|
data, ok := subslice(code, codeOff, length)
|
||||||
if ok {
|
if !ok {
|
||||||
memory.Set(memOff, length, data)
|
return nil, ErrCodeOutOfBounds
|
||||||
}
|
}
|
||||||
fmt.Printf(" => [%v, %v, %v] %X", memOff, codeOff, length, data)
|
dest, ok := subslice(memory, memOff, length)
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrMemoryOutOfBounds
|
||||||
|
}
|
||||||
|
copy(dest, data)
|
||||||
|
fmt.Printf(" => [%v, %v, %v] %X\n", memOff, codeOff, length, data)
|
||||||
|
|
||||||
case BLOCKHASH: // 0x40
|
case BLOCKHASH: // 0x40
|
||||||
/*
|
stack.Push(Zero)
|
||||||
num := stack.pop()
|
fmt.Printf(" => 0x%X (NOT SUPPORTED)\n", stack.Peek().Bytes())
|
||||||
n := new(big.Int).Sub(vm.env.BlockHeight(), Big257)
|
|
||||||
if num.Cmp(n) > 0 && num.Cmp(vm.env.BlockHeight()) < 0 {
|
|
||||||
stack.push(Bytes2Big(vm.env.GetBlockHash(num.Uint64())))
|
|
||||||
} else {
|
|
||||||
stack.push(Big0)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
stack.Push([4]Word{0, 0, 0, 0})
|
|
||||||
fmt.Printf(" => 0x%X (NOT SUPPORTED)", stack.Peek().Bytes())
|
|
||||||
|
|
||||||
case COINBASE: // 0x41
|
case COINBASE: // 0x41
|
||||||
stack.Push([4]Word{0, 0, 0, 0})
|
stack.Push(Zero)
|
||||||
fmt.Printf(" => 0x%X (NOT SUPPORTED)", stack.Peek().Bytes())
|
fmt.Printf(" => 0x%X (NOT SUPPORTED)\n", stack.Peek().Bytes())
|
||||||
|
|
||||||
case TIMESTAMP: // 0x42
|
case TIMESTAMP: // 0x42
|
||||||
time := vm.params.BlockTime
|
time := vm.params.BlockTime
|
||||||
stack.Push64(uint64(time))
|
stack.Push64(uint64(time))
|
||||||
fmt.Printf(" => 0x%X", time)
|
fmt.Printf(" => 0x%X\n", time)
|
||||||
|
|
||||||
case BLOCKHEIGHT: // 0x43
|
case BLOCKHEIGHT: // 0x43
|
||||||
number := uint64(vm.params.BlockHeight)
|
number := uint64(vm.params.BlockHeight)
|
||||||
stack.Push64(number)
|
stack.Push64(number)
|
||||||
fmt.Printf(" => 0x%X", number)
|
fmt.Printf(" => 0x%X\n", number)
|
||||||
|
|
||||||
case GASLIMIT: // 0x45
|
case GASLIMIT: // 0x45
|
||||||
stack.Push64(vm.params.GasLimit)
|
stack.Push64(vm.params.GasLimit)
|
||||||
fmt.Printf(" => %v", vm.params.GasLimit)
|
fmt.Printf(" => %v\n", vm.params.GasLimit)
|
||||||
|
|
||||||
case POP: // 0x50
|
case POP: // 0x50
|
||||||
stack.Pop()
|
stack.Pop()
|
||||||
fmt.Printf(" => %v", vm.params.GasLimit)
|
fmt.Printf(" => %v\n", vm.params.GasLimit)
|
||||||
|
|
||||||
case MLOAD: // 0x51
|
case MLOAD: // 0x51
|
||||||
offset := stack.Pop64()
|
offset := stack.Pop64()
|
||||||
data, _ := subslice(input, offset, 32)
|
data, ok := subslice(memory, offset, 32)
|
||||||
stack.PushBytes(RightPadBytes(data), 32)
|
if !ok {
|
||||||
fmt.Printf(" => 0x%X", data)
|
return nil, ErrMemoryOutOfBounds
|
||||||
|
}
|
||||||
|
stack.Push(RightPadWord(data))
|
||||||
|
fmt.Printf(" => 0x%X\n", data)
|
||||||
|
|
||||||
offset := stack.pop()
|
case MSTORE: // 0x52
|
||||||
val := Bytes2Big(mem.Get(offset.Int64(), 32))
|
offset, data := stack.Pop64(), stack.Pop()
|
||||||
stack.push(val)
|
dest, ok := subslice(memory, offset, 32)
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrMemoryOutOfBounds
|
||||||
|
}
|
||||||
|
copy(dest, data[:])
|
||||||
|
fmt.Printf(" => 0x%X\n", data)
|
||||||
|
|
||||||
fmt.Printf(" => 0x%X", val.Bytes())
|
case MSTORE8: // 0x53
|
||||||
case MSTORE: // Store the value at stack top-1 in to memory at location stack top
|
offset, val := stack.Pop64(), byte(stack.Pop64()&0xFF)
|
||||||
// pop value of the stack
|
if len(memory) <= int(offset) {
|
||||||
mStart, val := stack.pop(), stack.pop()
|
return nil, ErrMemoryOutOfBounds
|
||||||
mem.Set(mStart.Uint64(), 32, Big2Bytes(val, 256))
|
}
|
||||||
|
memory[offset] = val
|
||||||
|
fmt.Printf(" => [%v] 0x%X\n", offset, val)
|
||||||
|
|
||||||
fmt.Printf(" => 0x%X", val)
|
case SLOAD: // 0x54
|
||||||
case MSTORE8:
|
loc := stack.Pop()
|
||||||
off, val := stack.pop(), stack.pop()
|
data, _ := vm.appState.GetStorage(callee.Address, loc)
|
||||||
|
stack.Push(data)
|
||||||
|
fmt.Printf(" {0x%X : 0x%X}\n", loc, data)
|
||||||
|
|
||||||
mem.store[off.Int64()] = byte(val.Int64() & 0xff)
|
case SSTORE: // 0x55
|
||||||
|
loc, data := stack.Pop(), stack.Pop()
|
||||||
fmt.Printf(" => [%v] 0x%X", off, val)
|
updated, err_ := vm.appState.SetStorage(callee.Address, loc, data)
|
||||||
case SLOAD:
|
if err = firstErr(err, err_); err != nil {
|
||||||
loc := stack.pop()
|
return nil, err
|
||||||
val := Bytes2Big(state.GetState(context.Address(), loc.Bytes()))
|
}
|
||||||
stack.push(val)
|
if updated {
|
||||||
|
useGas(gas, GasStorageUpdate)
|
||||||
fmt.Printf(" {0x%X : 0x%X}", loc.Bytes(), val.Bytes())
|
} else {
|
||||||
case SSTORE:
|
useGas(gas, GasStorageCreate)
|
||||||
loc, val := stack.pop(), stack.pop()
|
}
|
||||||
state.SetState(context.Address(), loc.Bytes(), val)
|
fmt.Printf(" {0x%X : 0x%X}\n", loc, data)
|
||||||
|
|
||||||
fmt.Printf(" {0x%X : 0x%X}", loc.Bytes(), val.Bytes())
|
|
||||||
case JUMP:
|
|
||||||
jump(pc, stack.pop())
|
|
||||||
|
|
||||||
|
case JUMP: // 0x56
|
||||||
|
err = jump(code, stack.Pop64())
|
||||||
continue
|
continue
|
||||||
case JUMPI:
|
|
||||||
pos, cond := stack.pop(), stack.pop()
|
|
||||||
|
|
||||||
if cond.Cmp(BigTrue) >= 0 {
|
|
||||||
jump(pc, pos)
|
|
||||||
|
|
||||||
|
case JUMPI: // 0x57
|
||||||
|
pos, cond := stack.Pop64(), stack.Pop64()
|
||||||
|
if cond >= 1 {
|
||||||
|
err = jump(code, pos)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
fmt.Printf(" ~> false\n")
|
||||||
|
|
||||||
fmt.Printf(" ~> false")
|
case PC: // 0x58
|
||||||
|
stack.Push64(pc)
|
||||||
|
|
||||||
case JUMPDEST:
|
case MSIZE: // 0x59
|
||||||
case PC:
|
stack.Push64(uint64(len(memory)))
|
||||||
stack.push(Big(int64(pc)))
|
|
||||||
case MSIZE:
|
|
||||||
stack.push(Big(int64(mem.Len())))
|
|
||||||
case GAS:
|
|
||||||
stack.push(context.Gas)
|
|
||||||
|
|
||||||
fmt.Printf(" => %X", context.Gas)
|
case GAS: // 0x5A
|
||||||
|
stack.Push64(*gas)
|
||||||
|
fmt.Printf(" => %X\n", *gas)
|
||||||
|
|
||||||
|
case JUMPDEST: // 0x5B
|
||||||
|
// Do nothing
|
||||||
|
|
||||||
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
|
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
|
||||||
a := uint64(op - PUSH1 + 1)
|
a := uint64(op - PUSH1 + 1)
|
||||||
byts := context.GetRangeValue(pc+1, a)
|
codeSegment, ok := subslice(code, pc+1, a)
|
||||||
// push value to stack
|
if !ok {
|
||||||
stack.push(Bytes2Big(byts))
|
return nil, firstErr(err, ErrCodeOutOfBounds)
|
||||||
|
}
|
||||||
|
res := RightPadWord(codeSegment)
|
||||||
|
stack.Push(res)
|
||||||
pc += a
|
pc += a
|
||||||
|
fmt.Printf(" => 0x%X\n", res)
|
||||||
fmt.Printf(" => 0x%X", byts)
|
|
||||||
|
|
||||||
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
|
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
|
||||||
n := int(op - DUP1 + 1)
|
n := int(op - DUP1 + 1)
|
||||||
stack.dup(n)
|
stack.Dup(n)
|
||||||
|
fmt.Printf(" => [%d] 0x%X\n", n, stack.Peek().Bytes())
|
||||||
|
|
||||||
fmt.Printf(" => [%d] 0x%X", n, stack.peek().Bytes())
|
|
||||||
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
|
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
|
||||||
n := int(op - SWAP1 + 2)
|
n := int(op - SWAP1 + 2)
|
||||||
stack.swap(n)
|
stack.Swap(n)
|
||||||
|
fmt.Printf(" => [%d]\n", n)
|
||||||
|
|
||||||
fmt.Printf(" => [%d]", n)
|
|
||||||
case LOG0, LOG1, LOG2, LOG3, LOG4:
|
case LOG0, LOG1, LOG2, LOG3, LOG4:
|
||||||
n := int(op - LOG0)
|
n := int(op - LOG0)
|
||||||
topics := make([][]byte, n)
|
topics := make([]Word, n)
|
||||||
mStart, mSize := stack.pop(), stack.pop()
|
offset, size := stack.Pop64(), stack.Pop64()
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
topics[i] = LeftPadBytes(stack.pop().Bytes(), 32)
|
topics[i] = stack.Pop()
|
||||||
|
}
|
||||||
|
data, ok := subslice(memory, offset, size)
|
||||||
|
if !ok {
|
||||||
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
||||||
|
}
|
||||||
|
log := &Log{
|
||||||
|
callee.Address,
|
||||||
|
topics,
|
||||||
|
data,
|
||||||
|
vm.params.BlockHeight,
|
||||||
|
}
|
||||||
|
vm.appState.AddLog(log)
|
||||||
|
fmt.Printf(" => %v\n", log)
|
||||||
|
|
||||||
|
case CREATE: // 0xF0
|
||||||
|
value := stack.Pop64()
|
||||||
|
offset, size := stack.Pop64(), stack.Pop64()
|
||||||
|
input, ok := subslice(memory, offset, size)
|
||||||
|
if !ok {
|
||||||
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
data := mem.Get(mStart.Int64(), mSize.Int64())
|
// Check balance
|
||||||
log := &Log{context.Address(), topics, data, vm.env.BlockHeight().Uint64()}
|
if caller.Balance < value {
|
||||||
vm.env.AddLog(log)
|
return nil, firstErr(err, ErrInsufficientBalance)
|
||||||
|
|
||||||
fmt.Printf(" => %v", log)
|
|
||||||
|
|
||||||
// 0x60 range
|
|
||||||
case CREATE:
|
|
||||||
|
|
||||||
var (
|
|
||||||
value = stack.pop()
|
|
||||||
offset, size = stack.pop(), stack.pop()
|
|
||||||
input = mem.Get(offset.Int64(), size.Int64())
|
|
||||||
gas = new(big.Int).Set(context.Gas)
|
|
||||||
addr []byte
|
|
||||||
)
|
|
||||||
vm.Endl()
|
|
||||||
|
|
||||||
context.UseGas(context.Gas)
|
|
||||||
ret, suberr, ref := Create(vm, context, nil, input, gas, price, value)
|
|
||||||
if suberr != nil {
|
|
||||||
stack.push(BigFalse)
|
|
||||||
|
|
||||||
fmt.Printf(" (*) 0x0 %v", suberr)
|
|
||||||
} else {
|
} else {
|
||||||
|
caller.Balance -= value
|
||||||
// gas < len(ret) * CreateDataGas == NO_CODE
|
|
||||||
dataGas := Big(int64(len(ret)))
|
|
||||||
dataGas.Mul(dataGas, GasCreateByte)
|
|
||||||
if context.UseGas(dataGas) {
|
|
||||||
ref.SetCode(ret)
|
|
||||||
}
|
|
||||||
addr = ref.Address()
|
|
||||||
|
|
||||||
stack.push(Bytes2Big(addr))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case CALL, CALLCODE:
|
// Create a new address
|
||||||
gas := stack.pop()
|
nonce := caller.Nonce
|
||||||
// pop gas and value of the stack.
|
addr := createAddress(caller.Address, nonce)
|
||||||
addr, value := stack.pop(), stack.pop()
|
caller.Nonce += 1
|
||||||
value = U256(value)
|
|
||||||
// pop input size and offset
|
|
||||||
inOffset, inSize := stack.pop(), stack.pop()
|
|
||||||
// pop return size and offset
|
|
||||||
retOffset, retSize := stack.pop(), stack.pop()
|
|
||||||
|
|
||||||
address := addr.Bytes()
|
// TODO charge for gas to create account _ the code length * GasCreateByte
|
||||||
fmt.Printf(" => %X", address).Endl()
|
|
||||||
|
newAccount, err := vm.appState.CreateAccount(addr, value)
|
||||||
|
if err != nil {
|
||||||
|
stack.Push64(0)
|
||||||
|
fmt.Printf(" (*) 0x0 %v\n", err)
|
||||||
|
} else {
|
||||||
|
// Run the input to get the contract code.
|
||||||
|
// The code as well as the input to the code are the same.
|
||||||
|
ret, err_ := vm.Call(callee, newAccount, input, input, value, gas)
|
||||||
|
if err_ != nil {
|
||||||
|
caller.Balance += value // Return the balance
|
||||||
|
stack.Push64(0)
|
||||||
|
} else {
|
||||||
|
newAccount.Code = ret // Set the code
|
||||||
|
stack.Push(newAccount.Address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case CALL, CALLCODE: // 0xF1, 0xF2
|
||||||
|
gasLimit := stack.Pop64()
|
||||||
|
addr, value := stack.Pop(), stack.Pop64()
|
||||||
|
inOffset, inSize := stack.Pop64(), stack.Pop64() // inputs
|
||||||
|
retOffset, retSize := stack.Pop64(), stack.Pop64() // outputs
|
||||||
|
fmt.Printf(" => %X\n", addr)
|
||||||
|
|
||||||
// Get the arguments from the memory
|
// Get the arguments from the memory
|
||||||
args := mem.Get(inOffset.Int64(), inSize.Int64())
|
args, ok := subslice(memory, inOffset, inSize)
|
||||||
|
if !ok {
|
||||||
if len(value.Bytes()) > 0 {
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
||||||
gas.Add(gas, GasStipend)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// Ensure that gasLimit is reasonable
|
||||||
ret []byte
|
if *gas < gasLimit {
|
||||||
err error
|
return nil, firstErr(err, ErrInsufficientGas)
|
||||||
)
|
|
||||||
if op == CALLCODE {
|
|
||||||
ret, err = CallCode(env, context, address, args, gas, price, value)
|
|
||||||
} else {
|
} else {
|
||||||
ret, err = Call(env, context, address, args, gas, price, value)
|
*gas -= gasLimit
|
||||||
|
// NOTE: we will return any used gas later.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Begin execution
|
||||||
|
var ret []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// If addr is in nativeContracts
|
||||||
|
if nativeContract := nativeContracts[addr]; nativeContract != nil {
|
||||||
|
ret, err = nativeContract(args, &gasLimit)
|
||||||
|
} else {
|
||||||
|
if ok = useGas(gas, GasGetAccount); !ok {
|
||||||
|
return nil, firstErr(err, ErrInsufficientGas)
|
||||||
|
}
|
||||||
|
account, err_ := vm.appState.GetAccount(addr)
|
||||||
|
if err = firstErr(err, err_); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if op == CALLCODE {
|
||||||
|
ret, err = vm.Call(callee, callee, account.Code, args, value, gas)
|
||||||
|
} else {
|
||||||
|
ret, err = vm.Call(callee, account, account.Code, args, value, gas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push result
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stack.push(BigFalse)
|
stack.Push(Zero)
|
||||||
|
|
||||||
fmt.Printf("%v").Endl()
|
|
||||||
} else {
|
} else {
|
||||||
stack.push(BigTrue)
|
stack.Push(One)
|
||||||
|
dest, ok := subslice(memory, retOffset, retSize)
|
||||||
mem.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
if !ok {
|
||||||
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
||||||
|
}
|
||||||
|
copy(dest, ret)
|
||||||
}
|
}
|
||||||
fmt.Printf("resume %X (%v)", context.Address(), context.Gas)
|
|
||||||
|
|
||||||
case RETURN:
|
// Handle remaining gas.
|
||||||
offset, size := stack.pop(), stack.pop()
|
*gas += gasLimit
|
||||||
ret := mem.Get(offset.Int64(), size.Int64())
|
|
||||||
fmt.Printf(" => [%v, %v] (%d) 0x%X", offset, size, len(ret), ret).Endl()
|
|
||||||
return context.Return(ret), nil
|
|
||||||
|
|
||||||
case SUICIDE:
|
fmt.Printf("resume %X (%v)\n", callee.Address, gas)
|
||||||
receiver := state.GetOrNewStateObject(stack.pop().Bytes())
|
|
||||||
balance := state.GetBalance(context.Address())
|
|
||||||
|
|
||||||
fmt.Printf(" => (%X) %v", receiver.Address()[:4], balance)
|
case RETURN: // 0xF3
|
||||||
|
offset, size := stack.Pop64(), stack.Pop64()
|
||||||
receiver.AddBalance(balance)
|
ret, ok := subslice(memory, offset, size)
|
||||||
|
if !ok {
|
||||||
state.Delete(context.Address())
|
return nil, firstErr(err, ErrMemoryOutOfBounds)
|
||||||
|
}
|
||||||
|
fmt.Printf(" => [%v, %v] (%d) 0x%X\n", offset, size, len(ret), ret)
|
||||||
|
return ret, nil
|
||||||
|
|
||||||
|
case SUICIDE: // 0xFF
|
||||||
|
addr := stack.Pop()
|
||||||
|
if ok = useGas(gas, GasGetAccount); !ok {
|
||||||
|
return nil, firstErr(err, ErrInsufficientGas)
|
||||||
|
}
|
||||||
|
receiver, err_ := vm.appState.GetAccount(addr)
|
||||||
|
if err = firstErr(err, err_); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
balance := callee.Balance
|
||||||
|
receiver.Balance += balance
|
||||||
|
vm.appState.UpdateAccount(receiver)
|
||||||
|
vm.appState.DeleteAccount(callee)
|
||||||
|
fmt.Printf(" => (%X) %v\n", addr[:4], balance)
|
||||||
fallthrough
|
fallthrough
|
||||||
case STOP: // Stop the context
|
|
||||||
vm.Endl()
|
|
||||||
|
|
||||||
return context.Return(nil), nil
|
|
||||||
default:
|
default:
|
||||||
fmt.Printf("(pc) %-3v Invalid opcode %X\n", pc, op).Endl()
|
fmt.Printf("(pc) %-3v Invalid opcode %X\n", pc, op)
|
||||||
|
|
||||||
panic(fmt.Errorf("Invalid opcode %X", op))
|
panic(fmt.Errorf("Invalid opcode %X", op))
|
||||||
}
|
}
|
||||||
|
|
||||||
pc++
|
pc++
|
||||||
|
|
||||||
vm.Endl()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
func (vm *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context *Context) (ret []byte, err error) {
|
func (vm *VM) CallPrecompiled(p *PrecompiledAccount, callData []byte, context *Context) (ret []byte, err error) {
|
||||||
gas := p.Gas(len(callData))
|
gas := p.Gas(len(callData))
|
||||||
if context.UseGas(gas) {
|
if context.UseGas(gas) {
|
||||||
ret = p.Call(callData)
|
ret = p.Call(callData)
|
||||||
@ -608,10 +693,44 @@ func subslice(data []byte, offset, length uint64) ([]byte, bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func useGas(gasLeft, gasToUse uint64) (uint64, bool) {
|
func codeGetOp(code []byte, n uint64) OpCode {
|
||||||
if gasLeft > gasToUse {
|
if uint64(len(code)) <= n {
|
||||||
return gasLeft - gasToUse, true
|
return OpCode(0) // stop
|
||||||
} else {
|
} else {
|
||||||
return gasLeft, false
|
return OpCode(code[n])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func jump(code []byte, to uint64) (err error) {
|
||||||
|
dest := codeGetOp(code, to)
|
||||||
|
if dest != JUMPDEST {
|
||||||
|
return ErrInvalidJumpDest
|
||||||
|
}
|
||||||
|
fmt.Printf(" ~> %v\n", to)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func firstErr(errA, errB error) error {
|
||||||
|
if errA != nil {
|
||||||
|
return errA
|
||||||
|
} else {
|
||||||
|
return errB
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func useGas(gas *uint64, gasToUse uint64) bool {
|
||||||
|
if *gas > gasToUse {
|
||||||
|
*gas -= gasToUse
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a 20 byte address from the creatorAddr and nonce.
|
||||||
|
func createAddress(creatorAddr Word, nonce uint64) Word {
|
||||||
|
temp := make([]byte, 32+8)
|
||||||
|
copy(temp, creatorAddr[:])
|
||||||
|
PutUint64(temp[32:], nonce)
|
||||||
|
return RightPadWord(sha3.Sha3(temp)[:20])
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user