This commit is contained in:
Jeffrey Wilcke 2015-07-04 16:48:56 +00:00
commit 0ab465e84e
11 changed files with 570 additions and 88 deletions

View file

@ -22,79 +22,135 @@
package main
import (
"flag"
"fmt"
"log"
"math/big"
"os"
"runtime"
"time"
"github.com/codegangsta/cli"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
)
var (
code = flag.String("code", "", "evm code")
loglevel = flag.Int("log", 4, "log level")
gas = flag.String("gas", "1000000000", "gas amount")
price = flag.String("price", "0", "gas price")
value = flag.String("value", "0", "tx value")
dump = flag.Bool("dump", false, "dump state after run")
data = flag.String("data", "", "data")
app *cli.App
DebugFlag = cli.BoolFlag{
Name: "debug",
Usage: "output full trace logs",
}
SegmentationFlag = cli.BoolFlag{
Name: "nosegmentation",
Usage: "Disabled VM segmentation",
}
CodeFlag = cli.StringFlag{
Name: "code",
Usage: "EVM code",
}
GasFlag = cli.StringFlag{
Name: "gas",
Usage: "gas limit for the evm",
Value: "10000000000",
}
PriceFlag = cli.StringFlag{
Name: "price",
Usage: "price set for the evm",
Value: "0",
}
ValueFlag = cli.StringFlag{
Name: "value",
Usage: "value set for the evm",
Value: "0",
}
DumpFlag = cli.BoolFlag{
Name: "dump",
Usage: "dumps the state after the run",
}
InputFlag = cli.StringFlag{
Name: "input",
Usage: "input for the EVM",
}
SysStatFlag = cli.BoolFlag{
Name: "sysstat",
Usage: "display system stats",
}
)
func perr(v ...interface{}) {
fmt.Println(v...)
//os.Exit(1)
func init() {
app = utils.NewApp("0.2", "the evm command line interface")
app.Flags = []cli.Flag{
DebugFlag,
SegmentationFlag,
SysStatFlag,
CodeFlag,
GasFlag,
PriceFlag,
ValueFlag,
DumpFlag,
InputFlag,
}
app.Action = run
}
func main() {
flag.Parse()
func run(ctx *cli.Context) {
vm.Debug = ctx.GlobalBool(DebugFlag.Name)
vm.DisableSegmentation = ctx.GlobalBool(SegmentationFlag.Name)
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.LogLevel(*loglevel)))
vm.Debug = true
db, _ := ethdb.NewMemDatabase()
statedb := state.New(common.Hash{}, db)
sender := statedb.CreateAccount(common.StringToAddress("sender"))
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
receiver.SetCode(common.Hex2Bytes(*code))
receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)))
vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(*value))
vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)))
tstart := time.Now()
ret, e := vmenv.Call(
sender,
receiver.Address(),
common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)),
common.Big(ctx.GlobalString(GasFlag.Name)),
common.Big(ctx.GlobalString(PriceFlag.Name)),
common.Big(ctx.GlobalString(ValueFlag.Name)),
)
vmdone := time.Since(tstart)
ret, e := vmenv.Call(sender, receiver.Address(), common.Hex2Bytes(*data), common.Big(*gas), common.Big(*price), common.Big(*value))
logger.Flush()
if e != nil {
perr(e)
fmt.Println(e)
os.Exit(1)
}
if *dump {
if ctx.GlobalBool(DumpFlag.Name) {
fmt.Println(string(statedb.Dump()))
}
vm.StdErrFormat(vmenv.StructLogs())
var mem runtime.MemStats
runtime.ReadMemStats(&mem)
fmt.Printf("vm took %v\n", time.Since(tstart))
fmt.Printf(`alloc: %d
if ctx.GlobalBool(SysStatFlag.Name) {
var mem runtime.MemStats
runtime.ReadMemStats(&mem)
fmt.Printf("vm took %v\n", vmdone)
fmt.Printf(`alloc: %d
tot alloc: %d
no. malloc: %d
heap alloc: %d
heap objs: %d
num gc: %d
`, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC)
}
fmt.Printf("%x\n", ret)
fmt.Printf("OUT: 0x%x\n", ret)
}
func main() {
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
type VMEnv struct {

View file

@ -235,7 +235,7 @@ func (self *StateObject) AddGas(gas, price *big.Int) {
}
func (self *StateObject) Copy() *StateObject {
stateObject := NewStateObject(self.Address(), self.db)
stateObject := &StateObject{db: self.db, address: self.address, balance: new(big.Int), gasPool: new(big.Int), dirty: true}
stateObject.balance.Set(self.balance)
stateObject.codeHash = common.CopyBytes(self.codeHash)
stateObject.nonce = self.nonce

View file

@ -26,16 +26,25 @@ type Context struct {
Args []byte
}
var dests destinations
func init() {
dests = make(destinations)
}
// Create a new context for the given data items.
func NewContext(caller ContextRef, object ContextRef, value, gas, price *big.Int) *Context {
c := &Context{caller: caller, self: object, Args: nil}
if parent, ok := caller.(*Context); ok {
// Reuse JUMPDEST analysis from parent context if available.
c.jumpdests = parent.jumpdests
} else {
c.jumpdests = make(destinations)
}
c.jumpdests = dests
/*
if parent, ok := caller.(*Context); ok {
// Reuse JUMPDEST analysis from parent context if available.
c.jumpdests = parent.jumpdests
} else {
c.jumpdests = make(destinations)
}
*/
// Gas should be a pointer so it can safely be reduced through the run
// This pointer will be off the state transition

View file

@ -38,8 +38,8 @@ func baseCheck(op OpCode, stack *stack, gas *big.Int) error {
return err
}
if r.stackPush > 0 && len(stack.data)-r.stackPop+r.stackPush > int(params.StackLimit.Int64())+1 {
return fmt.Errorf("stack limit reached %d (%d)", len(stack.data), params.StackLimit.Int64())
if r.stackPush > 0 && stack.len()-r.stackPop+r.stackPush > int(params.StackLimit.Int64()) {
return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64())
}
gas.Add(gas, r.gas)

View file

@ -9,7 +9,6 @@ import (
)
func StdErrFormat(logs []StructLog) {
fmt.Fprintf(os.Stderr, "VM STAT %d OPs\n", len(logs))
for _, log := range logs {
fmt.Fprintf(os.Stderr, "PC %08d: %s GAS: %v COST: %v", log.Pc, log.Op, log.Gas, log.GasCost)
if log.Err != nil {

283
core/vm/native.go Normal file
View file

@ -0,0 +1,283 @@
package vm
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/hashicorp/golang-lru"
)
var (
segments *lru.Cache
DisableSegmentation bool
)
func init() {
segments, _ = lru.New(256)
}
type codeSegments map[uint64]*segment
// operation is a "native" implementation of an OpCode
type operation struct {
fn func(*stack, []byte)
data []byte
}
type segment struct {
cstart, cend, next uint64
msize uint64
gas *big.Int
ssize int
ops []operation
}
func (c *segment) String() string {
return fmt.Sprintf("{start: %d end: %d ssize: %d gas: %v}", c.cstart, c.cend, c.ssize, c.gas)
}
// native operations
func opPush(s *stack, data []byte) {
s.push(common.BytesToBig(data))
}
func opAdd(s *stack, data []byte) {
s.push(U256(new(big.Int).Add(s.pop(), s.pop())))
}
func opSub(s *stack, data []byte) {
s.push(U256(new(big.Int).Sub(s.pop(), s.pop())))
}
func opMul(s *stack, data []byte) {
s.push(U256(new(big.Int).Mul(s.pop(), s.pop())))
}
func opDiv(s *stack, data []byte) {
x, y := s.pop(), s.pop()
if y.Cmp(common.Big0) != 0 {
new(big.Int).Div(x, y)
}
U256(new(big.Int))
// pop result back on the s
s.push(new(big.Int))
}
func opSdiv(s *stack, data []byte) {
x, y := S256(s.pop()), S256(s.pop())
if y.Cmp(common.Big0) == 0 {
new(big.Int).Set(common.Big0)
} else {
n := new(big.Int)
if new(big.Int).Mul(x, y).Cmp(common.Big0) < 0 {
n.SetInt64(-1)
} else {
n.SetInt64(1)
}
new(big.Int).Div(x.Abs(x), y.Abs(y)).Mul(new(big.Int), n)
U256(new(big.Int))
}
s.push(new(big.Int))
}
func opMod(s *stack, data []byte) {
x, y := s.pop(), s.pop()
if y.Cmp(common.Big0) == 0 {
new(big.Int).Set(common.Big0)
} else {
new(big.Int).Mod(x, y)
}
U256(new(big.Int))
s.push(new(big.Int))
}
func opSmod(s *stack, data []byte) {
x, y := S256(s.pop()), S256(s.pop())
if y.Cmp(common.Big0) == 0 {
new(big.Int).Set(common.Big0)
} else {
n := new(big.Int)
if x.Cmp(common.Big0) < 0 {
n.SetInt64(-1)
} else {
n.SetInt64(1)
}
new(big.Int).Mod(x.Abs(x), y.Abs(y)).Mul(new(big.Int), n)
U256(new(big.Int))
}
s.push(new(big.Int))
}
func opExp(s *stack, data []byte) {
x, y := s.pop(), s.pop()
new(big.Int).Exp(x, y, Pow256)
U256(new(big.Int))
s.push(new(big.Int))
}
func opSignextend(s *stack, data []byte) {
back := s.pop()
if back.Cmp(big.NewInt(31)) < 0 {
bit := uint(back.Uint64()*8 + 7)
num := s.pop()
mask := new(big.Int).Lsh(common.Big1, bit)
mask.Sub(mask, common.Big1)
if common.BitTest(num, int(bit)) {
num.Or(num, mask.Not(mask))
} else {
num.And(num, mask)
}
num = U256(num)
s.push(num)
}
}
func opNot(s *stack, data []byte) {
s.push(U256(new(big.Int).Not(s.pop())))
}
func opLt(s *stack, data []byte) {
x, y := s.pop(), s.pop()
// x < y
if x.Cmp(y) < 0 {
s.push(common.BigTrue)
} else {
s.push(common.BigFalse)
}
}
func opGt(s *stack, data []byte) {
x, y := s.pop(), s.pop()
// x > y
if x.Cmp(y) > 0 {
s.push(common.BigTrue)
} else {
s.push(common.BigFalse)
}
}
func opSlt(s *stack, data []byte) {
x, y := S256(s.pop()), S256(s.pop())
// x < y
if x.Cmp(S256(y)) < 0 {
s.push(common.BigTrue)
} else {
s.push(common.BigFalse)
}
}
func opSgt(s *stack, data []byte) {
x, y := S256(s.pop()), S256(s.pop())
// x > y
if x.Cmp(y) > 0 {
s.push(common.BigTrue)
} else {
s.push(common.BigFalse)
}
}
func opEq(s *stack, data []byte) {
x, y := s.pop(), s.pop()
// x == y
if x.Cmp(y) == 0 {
s.push(common.BigTrue)
} else {
s.push(common.BigFalse)
}
}
func opIszero(s *stack, data []byte) {
x := s.pop()
if x.Cmp(common.BigFalse) > 0 {
s.push(common.BigFalse)
} else {
s.push(common.BigTrue)
}
}
func opAnd(s *stack, data []byte) {
s.push(new(big.Int).And(s.pop(), s.pop()))
}
func opOr(s *stack, data []byte) {
s.push(new(big.Int).Or(s.pop(), s.pop()))
}
func opXor(s *stack, data []byte) {
s.push(new(big.Int).Xor(s.pop(), s.pop()))
}
func opByte(s *stack, data []byte) {
th, val := s.pop(), s.pop()
res := new(big.Int)
if th.Cmp(big.NewInt(32)) < 0 {
byt := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
res.Set(byt)
} else {
res.Set(common.BigFalse)
}
s.push(res)
}
func opAddmod(s *stack, data []byte) {
x := s.pop()
y := s.pop()
z := s.pop()
res := new(big.Int)
if z.Cmp(Zero) > 0 {
add := new(big.Int).Add(x, y)
res.Mod(add, z)
res = U256(res)
}
s.push(res)
}
func opMulmod(s *stack, data []byte) {
x := s.pop()
y := s.pop()
z := s.pop()
res := new(big.Int)
if z.Cmp(Zero) > 0 {
res.Mul(x, y)
res.Mod(res, z)
res = U256(res)
}
s.push(res)
}

View file

@ -332,3 +332,15 @@ func (o OpCode) String() string {
return str
}
func isDynamic(op OpCode) bool {
switch op {
case CREATE, CALL, CALLCODE, CALLDATACOPY, GAS, MLOAD, JUMP, JUMPI, SUICIDE, STOP, RETURN, EXTCODECOPY, CODECOPY, MSTORE, MSTORE8, SSTORE:
return true
}
if _, ok := opCodeToString[op]; !ok {
return true
}
return false
}

View file

@ -11,32 +11,26 @@ func newstack() *stack {
type stack struct {
data []*big.Int
ptr int
}
func (st *stack) Data() []*big.Int {
return st.data[:st.ptr]
return st.data
}
func (st *stack) push(d *big.Int) {
// NOTE push limit (1024) is checked in baseCheck
stackItem := new(big.Int).Set(d)
if len(st.data) > st.ptr {
st.data[st.ptr] = stackItem
} else {
st.data = append(st.data, stackItem)
}
st.ptr++
st.data = append(st.data, stackItem)
}
func (st *stack) pop() (ret *big.Int) {
st.ptr--
ret = st.data[st.ptr]
ret = st.data[len(st.data)-1]
st.data = st.data[:len(st.data)-1]
return
}
func (st *stack) len() int {
return st.ptr
return len(st.data)
}
func (st *stack) swap(n int) {

View file

@ -2,6 +2,7 @@ package vm
import (
"fmt"
"math"
"math/big"
"github.com/ethereum/go-ethereum/common"
@ -10,6 +11,8 @@ import (
"github.com/ethereum/go-ethereum/params"
)
var t int
// Vm implements VirtualMachine
type Vm struct {
env Environment
@ -37,6 +40,13 @@ func New(env Environment) *Vm {
func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
self.env.SetDepth(self.env.Depth() + 1)
defer self.env.SetDepth(self.env.Depth() - 1)
defer func() {
if r := recover(); r != nil {
StdErrFormat(self.env.StructLogs())
ret = nil
err = fmt.Errorf("%v", r)
}
}()
var (
caller = context.caller
@ -44,14 +54,24 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
value = context.value
price = context.Price
op OpCode // current opcode
codehash = crypto.Sha3Hash(code) // codehash is used when doing jump dest caching
mem = NewMemory() // bound memory
stack = newstack() // local stack
statedb = self.env.State() // current state
op OpCode // current opcode
codehash = crypto.FnvHash(code) // codehash is used when doing jump dest caching
mem = NewMemory() // bound memory
stack = newstack() // local stack
statedb = self.env.State() // current state
// For optimisation reason we're using uint64 as the program counter.
// It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Pratically much less so feasible.
pc = uint64(0) // program counter
pc = uint64(0) // program counter
ppc = uint64(0) // previous program counter
csegment *segment // current code segment
csegments codeSegments // program segments
nopay uint64 = 0
addop = func(op operation) {
if csegment != nil && !DisableSegmentation {
csegment.ops = append(csegment.ops, op)
}
}
// jump evaluates and checks whether the given jump destination is a valid one
// if valid move the `pc` otherwise return an error.
@ -69,13 +89,15 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
newMemSize *big.Int
cost *big.Int
)
if c, ok := segments.Get(codehash); ok {
csegments = c.(codeSegments)
} else {
csegments = make(codeSegments)
segments.Add(codehash, csegments)
}
// User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
defer func() {
if self.After != nil {
self.After(context, err)
}
if err != nil {
self.log(pc, op, context.Gas, cost, mem, stack, context, err)
@ -84,6 +106,16 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
ret = context.Return(nil)
}
if !DisableSegmentation && csegment != nil && csegment.cend == 0 {
if err != nil {
// delete this tracking segment. We couldn't fully determine the stats
delete(csegments, csegment.cstart)
} else {
csegment.cend = pc
csegment.ssize = int(math.Abs(math.Min(float64(stack.len()-csegment.ssize), 0)))
}
}
}()
if context.CodeAddr != nil {
@ -98,31 +130,100 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
}
for {
// The base for all big integer arithmetic
base := new(big.Int)
// Get the memory location of pc
cost = new(big.Int)
// Get the current operation location of pc
op = context.GetOp(pc)
if !DisableSegmentation {
if isDynamic(op) {
//fmt.Println("encountered dyn")
if csegment != nil && csegment.cend == 0 {
//fmt.Println("closing segment with", op, "@", ppc, csegment.gas)
// mark end of segment bound
csegment.cend = ppc
// set the next op for continuation
csegment.next = pc
// write out the required required elements on the stack. i.e. the amount of items
// required outside of the segment itself.
// 0: P, 1
// 1: J, 2
// 2: D -+
// 3: P, 2 |- M,0 = M,0 pushes 1. Requires 2. 1 outside bounds of M,0
// 4: ADD -+
// 5: J, 2
csegment.ssize = int(math.Abs(math.Min(float64(stack.len()-csegment.ssize), 0)))
}
csegment = nil
nopay = 0
} else if csegment == nil && pc != 0 {
// check if a segment is available to us and set it.
if seg, ok := csegments[pc]; ok {
//fmt.Println(pc, "using seg", seg)
nopay = seg.cend
// calculate the new memory size and gas price for the current executing opcode
newMemSize, cost, err = self.calculateGasAndSize(context, caller, op, statedb, mem, stack)
if err != nil {
return nil, err
// make sure there's enough stack items to complete the segment
if err := stack.require(seg.ssize); err != nil {
return context.Return(nil), err
}
cost.Set(seg.gas)
// use required gas for this segment
if !context.UseGas(seg.gas) {
context.UseGas(context.Gas)
return context.Return(nil), OutOfGasError{}
}
csegment = seg
/*
for _, operation := range seg.ops {
operation.fn(stack, operation.data)
}
pc = seg.next
continue
*/
} else {
//fmt.Println("creating", pc)
// allocate a new segment for tracking information
csegment = &segment{pc, 0, 0, 0, new(big.Int), stack.len(), nil}
csegments[pc] = csegment
nopay = 0
}
}
}
// set previous program counter
ppc = pc
// Use the calculated gas. When insufficient gas is present, use all gas and return an
// Out Of Gas error
if !context.UseGas(cost) {
//fmt.Println(pc, nopay, pc > nopay, pc == 0)
if pc > nopay || pc == 0 || DisableSegmentation {
// calculate the new memory size and gas price for the current executing opcode
newMemSize, cost, err = self.calculateGasAndSize(context, caller, op, statedb, mem, stack)
if err != nil {
return nil, err
}
context.UseGas(context.Gas)
if csegment != nil {
csegment.gas.Add(csegment.gas, cost)
}
// Use the calculated gas. When insufficient gas is present, use all gas and return an
// Out Of Gas error
if !context.UseGas(cost) {
context.UseGas(context.Gas)
return context.Return(nil), OutOfGasError{}
}
// Resize the memory calculated previously
mem.Resize(newMemSize.Uint64())
return context.Return(nil), OutOfGasError{}
}
// Resize the memory calculated previously
mem.Resize(newMemSize.Uint64())
// Add a log message
self.log(pc, op, context.Gas, cost, mem, stack, context, nil)
// The base for all big integer arithmetic
base := new(big.Int)
switch op {
case ADD:
x, y := stack.pop(), stack.pop()
@ -131,6 +232,8 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
U256(base)
addop(operation{opAdd, nil})
// pop result back on the stack
stack.push(base)
case SUB:
@ -145,12 +248,12 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
case MUL:
x, y := stack.pop(), stack.pop()
base.Mul(x, y)
res := new(big.Int).Mul(x, y)
U256(base)
U256(res)
// pop result back on the stack
stack.push(base)
stack.push(res)
case DIV:
x, y := stack.pop(), stack.pop()
@ -221,6 +324,8 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
U256(base)
stack.push(base)
addop(operation{opExp, nil})
case SIGNEXTEND:
back := stack.pop()
if back.Cmp(big.NewInt(31)) < 0 {
@ -348,7 +453,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
case SHA3:
offset, size := stack.pop(), stack.pop()
data := crypto.Sha3(mem.Get(offset.Int64(), size.Int64()))
data := crypto.Sha3(mem.GetPtr(offset.Int64(), size.Int64()))
stack.push(common.BigD(data))
@ -463,6 +568,9 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
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:
size := uint64(op - PUSH1 + 1)
byts := getData(code, new(big.Int).SetUint64(pc+1), new(big.Int).SetUint64(size))
addop(operation{opPush, byts})
// push value to stack
stack.push(common.Bytes2Big(byts))
pc += size
@ -491,7 +599,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
case MLOAD:
offset := stack.pop()
val := common.BigD(mem.Get(offset.Int64(), 32))
val := common.BigD(mem.GetPtr(offset.Int64(), 32))
stack.push(val)
case MSTORE:
@ -538,6 +646,10 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
case MSIZE:
stack.push(big.NewInt(int64(mem.Len())))
case GAS:
if t > 1 {
//return context.Return(nil), nil
}
t++
stack.push(context.Gas)
case CREATE:
@ -800,7 +912,9 @@ func (self *Vm) log(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, st
mem := make([]byte, len(memory.Data()))
copy(mem, memory.Data())
stck := make([]*big.Int, len(stack.Data()))
copy(stck, stack.Data())
for i, item := range stack.Data() {
stck[i] = new(big.Int).Set(item)
}
object := context.self.(*state.StateObject)
storage := make(map[common.Hash][]byte)
@ -808,7 +922,7 @@ func (self *Vm) log(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, st
storage[common.BytesToHash(k)] = v
})
self.env.AddStructLog(StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, err})
self.env.AddStructLog(StructLog{pc, op, new(big.Int).Set(gas), new(big.Int).Set(cost), mem, stck, storage, err})
}
}

View file

@ -8,6 +8,7 @@ import (
"crypto/rand"
"crypto/sha256"
"fmt"
"hash/fnv"
"io"
"io/ioutil"
"math/big"
@ -35,6 +36,15 @@ func init() {
secp256k1n = common.String2Big("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141")
}
func FnvHash(data ...[]byte) (h common.Hash) {
fnvHash := fnv.New32()
for _, b := range data {
fnvHash.Write(b)
}
fnvHash.Sum(h[:0])
return h
}
func Sha3(data ...[]byte) []byte {
d := sha3.NewKeccak256()
for _, b := range data {

View file

@ -50,6 +50,13 @@ func runStateTests(tests map[string]VmTest, skipTests []string) error {
}
for name, test := range tests {
//vm.Debug = true
//vm.DisableSegmentation = true
/*
if name != "Call10" {
continue
}
*/
if skipTest[name] {
glog.Infoln("Skipping state test", name)
return nil
@ -91,9 +98,7 @@ func runStateTest(test VmTest) error {
}
var (
ret []byte
// gas *big.Int
// err error
ret []byte
logs state.Logs
)
@ -113,7 +118,7 @@ func runStateTest(test VmTest) error {
}
if obj.Balance().Cmp(common.Big(account.Balance)) != 0 {
return fmt.Errorf("(%x) balance failed. Expected %v, got %v => %v\n", obj.Address().Bytes()[:4], account.Balance, obj.Balance(), new(big.Int).Sub(common.Big(account.Balance), obj.Balance()))
return fmt.Errorf("(%x) balance failed. Expected %v, got %v => %v\n", obj.Address().Bytes()[:4], common.Big(account.Balance), obj.Balance(), new(big.Int).Sub(common.Big(account.Balance), obj.Balance()))
}
if obj.Nonce() != common.String2Big(account.Nonce).Uint64() {