mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-20 16:36:31 +00:00
VM intermediate...
This commit is contained in:
0
vm/.ethtest
Normal file
0
vm/.ethtest
Normal file
73
vm/common.go
Normal file
73
vm/common.go
Normal 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
141
vm/environment.go
Normal 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
74
vm/execution.go
Normal 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
7
vm/log.go
Normal 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
354
vm/opcodes.go
Normal 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
74
vm/precompiled.go
Normal 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
14
vm/receipt.go
Normal 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
171
vm/sha3/keccakf.go
Normal 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
224
vm/sha3/sha3.go
Normal 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
139
vm/stack.go
Normal 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
617
vm/vm.go
Normal 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
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user