VM intermediate...

This commit is contained in:
Jae Kwon
2015-03-16 02:24:08 -07:00
parent 722474a9b3
commit 77a16f100c
12 changed files with 1888 additions and 0 deletions

0
vm/.ethtest Normal file
View File

73
vm/common.go Normal file
View File

@ -0,0 +1,73 @@
package vm
import (
"math/big"
)
var (
GasStorageGet = Big(50)
GasStorageAdd = Big(20000)
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
func calcMemSize(off, l *big.Int) *big.Int {
if l.Cmp(Big0) == 0 {
return Big0
}
return new(big.Int).Add(off, l)
}
// Mainly used for print variables and passing to Print*
func toValue(val *big.Int) interface{} {
// Let's assume a string on right padded zero's
b := val.Bytes()
if b[0] != 0 && b[len(b)-1] == 0x0 && b[len(b)-2] == 0x0 {
return string(b)
}
return val
}

141
vm/environment.go Normal file
View File

@ -0,0 +1,141 @@
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)
}

74
vm/execution.go Normal file
View File

@ -0,0 +1,74 @@
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
}

7
vm/log.go Normal file
View File

@ -0,0 +1,7 @@
package vm
import (
"github.com/tendermint/tendermint/logger"
)
var log = logger.New("module", "vm")

354
vm/opcodes.go Normal file
View File

@ -0,0 +1,354 @@
package vm
import (
"fmt"
"gopkg.in/fatih/set.v0"
)
type OpCode byte
// Op codes
const (
// 0x0 range - arithmetic ops
STOP OpCode = iota
ADD
MUL
SUB
DIV
SDIV
MOD
SMOD
ADDMOD
MULMOD
EXP
SIGNEXTEND
)
const (
LT OpCode = iota + 0x10
GT
SLT
SGT
EQ
ISZERO
AND
OR
XOR
NOT
BYTE
SHA3 = 0x20
)
const (
// 0x30 range - closure state
ADDRESS OpCode = 0x30 + iota
BALANCE
ORIGIN
CALLER
CALLVALUE
CALLDATALOAD
CALLDATASIZE
CALLDATACOPY
CODESIZE
CODECOPY
GASPRICE
EXTCODESIZE
EXTCODECOPY
)
const (
// 0x40 range - block operations
BLOCKHASH OpCode = 0x40 + iota
COINBASE
TIMESTAMP
BLOCKHEIGHT
DIFFICULTY_DEPRECATED
GASLIMIT
)
const (
// 0x50 range - 'storage' and execution
POP OpCode = 0x50 + iota
MLOAD
MSTORE
MSTORE8
SLOAD
SSTORE
JUMP
JUMPI
PC
MSIZE
GAS
JUMPDEST
)
const (
// 0x60 range
PUSH1 OpCode = 0x60 + iota
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
DUP1
DUP2
DUP3
DUP4
DUP5
DUP6
DUP7
DUP8
DUP9
DUP10
DUP11
DUP12
DUP13
DUP14
DUP15
DUP16
SWAP1
SWAP2
SWAP3
SWAP4
SWAP5
SWAP6
SWAP7
SWAP8
SWAP9
SWAP10
SWAP11
SWAP12
SWAP13
SWAP14
SWAP15
SWAP16
)
const (
LOG0 OpCode = 0xa0 + iota
LOG1
LOG2
LOG3
LOG4
)
const (
// 0xf0 range - closures
CREATE OpCode = 0xf0 + iota
CALL
CALLCODE
RETURN
// 0x70 range - other
SUICIDE = 0xff
)
// Since the opcodes aren't all in order we can't use a regular slice
var opCodeToString = map[OpCode]string{
// 0x0 range - arithmetic ops
STOP: "STOP",
ADD: "ADD",
MUL: "MUL",
SUB: "SUB",
DIV: "DIV",
SDIV: "SDIV",
MOD: "MOD",
SMOD: "SMOD",
EXP: "EXP",
NOT: "NOT",
LT: "LT",
GT: "GT",
SLT: "SLT",
SGT: "SGT",
EQ: "EQ",
ISZERO: "ISZERO",
SIGNEXTEND: "SIGNEXTEND",
// 0x10 range - bit ops
AND: "AND",
OR: "OR",
XOR: "XOR",
BYTE: "BYTE",
ADDMOD: "ADDMOD",
MULMOD: "MULMOD",
// 0x20 range - crypto
SHA3: "SHA3",
// 0x30 range - closure state
ADDRESS: "ADDRESS",
BALANCE: "BALANCE",
ORIGIN: "ORIGIN",
CALLER: "CALLER",
CALLVALUE: "CALLVALUE",
CALLDATALOAD: "CALLDATALOAD",
CALLDATASIZE: "CALLDATASIZE",
CALLDATACOPY: "CALLDATACOPY",
CODESIZE: "CODESIZE",
CODECOPY: "CODECOPY",
GASPRICE: "TXGASPRICE",
// 0x40 range - block operations
BLOCKHASH: "BLOCKHASH",
COINBASE: "COINBASE",
TIMESTAMP: "TIMESTAMP",
BLOCKHEIGHT: "BLOCKHEIGHT",
DIFFICULTY_DEPRECATED: "DIFFICULTY_DEPRECATED",
GASLIMIT: "GASLIMIT",
EXTCODESIZE: "EXTCODESIZE",
EXTCODECOPY: "EXTCODECOPY",
// 0x50 range - 'storage' and execution
POP: "POP",
//DUP: "DUP",
//SWAP: "SWAP",
MLOAD: "MLOAD",
MSTORE: "MSTORE",
MSTORE8: "MSTORE8",
SLOAD: "SLOAD",
SSTORE: "SSTORE",
JUMP: "JUMP",
JUMPI: "JUMPI",
PC: "PC",
MSIZE: "MSIZE",
GAS: "GAS",
JUMPDEST: "JUMPDEST",
// 0x60 range - push
PUSH1: "PUSH1",
PUSH2: "PUSH2",
PUSH3: "PUSH3",
PUSH4: "PUSH4",
PUSH5: "PUSH5",
PUSH6: "PUSH6",
PUSH7: "PUSH7",
PUSH8: "PUSH8",
PUSH9: "PUSH9",
PUSH10: "PUSH10",
PUSH11: "PUSH11",
PUSH12: "PUSH12",
PUSH13: "PUSH13",
PUSH14: "PUSH14",
PUSH15: "PUSH15",
PUSH16: "PUSH16",
PUSH17: "PUSH17",
PUSH18: "PUSH18",
PUSH19: "PUSH19",
PUSH20: "PUSH20",
PUSH21: "PUSH21",
PUSH22: "PUSH22",
PUSH23: "PUSH23",
PUSH24: "PUSH24",
PUSH25: "PUSH25",
PUSH26: "PUSH26",
PUSH27: "PUSH27",
PUSH28: "PUSH28",
PUSH29: "PUSH29",
PUSH30: "PUSH30",
PUSH31: "PUSH31",
PUSH32: "PUSH32",
DUP1: "DUP1",
DUP2: "DUP2",
DUP3: "DUP3",
DUP4: "DUP4",
DUP5: "DUP5",
DUP6: "DUP6",
DUP7: "DUP7",
DUP8: "DUP8",
DUP9: "DUP9",
DUP10: "DUP10",
DUP11: "DUP11",
DUP12: "DUP12",
DUP13: "DUP13",
DUP14: "DUP14",
DUP15: "DUP15",
DUP16: "DUP16",
SWAP1: "SWAP1",
SWAP2: "SWAP2",
SWAP3: "SWAP3",
SWAP4: "SWAP4",
SWAP5: "SWAP5",
SWAP6: "SWAP6",
SWAP7: "SWAP7",
SWAP8: "SWAP8",
SWAP9: "SWAP9",
SWAP10: "SWAP10",
SWAP11: "SWAP11",
SWAP12: "SWAP12",
SWAP13: "SWAP13",
SWAP14: "SWAP14",
SWAP15: "SWAP15",
SWAP16: "SWAP16",
LOG0: "LOG0",
LOG1: "LOG1",
LOG2: "LOG2",
LOG3: "LOG3",
LOG4: "LOG4",
// 0xf0 range
CREATE: "CREATE",
CALL: "CALL",
RETURN: "RETURN",
CALLCODE: "CALLCODE",
// 0x70 range - other
SUICIDE: "SUICIDE",
}
func (o OpCode) String() string {
str := opCodeToString[o]
if len(str) == 0 {
return fmt.Sprintf("Missing opcode 0x%x", int(o))
}
return str
}
//-----------------------------------------------------------------------------
func AnalyzeJumpDests(code []byte) (dests *set.Set) {
dests = set.New()
for pc := uint64(0); pc < uint64(len(code)); pc++ {
var op OpCode = OpCode(code[pc])
switch op {
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) - uint64(PUSH1) + 1
pc += a
case JUMPDEST:
dests.Add(pc)
}
}
return
}

