mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 21:26:42 +00:00
cmd/vm: code segmenting
This commit is contained in:
parent
be935bff84
commit
cb7cd07ed0
9 changed files with 237 additions and 81 deletions
134
cmd/evm/main.go
134
cmd/evm/main.go
|
|
@ -22,79 +22,153 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
code = flag.String("code", "", "evm code")
|
app *cli.App
|
||||||
loglevel = flag.Int("log", 4, "log level")
|
DebugFlag = cli.BoolFlag{
|
||||||
gas = flag.String("gas", "1000000000", "gas amount")
|
Name: "debug",
|
||||||
price = flag.String("price", "0", "gas price")
|
Usage: "output full trace logs",
|
||||||
value = flag.String("value", "0", "tx value")
|
}
|
||||||
dump = flag.Bool("dump", false, "dump state after run")
|
CodeFlag = cli.StringFlag{
|
||||||
data = flag.String("data", "", "data")
|
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{}) {
|
func init() {
|
||||||
fmt.Println(v...)
|
app = utils.NewApp("0.2", "the evm command line interface")
|
||||||
//os.Exit(1)
|
app.Flags = []cli.Flag{
|
||||||
|
DebugFlag,
|
||||||
|
SysStatFlag,
|
||||||
|
CodeFlag,
|
||||||
|
GasFlag,
|
||||||
|
PriceFlag,
|
||||||
|
ValueFlag,
|
||||||
|
DumpFlag,
|
||||||
|
InputFlag,
|
||||||
|
}
|
||||||
|
app.Action = run
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func run(ctx *cli.Context) {
|
||||||
flag.Parse()
|
vm.Debug = ctx.GlobalBool(DebugFlag.Name)
|
||||||
|
|
||||||
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.LogLevel(*loglevel)))
|
|
||||||
|
|
||||||
vm.Debug = true
|
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb := state.New(common.Hash{}, db)
|
statedb := state.New(common.Hash{}, db)
|
||||||
sender := statedb.CreateAccount(common.StringToAddress("sender"))
|
sender := statedb.CreateAccount(common.StringToAddress("sender"))
|
||||||
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
|
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()
|
tstart := time.Now()
|
||||||
|
|
||||||
ret, e := vmenv.Call(sender, receiver.Address(), common.Hex2Bytes(*data), common.Big(*gas), common.Big(*price), common.Big(*value))
|
vmstart := 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)),
|
||||||
|
)
|
||||||
|
|
||||||
logger.Flush()
|
|
||||||
if e != nil {
|
if e != nil {
|
||||||
perr(e)
|
fmt.Println(e)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
fmt.Println("no analysis", time.Since(vmstart))
|
||||||
|
|
||||||
if *dump {
|
vmstart = 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)),
|
||||||
|
)
|
||||||
|
|
||||||
|
if e != nil {
|
||||||
|
fmt.Println(e)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println("with analysis", time.Since(vmstart))
|
||||||
|
|
||||||
|
if ctx.GlobalBool(DumpFlag.Name) {
|
||||||
fmt.Println(string(statedb.Dump()))
|
fmt.Println(string(statedb.Dump()))
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.StdErrFormat(vmenv.StructLogs())
|
vm.StdErrFormat(vmenv.StructLogs())
|
||||||
|
|
||||||
var mem runtime.MemStats
|
if ctx.GlobalBool(SysStatFlag.Name) {
|
||||||
runtime.ReadMemStats(&mem)
|
var mem runtime.MemStats
|
||||||
fmt.Printf("vm took %v\n", time.Since(tstart))
|
runtime.ReadMemStats(&mem)
|
||||||
fmt.Printf(`alloc: %d
|
fmt.Printf("vm took %v\n", time.Since(tstart))
|
||||||
|
fmt.Printf(`alloc: %d
|
||||||
tot alloc: %d
|
tot alloc: %d
|
||||||
no. malloc: %d
|
no. malloc: %d
|
||||||
heap alloc: %d
|
heap alloc: %d
|
||||||
heap objs: %d
|
heap objs: %d
|
||||||
num gc: %d
|
num gc: %d
|
||||||
`, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC)
|
`, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Printf("%x\n", ret)
|
fmt.Printf("OUT: 0x%x\n", ret)
|
||||||
|
|
||||||
|
for codehash, chunks := range vm.Chunks {
|
||||||
|
for _, chunk := range chunks {
|
||||||
|
fmt.Printf("%x => %v\n", codehash, chunk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := app.Run(os.Args); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type VMEnv struct {
|
type VMEnv struct {
|
||||||
|
|
|
||||||
|
|
@ -243,7 +243,7 @@ func (self *StateObject) AddGas(gas, price *big.Int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateObject) Copy() *StateObject {
|
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.balance.Set(self.balance)
|
||||||
stateObject.codeHash = common.CopyBytes(self.codeHash)
|
stateObject.codeHash = common.CopyBytes(self.codeHash)
|
||||||
stateObject.nonce = self.nonce
|
stateObject.nonce = self.nonce
|
||||||
|
|
|
||||||
|
|
@ -26,16 +26,25 @@ type Context struct {
|
||||||
Args []byte
|
Args []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dests destinations
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dests = make(destinations)
|
||||||
|
}
|
||||||
|
|
||||||
// Create a new context for the given data items.
|
// Create a new context for the given data items.
|
||||||
func NewContext(caller ContextRef, object ContextRef, value, gas, price *big.Int) *Context {
|
func NewContext(caller ContextRef, object ContextRef, value, gas, price *big.Int) *Context {
|
||||||
c := &Context{caller: caller, self: object, Args: nil}
|
c := &Context{caller: caller, self: object, Args: nil}
|
||||||
|
|
||||||
if parent, ok := caller.(*Context); ok {
|
c.jumpdests = dests
|
||||||
// Reuse JUMPDEST analysis from parent context if available.
|
/*
|
||||||
c.jumpdests = parent.jumpdests
|
if parent, ok := caller.(*Context); ok {
|
||||||
} else {
|
// Reuse JUMPDEST analysis from parent context if available.
|
||||||
c.jumpdests = make(destinations)
|
c.jumpdests = parent.jumpdests
|
||||||
}
|
} else {
|
||||||
|
c.jumpdests = make(destinations)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// Gas should be a pointer so it can safely be reduced through the run
|
// Gas should be a pointer so it can safely be reduced through the run
|
||||||
// This pointer will be off the state transition
|
// This pointer will be off the state transition
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,8 @@ func baseCheck(op OpCode, stack *stack, gas *big.Int) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.stackPush > 0 && len(stack.data)-r.stackPop+r.stackPush > int(params.StackLimit.Int64())+1 {
|
if r.stackPush > 0 && stack.len()-r.stackPop+r.stackPush > int(params.StackLimit.Int64()) {
|
||||||
return fmt.Errorf("stack limit reached %d (%d)", len(stack.data), params.StackLimit.Int64())
|
return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64())
|
||||||
}
|
}
|
||||||
|
|
||||||
gas.Add(gas, r.gas)
|
gas.Add(gas, r.gas)
|
||||||
|
|
|
||||||
|
|
@ -332,3 +332,15 @@ func (o OpCode) String() string {
|
||||||
|
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isDynamic(op OpCode) bool {
|
||||||
|
switch op {
|
||||||
|
case CREATE, CALL, CALLCODE, JUMP, JUMPI, SUICIDE, STOP, RETURN, EXTCODECOPY, CODECOPY, MSTORE, MSTORE8:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := opCodeToString[op]; !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,32 +11,26 @@ func newstack() *stack {
|
||||||
|
|
||||||
type stack struct {
|
type stack struct {
|
||||||
data []*big.Int
|
data []*big.Int
|
||||||
ptr int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *stack) Data() []*big.Int {
|
func (st *stack) Data() []*big.Int {
|
||||||
return st.data[:st.ptr]
|
return st.data
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *stack) push(d *big.Int) {
|
func (st *stack) push(d *big.Int) {
|
||||||
// NOTE push limit (1024) is checked in baseCheck
|
// NOTE push limit (1024) is checked in baseCheck
|
||||||
stackItem := new(big.Int).Set(d)
|
stackItem := new(big.Int).Set(d)
|
||||||
if len(st.data) > st.ptr {
|
st.data = append(st.data, stackItem)
|
||||||
st.data[st.ptr] = stackItem
|
|
||||||
} else {
|
|
||||||
st.data = append(st.data, stackItem)
|
|
||||||
}
|
|
||||||
st.ptr++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *stack) pop() (ret *big.Int) {
|
func (st *stack) pop() (ret *big.Int) {
|
||||||
st.ptr--
|
ret = st.data[len(st.data)-1]
|
||||||
ret = st.data[st.ptr]
|
st.data = st.data[:len(st.data)-1]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *stack) len() int {
|
func (st *stack) len() int {
|
||||||
return st.ptr
|
return len(st.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *stack) swap(n int) {
|
func (st *stack) swap(n int) {
|
||||||
|
|
|
||||||
113
core/vm/vm.go
113
core/vm/vm.go
|
|
@ -8,8 +8,27 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/hashicorp/golang-lru"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type segment struct {
|
||||||
|
cstart, cend uint64
|
||||||
|
msize uint64
|
||||||
|
gas *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *segment) String() string {
|
||||||
|
return fmt.Sprintf("{%d %d %d %v}", c.cstart, c.cend, c.msize, c.gas)
|
||||||
|
}
|
||||||
|
|
||||||
|
type codeSegments map[uint64]*segment
|
||||||
|
|
||||||
|
var segments *lru.Cache
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
segments, _ = lru.New(256)
|
||||||
|
}
|
||||||
|
|
||||||
// Vm implements VirtualMachine
|
// Vm implements VirtualMachine
|
||||||
type Vm struct {
|
type Vm struct {
|
||||||
env Environment
|
env Environment
|
||||||
|
|
@ -44,14 +63,19 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||||
value = context.value
|
value = context.value
|
||||||
price = context.Price
|
price = context.Price
|
||||||
|
|
||||||
op OpCode // current opcode
|
op OpCode // current opcode
|
||||||
codehash = crypto.Sha3Hash(code) // codehash is used when doing jump dest caching
|
codehash = crypto.FnvHash(code) // codehash is used when doing jump dest caching
|
||||||
mem = NewMemory() // bound memory
|
mem = NewMemory() // bound memory
|
||||||
stack = newstack() // local stack
|
stack = newstack() // local stack
|
||||||
statedb = self.env.State() // current state
|
statedb = self.env.State() // current state
|
||||||
// For optimisation reason we're using uint64 as the program counter.
|
// 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.
|
// 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
|
||||||
|
disableOptim bool
|
||||||
|
|
||||||
// jump evaluates and checks whether the given jump destination is a valid one
|
// jump evaluates and checks whether the given jump destination is a valid one
|
||||||
// if valid move the `pc` otherwise return an error.
|
// if valid move the `pc` otherwise return an error.
|
||||||
|
|
@ -69,13 +93,15 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||||
newMemSize *big.Int
|
newMemSize *big.Int
|
||||||
cost *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.
|
// User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
|
||||||
defer func() {
|
defer func() {
|
||||||
if self.After != nil {
|
|
||||||
self.After(context, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
self.log(pc, op, context.Gas, cost, mem, stack, context, err)
|
self.log(pc, op, context.Gas, cost, mem, stack, context, err)
|
||||||
|
|
||||||
|
|
@ -84,6 +110,13 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||||
|
|
||||||
ret = context.Return(nil)
|
ret = context.Return(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if csegment != nil && err != nil && csegment.cend == 0 {
|
||||||
|
// delete this tracking segment. We couldn't fully determine the stats
|
||||||
|
delete(csegments, csegment.cstart)
|
||||||
|
} else if csegment != nil && err == nil && csegment.cend == 0 {
|
||||||
|
csegment.cend = pc
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if context.CodeAddr != nil {
|
if context.CodeAddr != nil {
|
||||||
|
|
@ -98,31 +131,57 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// The base for all big integer arithmetic
|
// Get the current operation location of pc
|
||||||
base := new(big.Int)
|
|
||||||
|
|
||||||
// Get the memory location of pc
|
|
||||||
op = context.GetOp(pc)
|
op = context.GetOp(pc)
|
||||||
|
if isDynamic(op) && csegment != nil {
|
||||||
|
csegment.cend = ppc
|
||||||
|
csegment = nil
|
||||||
|
nopay = 0
|
||||||
|
} else if csegment == nil && pc != 0 {
|
||||||
|
// allocate a new segment for tracking information
|
||||||
|
csegment = &segment{pc, 0, 0, new(big.Int)}
|
||||||
|
csegments[pc] = csegment
|
||||||
|
} else {
|
||||||
|
if segment, ok := csegments[pc]; ok && !disableOptim {
|
||||||
|
nopay = segment.cend
|
||||||
|
if !context.UseGas(segment.gas) {
|
||||||
|
context.UseGas(context.Gas)
|
||||||
|
|
||||||
// calculate the new memory size and gas price for the current executing opcode
|
return context.Return(nil), OutOfGasError{}
|
||||||
newMemSize, cost, err = self.calculateGasAndSize(context, caller, op, statedb, mem, stack)
|
}
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the calculated gas. When insufficient gas is present, use all gas and return an
|
if pc > nopay || pc == 0 || disableOptim {
|
||||||
// Out Of Gas error
|
// calculate the new memory size and gas price for the current executing opcode
|
||||||
if !context.UseGas(cost) {
|
newMemSize, cost, err = self.calculateGasAndSize(context, caller, op, statedb, mem, stack)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
context.UseGas(context.Gas)
|
// 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{}
|
return context.Return(nil), OutOfGasError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize the memory calculated previously
|
||||||
|
mem.Resize(newMemSize.Uint64())
|
||||||
|
|
||||||
|
if csegment != nil {
|
||||||
|
csegment.msize = uint64(len(mem.store))
|
||||||
|
csegment.gas.Add(csegment.gas, cost)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Resize the memory calculated previously
|
|
||||||
mem.Resize(newMemSize.Uint64())
|
|
||||||
// Add a log message
|
// Add a log message
|
||||||
self.log(pc, op, context.Gas, cost, mem, stack, context, nil)
|
self.log(pc, op, context.Gas, cost, mem, stack, context, nil)
|
||||||
|
|
||||||
|
ppc = pc
|
||||||
|
// The base for all big integer arithmetic
|
||||||
|
base := new(big.Int)
|
||||||
|
|
||||||
switch op {
|
switch op {
|
||||||
case ADD:
|
case ADD:
|
||||||
x, y := stack.pop(), stack.pop()
|
x, y := stack.pop(), stack.pop()
|
||||||
|
|
@ -348,7 +407,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||||
|
|
||||||
case SHA3:
|
case SHA3:
|
||||||
offset, size := stack.pop(), stack.pop()
|
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))
|
stack.push(common.BigD(data))
|
||||||
|
|
||||||
|
|
@ -491,7 +550,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||||
|
|
||||||
case MLOAD:
|
case MLOAD:
|
||||||
offset := stack.pop()
|
offset := stack.pop()
|
||||||
val := common.BigD(mem.Get(offset.Int64(), 32))
|
val := common.BigD(mem.GetPtr(offset.Int64(), 32))
|
||||||
stack.push(val)
|
stack.push(val)
|
||||||
|
|
||||||
case MSTORE:
|
case MSTORE:
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"hash/fnv"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -35,6 +36,15 @@ func init() {
|
||||||
secp256k1n = common.String2Big("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141")
|
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 {
|
func Sha3(data ...[]byte) []byte {
|
||||||
d := sha3.NewKeccak256()
|
d := sha3.NewKeccak256()
|
||||||
for _, b := range data {
|
for _, b := range data {
|
||||||
|
|
|
||||||
|
|
@ -91,9 +91,7 @@ func runStateTest(test VmTest) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ret []byte
|
ret []byte
|
||||||
// gas *big.Int
|
|
||||||
// err error
|
|
||||||
logs state.Logs
|
logs state.Logs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -113,7 +111,7 @@ func runStateTest(test VmTest) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if obj.Balance().Cmp(common.Big(account.Balance)) != 0 {
|
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() {
|
if obj.Nonce() != common.String2Big(account.Nonce).Uint64() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue