vm makeover

This commit is contained in:
Jae Kwon 2015-03-17 21:46:26 -07:00
parent f03547007a
commit c21369cebd
11 changed files with 607 additions and 684 deletions

View File

@ -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)
} }

View File

@ -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)
}

View File

@ -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
View 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
View 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
}

View File

@ -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
}

View File

@ -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

@ -0,0 +1 @@
Subproject commit 8a939c2f861148885b43c58ce52a36882c516fd6

View File

@ -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
View 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
View File

@ -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])
}