74
vm/precompiled.go Normal file
View File

@ -0,0 +1,74 @@
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
}

14
vm/receipt.go Normal file
View File

@ -0,0 +1,14 @@
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)
}

171
vm/sha3/keccakf.go Normal file
View File

@ -0,0 +1,171 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sha3
// This file implements the core Keccak permutation function necessary for computing SHA3.
// This is implemented in a separate file to allow for replacement by an optimized implementation.
// Nothing in this package is exported.
// For the detailed specification, refer to the Keccak web site (http://keccak.noekeon.org/).
// rc stores the round constants for use in the ι step.
var rc = [...]uint64{
0x0000000000000001,
0x0000000000008082,
0x800000000000808A,
0x8000000080008000,
0x000000000000808B,
0x0000000080000001,
0x8000000080008081,
0x8000000000008009,
0x000000000000008A,
0x0000000000000088,
0x0000000080008009,
0x000000008000000A,
0x000000008000808B,
0x800000000000008B,
0x8000000000008089,
0x8000000000008003,
0x8000000000008002,
0x8000000000000080,
0x000000000000800A,
0x800000008000000A,
0x8000000080008081,
0x8000000000008080,
0x0000000080000001,
0x8000000080008008,
}
// ro_xx represent the rotation offsets for use in the χ step.
// Defining them as const instead of in an array allows the compiler to insert constant shifts.
const (
ro_00 = 0
ro_01 = 36
ro_02 = 3
ro_03 = 41
ro_04 = 18
ro_05 = 1
ro_06 = 44
ro_07 = 10
ro_08 = 45
ro_09 = 2
ro_10 = 62
ro_11 = 6
ro_12 = 43
ro_13 = 15
ro_14 = 61
ro_15 = 28
ro_16 = 55
ro_17 = 25
ro_18 = 21
ro_19 = 56
ro_20 = 27
ro_21 = 20
ro_22 = 39
ro_23 = 8
ro_24 = 14
)
// keccakF computes the complete Keccak-f function consisting of 24 rounds with a different
// constant (rc) in each round. This implementation fully unrolls the round function to avoid
// inner loops, as well as pre-calculating shift offsets.
func (d *digest) keccakF() {
for _, roundConstant := range rc {
// θ step
d.c[0] = d.a[0] ^ d.a[5] ^ d.a[10] ^ d.a[15] ^ d.a[20]
d.c[1] = d.a[1] ^ d.a[6] ^ d.a[11] ^ d.a[16] ^ d.a[21]
d.c[2] = d.a[2] ^ d.a[7] ^ d.a[12] ^ d.a[17] ^ d.a[22]
d.c[3] = d.a[3] ^ d.a[8] ^ d.a[13] ^ d.a[18] ^ d.a[23]
d.c[4] = d.a[4] ^ d.a[9] ^ d.a[14] ^ d.a[19] ^ d.a[24]
d.d[0] = d.c[4] ^ (d.c[1]<<1 ^ d.c[1]>>63)
d.d[1] = d.c[0] ^ (d.c[2]<<1 ^ d.c[2]>>63)
d.d[2] = d.c[1] ^ (d.c[3]<<1 ^ d.c[3]>>63)
d.d[3] = d.c[2] ^ (d.c[4]<<1 ^ d.c[4]>>63)
d.d[4] = d.c[3] ^ (d.c[0]<<1 ^ d.c[0]>>63)
d.a[0] ^= d.d[0]
d.a[1] ^= d.d[1]
d.a[2] ^= d.d[2]
d.a[3] ^= d.d[3]
d.a[4] ^= d.d[4]
d.a[5] ^= d.d[0]
d.a[6] ^= d.d[1]
d.a[7] ^= d.d[2]
d.a[8] ^= d.d[3]
d.a[9] ^= d.d[4]
d.a[10] ^= d.d[0]
d.a[11] ^= d.d[1]
d.a[12] ^= d.d[2]
d.a[13] ^= d.d[3]
d.a[14] ^= d.d[4]
d.a[15] ^= d.d[0]
d.a[16] ^= d.d[1]
d.a[17] ^= d.d[2]
d.a[18] ^= d.d[3]
d.a[19] ^= d.d[4]
d.a[20] ^= d.d[0]
d.a[21] ^= d.d[1]
d.a[22] ^= d.d[2]
d.a[23] ^= d.d[3]
d.a[24] ^= d.d[4]
// ρ and π steps
d.b[0] = d.a[0]
d.b[1] = d.a[6]<<ro_06 ^ d.a[6]>>(64-ro_06)
d.b[2] = d.a[12]<<ro_12 ^ d.a[12]>>(64-ro_12)
d.b[3] = d.a[18]<<ro_18 ^ d.a[18]>>(64-ro_18)
d.b[4] = d.a[24]<<ro_24 ^ d.a[24]>>(64-ro_24)
d.b[5] = d.a[3]<<ro_15 ^ d.a[3]>>(64-ro_15)
d.b[6] = d.a[9]<<ro_21 ^ d.a[9]>>(64-ro_21)
d.b[7] = d.a[10]<<ro_02 ^ d.a[10]>>(64-ro_02)
d.b[8] = d.a[16]<<ro_08 ^ d.a[16]>>(64-ro_08)
d.b[9] = d.a[22]<<ro_14 ^ d.a[22]>>(64-ro_14)
d.b[10] = d.a[1]<<ro_05 ^ d.a[1]>>(64-ro_05)
d.b[11] = d.a[7]<<ro_11 ^ d.a[7]>>(64-ro_11)
d.b[12] = d.a[13]<<ro_17 ^ d.a[13]>>(64-ro_17)
d.b[13] = d.a[19]<<ro_23 ^ d.a[19]>>(64-ro_23)
d.b[14] = d.a[20]<<ro_04 ^ d.a[20]>>(64-ro_04)
d.b[15] = d.a[4]<<ro_20 ^ d.a[4]>>(64-ro_20)
d.b[16] = d.a[5]<<ro_01 ^ d.a[5]>>(64-ro_01)
d.b[17] = d.a[11]<<ro_07 ^ d.a[11]>>(64-ro_07)
d.b[18] = d.a[17]<<ro_13 ^ d.a[17]>>(64-ro_13)
d.b[19] = d.a[23]<<ro_19 ^ d.a[23]>>(64-ro_19)
d.b[20] = d.a[2]<<ro_10 ^ d.a[2]>>(64-ro_10)
d.b[21] = d.a[8]<<ro_16 ^ d.a[8]>>(64-ro_16)
d.b[22] = d.a[14]<<ro_22 ^ d.a[14]>>(64-ro_22)
d.b[23] = d.a[15]<<ro_03 ^ d.a[15]>>(64-ro_03)
d.b[24] = d.a[21]<<ro_09 ^ d.a[21]>>(64-ro_09)
// χ step
d.a[0] = d.b[0] ^ (^d.b[1] & d.b[2])
d.a[1] = d.b[1] ^ (^d.b[2] & d.b[3])
d.a[2] = d.b[2] ^ (^d.b[3] & d.b[4])
d.a[3] = d.b[3] ^ (^d.b[4] & d.b[0])
d.a[4] = d.b[4] ^ (^d.b[0] & d.b[1])
d.a[5] = d.b[5] ^ (^d.b[6] & d.b[7])
d.a[6] = d.b[6] ^ (^d.b[7] & d.b[8])
d.a[7] = d.b[7] ^ (^d.b[8] & d.b[9])
d.a[8] = d.b[8] ^ (^d.b[9] & d.b[5])
d.a[9] = d.b[9] ^ (^d.b[5] & d.b[6])
d.a[10] = d.b[10] ^ (^d.b[11] & d.b[12])
d.a[11] = d.b[11] ^ (^d.b[12] & d.b[13])
d.a[12] = d.b[12] ^ (^d.b[13] & d.b[14])
d.a[13] = d.b[13] ^ (^d.b[14] & d.b[10])
d.a[14] = d.b[14] ^ (^d.b[10] & d.b[11])
d.a[15] = d.b[15] ^ (^d.b[16] & d.b[17])
d.a[16] = d.b[16] ^ (^d.b[17] & d.b[18])
d.a[17] = d.b[17] ^ (^d.b[18] & d.b[19])
d.a[18] = d.b[18] ^ (^d.b[19] & d.b[15])
d.a[19] = d.b[19] ^ (^d.b[15] & d.b[16])
d.a[20] = d.b[20] ^ (^d.b[21] & d.b[22])
d.a[21] = d.b[21] ^ (^d.b[22] & d.b[23])
d.a[22] = d.b[22] ^ (^d.b[23] & d.b[24])
d.a[23] = d.b[23] ^ (^d.b[24] & d.b[20])
d.a[24] = d.b[24] ^ (^d.b[20] & d.b[21])
// ι step
d.a[0] ^= roundConstant
}
}

224
vm/sha3/sha3.go Normal file
View File

@ -0,0 +1,224 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package sha3 implements the SHA3 hash algorithm (formerly called Keccak) chosen by NIST in 2012.
// This file provides a SHA3 implementation which implements the standard hash.Hash interface.
// Writing input data, including padding, and reading output data are computed in this file.
// Note that the current implementation can compute the hash of an integral number of bytes only.
// This is a consequence of the hash interface in which a buffer of bytes is passed in.
// The internals of the Keccak-f function are computed in keccakf.go.
// For the detailed specification, refer to the Keccak web site (http://keccak.noekeon.org/).
package sha3
import (
"encoding/binary"
"hash"
)
// laneSize is the size in bytes of each "lane" of the internal state of SHA3 (5 * 5 * 8).
// Note that changing this size would requires using a type other than uint64 to store each lane.
const laneSize = 8
// sliceSize represents the dimensions of the internal state, a square matrix of
// sliceSize ** 2 lanes. This is the size of both the "rows" and "columns" dimensions in the
// terminology of the SHA3 specification.
const sliceSize = 5
// numLanes represents the total number of lanes in the state.
const numLanes = sliceSize * sliceSize
// stateSize is the size in bytes of the internal state of SHA3 (5 * 5 * WSize).
const stateSize = laneSize * numLanes
// digest represents the partial evaluation of a checksum.
// Note that capacity, and not outputSize, is the critical security parameter, as SHA3 can output
// an arbitrary number of bytes for any given capacity. The Keccak proposal recommends that
// capacity = 2*outputSize to ensure that finding a collision of size outputSize requires
// O(2^{outputSize/2}) computations (the birthday lower bound). Future standards may modify the
// capacity/outputSize ratio to allow for more output with lower cryptographic security.
type digest struct {
a [numLanes]uint64 // main state of the hash
b [numLanes]uint64 // intermediate states
c [sliceSize]uint64 // intermediate states
d [sliceSize]uint64 // intermediate states
outputSize int // desired output size in bytes
capacity int // number of bytes to leave untouched during squeeze/absorb
absorbed int // number of bytes absorbed thus far
}
// minInt returns the lesser of two integer arguments, to simplify the absorption routine.
func minInt(v1, v2 int) int {
if v1 <= v2 {
return v1
}
return v2
}
// rate returns the number of bytes of the internal state which can be absorbed or squeezed
// in between calls to the permutation function.
func (d *digest) rate() int {
return stateSize - d.capacity
}
// Reset clears the internal state by zeroing bytes in the state buffer.
// This can be skipped for a newly-created hash state; the default zero-allocated state is correct.
func (d *digest) Reset() {
d.absorbed = 0
for i := range d.a {
d.a[i] = 0
}
}
// BlockSize, required by the hash.Hash interface, does not have a standard intepretation
// for a sponge-based construction like SHA3. We return the data rate: the number of bytes which
// can be absorbed per invocation of the permutation function. For Merkle-Damgård based hashes
// (ie SHA1, SHA2, MD5) the output size of the internal compression function is returned.
// We consider this to be roughly equivalent because it represents the number of bytes of output
// produced per cryptographic operation.
func (d *digest) BlockSize() int { return d.rate() }
// Size returns the output size of the hash function in bytes.
func (d *digest) Size() int {
return d.outputSize
}
// unalignedAbsorb is a helper function for Write, which absorbs data that isn't aligned with an
// 8-byte lane. This requires shifting the individual bytes into position in a uint64.
func (d *digest) unalignedAbsorb(p []byte) {
var t uint64
for i := len(p) - 1; i >= 0; i-- {
t <<= 8
t |= uint64(p[i])
}
offset := (d.absorbed) % d.rate()
t <<= 8 * uint(offset%laneSize)
d.a[offset/laneSize] ^= t
d.absorbed += len(p)
}
// Write "absorbs" bytes into the state of the SHA3 hash, updating as needed when the sponge
// "fills up" with rate() bytes. Since lanes are stored internally as type uint64, this requires
// converting the incoming bytes into uint64s using a little endian interpretation. This
// implementation is optimized for large, aligned writes of multiples of 8 bytes (laneSize).
// Non-aligned or uneven numbers of bytes require shifting and are slower.
func (d *digest) Write(p []byte) (int, error) {
// An initial offset is needed if the we aren't absorbing to the first lane initially.
offset := d.absorbed % d.rate()
toWrite := len(p)
// The first lane may need to absorb unaligned and/or incomplete data.
if (offset%laneSize != 0 || len(p) < 8) && len(p) > 0 {
toAbsorb := minInt(laneSize-(offset%laneSize), len(p))
d.unalignedAbsorb(p[:toAbsorb])
p = p[toAbsorb:]
offset = (d.absorbed) % d.rate()
// For every rate() bytes absorbed, the state must be permuted via the F Function.
if (d.absorbed)%d.rate() == 0 {
d.keccakF()
}
}
// This loop should absorb the bulk of the data into full, aligned lanes.
// It will call the update function as necessary.
for len(p) > 7 {
firstLane := offset / laneSize
lastLane := minInt(d.rate()/laneSize, firstLane+len(p)/laneSize)
// This inner loop absorbs input bytes into the state in groups of 8, converted to uint64s.
for lane := firstLane; lane < lastLane; lane++ {
d.a[lane] ^= binary.LittleEndian.Uint64(p[:laneSize])
p = p[laneSize:]
}
d.absorbed += (lastLane - firstLane) * laneSize
// For every rate() bytes absorbed, the state must be permuted via the F Function.
if (d.absorbed)%d.rate() == 0 {
d.keccakF()
}
offset = 0
}
// If there are insufficient bytes to fill the final lane, an unaligned absorption.
// This should always start at a correct lane boundary though, or else it would be caught
// by the uneven opening lane case above.
if len(p) > 0 {
d.unalignedAbsorb(p)
}
return toWrite, nil
}
// pad computes the SHA3 padding scheme based on the number of bytes absorbed.
// The padding is a 1 bit, followed by an arbitrary number of 0s and then a final 1 bit, such that
// the input bits plus padding bits are a multiple of rate(). Adding the padding simply requires
// xoring an opening and closing bit into the appropriate lanes.
func (d *digest) pad() {
offset := d.absorbed % d.rate()
// The opening pad bit must be shifted into position based on the number of bytes absorbed
padOpenLane := offset / laneSize
d.a[padOpenLane] ^= 0x0000000000000001 << uint(8*(offset%laneSize))
// The closing padding bit is always in the last position
padCloseLane := (d.rate() / laneSize) - 1
d.a[padCloseLane] ^= 0x8000000000000000
}
// finalize prepares the hash to output data by padding and one final permutation of the state.
func (d *digest) finalize() {
d.pad()
d.keccakF()
}
// squeeze outputs an arbitrary number of bytes from the hash state.
// Squeezing can require multiple calls to the F function (one per rate() bytes squeezed),
// although this is not the case for standard SHA3 parameters. This implementation only supports
// squeezing a single time, subsequent squeezes may lose alignment. Future implementations
// may wish to support multiple squeeze calls, for example to support use as a PRNG.
func (d *digest) squeeze(in []byte, toSqueeze int) []byte {
// Because we read in blocks of laneSize, we need enough room to read
// an integral number of lanes
needed := toSqueeze + (laneSize-toSqueeze%laneSize)%laneSize
if cap(in)-len(in) < needed {
newIn := make([]byte, len(in), len(in)+needed)
copy(newIn, in)
in = newIn
}
out := in[len(in) : len(in)+needed]
for len(out) > 0 {
for i := 0; i < d.rate() && len(out) > 0; i += laneSize {
binary.LittleEndian.PutUint64(out[:], d.a[i/laneSize])
out = out[laneSize:]
}
if len(out) > 0 {
d.keccakF()
}
}
return in[:len(in)+toSqueeze] // Re-slice in case we wrote extra data.
}
// Sum applies padding to the hash state and then squeezes out the desired nubmer of output bytes.
func (d *digest) Sum(in []byte) []byte {
// Make a copy of the original hash so that caller can keep writing and summing.
dup := *d
dup.finalize()
return dup.squeeze(in, dup.outputSize)
}
// The NewKeccakX constructors enable initializing a hash in any of the four recommend sizes
// from the Keccak specification, all of which set capacity=2*outputSize. Note that the final
// NIST standard for SHA3 may specify different input/output lengths.
// The output size is indicated in bits but converted into bytes internally.
func NewKeccak224() hash.Hash { return &digest{outputSize: 224 / 8, capacity: 2 * 224 / 8} }
func NewKeccak256() hash.Hash { return &digest{outputSize: 256 / 8, capacity: 2 * 256 / 8} }
func NewKeccak384() hash.Hash { return &digest{outputSize: 384 / 8, capacity: 2 * 384 / 8} }
func NewKeccak512() hash.Hash { return &digest{outputSize: 512 / 8, capacity: 2 * 512 / 8} }
func Sha3(data ...[]byte) []byte {
d := NewKeccak256()
for _, b := range data {
d.Write(b)
}
return d.Sum(nil)
}

139
vm/stack.go Normal file
View File

@ -0,0 +1,139 @@
package vm
import (
"encoding/binary"
"errors"
"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
type Stack struct {
data []Word
ptr int
gas *uint64
err *error
}
func NewStack(capacity int, gas *uint64, err *error) *Stack {
return &Stack{
data: make([]Word, capacity),
ptr: 0,
gas: gas,
err: err,
}
}
func (st *Stack) Push(d Word) error {
if st.ptr == cap(st.data) {
return ErrDataStackOverflow
}
st.data[st.ptr] = d
st.ptr++
}
func (st *Stack) Push64(i uint64) error {
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 {
panic("Invalid bytes size: expected 32")
}
if st.ptr == cap(st.data) {
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) {
if st.ptr == 0 {
return Zero, ErrDataStackUnderflow
}
st.ptr--
return st.data[st.ptr], nil
}
func (st *Stack) Pop64() (uint64, error) {
if st.ptr == 0 {
return Zero, ErrDataStackUnderflow
}
st.ptr--
return st.data[st.ptr][0], nil
}
func (st *Stack) PopBytes() ([]byte, error) {
if st.ptr == 0 {
return Zero, ErrDataStackUnderflow
}
st.ptr--
res := make([]byte, 32)
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 {
return st.ptr
}
func (st *Stack) Swap(n int) error {
if st.ptr < n {
return ErrDataStackUnderflow
}
st.data[st.ptr-n], st.data[st.ptr-1] = st.data[st.ptr-1], st.data[st.ptr-n]
return nil
}
func (st *Stack) Dup(n int) {
st.Push(st.data[st.ptr-n])
}
func (st *Stack) Peek() Word {
return st.data[st.ptr-1]
}
func (st *Stack) Require(n int) error {
if st.ptr < n {
return ErrDataStackUnderflow
}
}
func (st *Stack) Print() {
fmt.Println("### stack ###")
if st.ptr > 0 {
for i, val := range st.data {
fmt.Printf("%-3d %v\n", i, val)
}
} else {
fmt.Println("-- empty --")
}
fmt.Println("#############")
}

617
vm/vm.go Normal file
View File

@ -0,0 +1,617 @@
package vm
import (
"fmt"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/vm/sha3"
)
type Vm struct {
VMEnvironment
}
func NewVM(appState AppState, params VMParams) *Vm {
vmEnv := NewVMEnvironment(appState, params)
return &VM{
VMEnvironment: vmEnv,
}
}
// feeLimit: the maximum the caller is willing to pay for fees.
// gasLimit: the maximum gas that will be run.
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
if caller.Balance < (feeLimit + value) {
return nil, ErrInsufficientAccountBalance
}
// Deduct balance from caller.
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)
/*
if p := Precompiled[string(me.Address())]; p != nil {
return vm.RunPrecompiled(p, callData, context)
}
*/
//-----------------------------------
// By the time we're here, the related VMCall context is already appended onto VMEnvironment.callStack
var (
code = target.Code
pc uint64 = 0
gas uint64 = call.gasLimit
err error = nil
stack = NewStack(defaultDataStackCapacity, &gas, &err)
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 {
var op = CodeGetOp(code, pc)
fmt.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.Len())
switch op {
case ADD: // 0x01
x, y := stack.Pop64(), stack.Pop64()
stack.Push64(x + y)
fmt.Printf(" %v + %v = %v\n", x, y, x+y)
case MUL: // 0x02
x, y := stack.Pop64(), stack.Pop64()
stack.Push64(x * y)
fmt.Printf(" %v * %v = %v\n", x, y, x*y)
case SUB: // 0x03
x, y := stack.Pop64(), stack.Pop64()
stack.Push64(x - y)
fmt.Printf(" %v - %v = %v\n", x, y, x-y)
case DIV: // 0x04
x, y := stack.Pop64(), stack.Pop64()
if y == 0 { // TODO
stack.Push64(0)
fmt.Printf(" %v / %v = %v (TODO)\n", x, y, 0)
} else {
stack.Push64(x / y)
fmt.Printf(" %v / %v = %v\n", x, y, x/y)
}
case SDIV: // 0x05
x, y := int64(stack.Pop64()), int64(stack.Pop64())
if y == 0 { // TODO
stack.Push64(0)
fmt.Printf(" %v / %v = %v (TODO)\n", x, y, 0)
} else {
stack.Push64(uint64(x / y))
fmt.Printf(" %v / %v = %v\n", x, y, x/y)
}
case MOD: // 0x06
x, y := stack.Pop64(), stack.Pop64()
if y == 0 { // TODO
stack.Push64(0)
fmt.Printf(" %v %% %v = %v (TODO)\n", x, y, 0)
} else {
stack.Push64(x % y)
fmt.Printf(" %v %% %v = %v\n", x, y, x%y)
}
case SMOD: // 0x07
x, y := int64(stack.Pop64()), int64(stack.Pop64())
if y == 0 { // TODO
stack.Push64(0)
fmt.Printf(" %v %% %v = %v (TODO)\n", x, y, 0)
} else {
stack.Push64(uint64(x % y))
fmt.Printf(" %v %% %v = %v\n", x, y, x%y)
}
case ADDMOD: // 0x08
x, y, z := stack.Pop64(), stack.Pop64(), stack.Pop64()
if z == 0 { // TODO
stack.Push64(0)
fmt.Printf(" (%v + %v) %% %v = %v (TODO)\n", x, y, z, 0)
} else {
stack.Push64(x % y)
fmt.Printf(" (%v + %v) %% %v = %v\n", x, y, z, (x+y)%z)
}
case MULMOD: // 0x09
x, y, z := stack.Pop64(), stack.Pop64(), stack.Pop64()
if z == 0 { // TODO
stack.Push64(0)
fmt.Printf(" (%v + %v) %% %v = %v (TODO)\n", x, y, z, 0)
} else {
stack.Push64(x % y)
fmt.Printf(" (%v + %v) %% %v = %v\n", x, y, z, (x*y)%z)
}
case EXP: // 0x0A
x, y := stack.Pop64(), stack.Pop64()
stack.Push64(ExpUint64(x, y))
fmt.Printf(" %v ** %v = %v\n", x, y, uint64(math.Pow(float64(x), float64(y))))
case SIGNEXTEND: // 0x0B
x, y := stack.Pop64(), stack.Pop64()
res := (y << uint(x)) >> x
stack.Push64(res)
fmt.Printf(" (%v << %v) >> %v = %v\n", y, x, x, res)
case LT: // 0x10
x, y := stack.Pop64(), stack.Pop64()
if x < y {
stack.Push64(1)
} else {
stack.Push64(0)
}
fmt.Printf(" %v < %v = %v\n", x, y, x < y)
case GT: // 0x11
x, y := stack.Pop64(), stack.Pop64()
if x > y {
stack.Push64(1)
} else {
stack.Push64(0)
}
fmt.Printf(" %v > %v = %v\n", x, y, x > y)
case SLT: // 0x12
x, y := int64(stack.Pop64()), int64(stack.Pop64())
if x < y {
stack.Push64(1)
} else {
stack.Push64(0)
}
fmt.Printf(" %v < %v = %v\n", x, y, x < y)
case SGT: // 0x13
x, y := int64(stack.Pop64()), int64(stack.Pop64())
if x > y {
stack.Push64(1)
} else {
stack.Push64(0)
}
fmt.Printf(" %v > %v = %v\n", x, y, x > y)
case EQ: // 0x14
x, y := stack.Pop64(), stack.Pop64()
if x > y {
stack.Push64(1)
} else {
stack.Push64(0)
}
fmt.Printf(" %v == %v = %v\n", x, y, x == y)
case ISZERO: // 0x15
x := stack.Pop64()
if x == 0 {
stack.Push64(1)
} else {
stack.Push64(0)
}
fmt.Printf(" %v == 0 = %v\n", x, x == 0)
case AND: // 0x16
x, y := stack.Pop64(), stack.Pop64()
stack.Push64(x & y)
fmt.Printf(" %v & %v = %v\n", x, y, x&y)
case OR: // 0x17
x, y := stack.Pop64(), stack.Pop64()
stack.Push64(x | y)
fmt.Printf(" %v | %v = %v\n", x, y, x|y)
case XOR: // 0x18
x, y := stack.Pop64(), stack.Pop64()
stack.Push64(x ^ y)
fmt.Printf(" %v ^ %v = %v\n", x, y, x^y)
case NOT: // 0x19
x := stack.Pop64()
stack.Push64(^x)
fmt.Printf(" !%v = %v\n", x, ^x)
case BYTE: // 0x1A
idx, val := stack.Pop64(), stack.Pop()
res := 0
if idx < 32 {
res = Uint642Bytes(val[idx/8])[idx%8]
}
stack.Push64(res)
fmt.Printf(" => 0x%X", res)
case SHA3: // 0x20
if gas, ok = useGas(gas, GasSha3); !ok {
return ErrInsufficientGas
}
offset, size := stack.Pop64(), stack.Pop64()
data := sha3.Sha3(memory.Get(offset, size))
stack.PushBytes(data)
fmt.Printf(" => (%v) %X", size, data)
case ADDRESS: // 0x30
stack.PushBytes(RightPadBytes(context.Address(), 32))
fmt.Printf(" => %X", RightPadBytes(context.Address(), 32))
case BALANCE: // 0x31
addr := stack.PopBytes()
if gas, ok = useGas(gas, GasGetAccount); !ok {
return ErrInsufficientGas
}
account := vm.GetAccount(addr) // TODO ensure that 20byte lengths are supported.
balance := account.Balance
stack.Push64(balance)
fmt.Printf(" => %v (%X)", balance, addr)
case ORIGIN: // 0x32
origin := vm.Origin()
stack.PushBytes(origin)
fmt.Printf(" => %X", origin)
case CALLER: // 0x33
caller := vm.lastCall.caller
stack.PushBytes(caller.Address)
fmt.Printf(" => %X", caller.Address)
case CALLVALUE: // 0x34
stack.Push64(value)
fmt.Printf(" => %v", value)
case CALLDATALOAD: // 0x35
offset := stack.Pop64()
data, _ := subslice(input, offset, 32)
stack.PushBytes(RightPadBytes(data), 32)
fmt.Printf(" => 0x%X", data)
case CALLDATASIZE: // 0x36
stack.Push64(uint64(len(callData)))
fmt.Printf(" => %d", len(callData))
case CALLDATACOPY: // 0x37
memOff := stack.Pop64()
inputOff := stack.Pop64()
length := stack.Pop64()
data, ok := subslice(input, inputOff, length)
if ok {
memory.Set(memOff, length, data)
}
fmt.Printf(" => [%v, %v, %v] %X", memOff, inputOff, length, data)
case CODESIZE: // 0x38
l := uint64(len(code))
stack.Push64(l)
fmt.Printf(" => %d", l)
case CODECOPY: // 0x39
memOff := stack.Pop64()
codeOff := stack.Pop64()
length := stack.Pop64()
data, ok := subslice(code, codeOff, length)
if ok {
memory.Set(memOff, length, data)
}
fmt.Printf(" => [%v, %v, %v] %X", memOff, codeOff, length, data)
case GASPRICE: // 0x3A
stack.Push64(vm.params.GasPrice)
fmt.Printf(" => %X", vm.params.GasPrice)
case EXTCODESIZE: // 0x3B
addr := stack.PopBytes()[:20]
account := vm.GetAccount(addr)
code := account.Code
l := uint64(len(code))
stack.Push64(l)
fmt.Printf(" => %d", l)
case EXTCODECOPY: // 0x3C
addr := stack.PopBytes()[:20]
account := vm.GetAccount(addr)
code := account.Code
memOff := stack.Pop64()
codeOff := stack.Pop64()
length := stack.Pop64()
data, ok := subslice(code, codeOff, length)
if ok {
memory.Set(memOff, length, data)
}
fmt.Printf(" => [%v, %v, %v] %X", memOff, codeOff, length, data)
case BLOCKHASH: // 0x40
/*
num := stack.pop()
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
stack.Push([4]Word{0, 0, 0, 0})
fmt.Printf(" => 0x%X (NOT SUPPORTED)", stack.Peek().Bytes())
case TIMESTAMP: // 0x42
time := vm.params.BlockTime
stack.Push64(uint64(time))
fmt.Printf(" => 0x%X", time)
case BLOCKHEIGHT: // 0x43
number := uint64(vm.params.BlockHeight)
stack.Push64(number)
fmt.Printf(" => 0x%X", number)
case GASLIMIT: // 0x45
stack.Push64(vm.params.GasLimit)
fmt.Printf(" => %v", vm.params.GasLimit)
case POP: // 0x50
stack.Pop()
fmt.Printf(" => %v", vm.params.GasLimit)
case MLOAD: // 0x51
offset := stack.Pop64()
data, _ := subslice(input, offset, 32)
stack.PushBytes(RightPadBytes(data), 32)
fmt.Printf(" => 0x%X", data)
offset := stack.pop()
val := Bytes2Big(mem.Get(offset.Int64(), 32))
stack.push(val)
fmt.Printf(" => 0x%X", val.Bytes())
case MSTORE: // Store the value at stack top-1 in to memory at location stack top
// pop value of the stack
mStart, val := stack.pop(), stack.pop()
mem.Set(mStart.Uint64(), 32, Big2Bytes(val, 256))
fmt.Printf(" => 0x%X", val)
case MSTORE8:
off, val := stack.pop(), stack.pop()
mem.store[off.Int64()] = byte(val.Int64() & 0xff)
fmt.Printf(" => [%v] 0x%X", off, val)
case SLOAD:
loc := stack.pop()
val := Bytes2Big(state.GetState(context.Address(), loc.Bytes()))
stack.push(val)
fmt.Printf(" {0x%X : 0x%X}", loc.Bytes(), val.Bytes())
case SSTORE:
loc, val := stack.pop(), stack.pop()
state.SetState(context.Address(), loc.Bytes(), val)
fmt.Printf(" {0x%X : 0x%X}", loc.Bytes(), val.Bytes())
case JUMP:
jump(pc, stack.pop())
continue
case JUMPI:
pos, cond := stack.pop(), stack.pop()
if cond.Cmp(BigTrue) >= 0 {
jump(pc, pos)
continue
}
fmt.Printf(" ~> false")
case JUMPDEST:
case PC:
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 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)
byts := context.GetRangeValue(pc+1, a)
// push value to stack
stack.push(Bytes2Big(byts))
pc += a
fmt.Printf(" => 0x%X", byts)
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
n := int(op - DUP1 + 1)
stack.dup(n)
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:
n := int(op - SWAP1 + 2)
stack.swap(n)
fmt.Printf(" => [%d]", n)
case LOG0, LOG1, LOG2, LOG3, LOG4:
n := int(op - LOG0)
topics := make([][]byte, n)
mStart, mSize := stack.pop(), stack.pop()
for i := 0; i < n; i++ {
topics[i] = LeftPadBytes(stack.pop().Bytes(), 32)
}
data := mem.Get(mStart.Int64(), mSize.Int64())
log := &Log{context.Address(), topics, data, vm.env.BlockHeight().Uint64()}
vm.env.AddLog(log)
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 {
// 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:
gas := stack.pop()
// pop gas and value of the stack.
addr, value := stack.pop(), stack.pop()
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()
fmt.Printf(" => %X", address).Endl()
// Get the arguments from the memory
args := mem.Get(inOffset.Int64(), inSize.Int64())
if len(value.Bytes()) > 0 {
gas.Add(gas, GasStipend)
}
var (
ret []byte
err error
)
if op == CALLCODE {
ret, err = CallCode(env, context, address, args, gas, price, value)
} else {
ret, err = Call(env, context, address, args, gas, price, value)
}
if err != nil {
stack.push(BigFalse)
fmt.Printf("%v").Endl()
} else {
stack.push(BigTrue)
mem.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
fmt.Printf("resume %X (%v)", context.Address(), context.Gas)
case RETURN:
offset, size := stack.pop(), stack.pop()
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:
receiver := state.GetOrNewStateObject(stack.pop().Bytes())
balance := state.GetBalance(context.Address())
fmt.Printf(" => (%X) %v", receiver.Address()[:4], balance)
receiver.AddBalance(balance)
state.Delete(context.Address())
fallthrough
case STOP: // Stop the context
vm.Endl()
return context.Return(nil), nil
default:
fmt.Printf("(pc) %-3v Invalid opcode %X\n", pc, op).Endl()
panic(fmt.Errorf("Invalid opcode %X", op))
}
pc++
vm.Endl()
}
}
/*
func (vm *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context *Context) (ret []byte, err error) {
gas := p.Gas(len(callData))
if context.UseGas(gas) {
ret = p.Call(callData)
fmt.Printf("NATIVE_FUNC => %X", ret)
vm.Endl()
return context.Return(ret), nil
} else {
fmt.Printf("NATIVE_FUNC => failed").Endl()
tmp := new(big.Int).Set(context.Gas)
panic(OOG(gas, tmp).Error())
}
}
*/
func subslice(data []byte, offset, length uint64) ([]byte, bool) {
size := uint64(len(data))
if size < offset {
return nil, false
} else if size < offset+length {
return data[offset:], false
} else {
return data[offset : offset+length], true
}
}
func useGas(gasLeft, gasToUse uint64) (uint64, bool) {
if gasLeft > gasToUse {
return gasLeft - gasToUse, true
} else {
return gasLeft, false
}
}