diff --git a/ethchain/closure.go b/ethchain/closure.go index 3d15f2a990..de21964996 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -52,7 +52,7 @@ func (c *Closure) Get(x *big.Int) *ethutil.Value { } func (c *Closure) Gets(x, y *big.Int) *ethutil.Value { - if x.Int64() > int64(len(c.Script)) || y.Int64() > int64(len(c.Script)) { + if x.Int64() >= int64(len(c.Script)) || y.Int64() >= int64(len(c.Script)) { return ethutil.NewValue(0) } @@ -69,7 +69,7 @@ func (c *Closure) Address() []byte { return c.object.Address() } -type DebugHook func(op OpCode) +type DebugHook func(op OpCode, mem *Memory, stack *Stack) func (c *Closure) Call(vm *Vm, args []byte, hook DebugHook) []byte { c.Args = args diff --git a/ethchain/contract.go b/ethchain/contract.go index e99e413f7a..af348667c3 100644 --- a/ethchain/contract.go +++ b/ethchain/contract.go @@ -69,6 +69,14 @@ func (c *Contract) Address() []byte { return c.address } +func (c *Contract) Script() []byte { + return c.script +} + +func (c *Contract) Init() []byte { + return c.initScript +} + func (c *Contract) RlpEncode() []byte { return ethutil.Encode([]interface{}{c.Amount, c.Nonce, c.state.trie.Root, c.script, c.initScript}) } diff --git a/ethchain/stack.go b/ethchain/stack.go index d475f2f8e0..2aca0a3500 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -173,6 +173,10 @@ func NewStack() *Stack { return &Stack{} } +func (st *Stack) Data() []*big.Int { + return st.data +} + func (st *Stack) Pop() *big.Int { str := st.data[len(st.data)-1] @@ -246,6 +250,10 @@ func (m *Memory) Len() int { return len(m.store) } +func (m *Memory) Data() []byte { + return m.store +} + func (m *Memory) Print() { fmt.Printf("### mem %d bytes ###\n", len(m.store)) if len(m.store) > 0 { diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 111d2c019e..549d599594 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -318,14 +318,14 @@ func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, blo caller := sm.procState.GetAccount(tx.Sender()) closure := NewClosure(caller, contract, contract.script, sm.procState, tx.Gas, tx.Value) vm := NewVm(sm.procState, RuntimeVars{ - origin: caller.Address(), - blockNumber: block.BlockInfo().Number, - prevHash: block.PrevHash, - coinbase: block.Coinbase, - time: block.Time, - diff: block.Difficulty, + Origin: caller.Address(), + BlockNumber: block.BlockInfo().Number, + PrevHash: block.PrevHash, + Coinbase: block.Coinbase, + Time: block.Time, + Diff: block.Difficulty, // XXX Tx data? Could be just an argument to the closure instead - txData: nil, + TxData: nil, }) closure.Call(vm, nil, nil) diff --git a/ethchain/vm.go b/ethchain/vm.go index dce972cc73..fbe0d0439b 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -2,7 +2,7 @@ package ethchain import ( _ "bytes" - _ "fmt" + "fmt" "github.com/ethereum/eth-go/ethutil" _ "github.com/obscuren/secp256k1-go" _ "math" @@ -33,13 +33,13 @@ type Vm struct { } type RuntimeVars struct { - origin []byte - blockNumber uint64 - prevHash []byte - coinbase []byte - time int64 - diff *big.Int - txData []string + Origin []byte + BlockNumber uint64 + PrevHash []byte + Coinbase []byte + Time int64 + Diff *big.Int + TxData []string } func NewVm(state *State, vars RuntimeVars) *Vm { @@ -65,9 +65,11 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { // The base for all big integer arithmetic base := new(big.Int) - if ethutil.Config.Debug { - ethutil.Config.Log.Debugf("# op\n") - } + /* + if ethutil.Config.Debug { + ethutil.Config.Log.Debugf("# op\n") + } + */ for { step++ @@ -75,9 +77,11 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { val := closure.Get(pc) // Get the opcode (it must be an opcode!) op := OpCode(val.Uint()) - if ethutil.Config.Debug { - ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) - } + /* + if ethutil.Config.Debug { + ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) + } + */ // TODO Get each instruction cost properly gas := new(big.Int) @@ -270,7 +274,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { case oBALANCE: stack.Push(closure.Value) case oORIGIN: - stack.Push(ethutil.BigD(vm.vars.origin)) + stack.Push(ethutil.BigD(vm.vars.Origin)) case oCALLER: stack.Push(ethutil.BigD(closure.Callee().Address())) case oCALLVALUE: @@ -286,15 +290,15 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { // 0x40 range case oPREVHASH: - stack.Push(ethutil.BigD(vm.vars.prevHash)) + stack.Push(ethutil.BigD(vm.vars.PrevHash)) case oCOINBASE: - stack.Push(ethutil.BigD(vm.vars.coinbase)) + stack.Push(ethutil.BigD(vm.vars.Coinbase)) case oTIMESTAMP: - stack.Push(big.NewInt(vm.vars.time)) + stack.Push(big.NewInt(vm.vars.Time)) case oNUMBER: - stack.Push(big.NewInt(int64(vm.vars.blockNumber))) + stack.Push(big.NewInt(int64(vm.vars.BlockNumber))) case oDIFFICULTY: - stack.Push(vm.vars.diff) + stack.Push(vm.vars.Diff) case oGASLIMIT: // TODO @@ -406,7 +410,59 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { pc.Add(pc, ethutil.Big1) if hook != nil { - hook(op) + hook(op, mem, stack) + } + } +} + +func Disassemble(script []byte) (asm []string) { + pc := new(big.Int) + for { + if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 { + return + } + + // Get the memory location of pc + val := script[pc.Int64()] + // Get the opcode (it must be an opcode!) + op := OpCode(val) + + asm = append(asm, fmt.Sprintf("%v", op)) + + switch op { + case oPUSH: // Push PC+1 on to the stack + pc.Add(pc, ethutil.Big1) + data := script[pc.Int64() : pc.Int64()+32] + val := ethutil.BigD(data) + + var b []byte + if val.Int64() == 0 { + b = []byte{0} + } else { + b = val.Bytes() + } + + asm = append(asm, fmt.Sprintf("0x%x", b)) + + pc.Add(pc, big.NewInt(31)) + case oPUSH20: + pc.Add(pc, ethutil.Big1) + data := script[pc.Int64() : pc.Int64()+20] + val := ethutil.BigD(data) + var b []byte + if val.Int64() == 0 { + b = []byte{0} + } else { + b = val.Bytes() + } + + asm = append(asm, fmt.Sprintf("0x%x", b)) + + pc.Add(pc, big.NewInt(19)) } + + pc.Add(pc, ethutil.Big1) } + + return } diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 5a1419c0f9..a0add95328 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -130,14 +130,29 @@ func TestRun4(t *testing.T) { callerClosure := NewClosure(account, c, c.script, state, big.NewInt(1000000000), new(big.Int)) vm := NewVm(state, RuntimeVars{ - origin: account.Address(), - blockNumber: 1, - prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"), - coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), - time: 1, - diff: big.NewInt(256), + Origin: account.Address(), + BlockNumber: 1, + PrevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"), + Coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), + Time: 1, + Diff: big.NewInt(256), // XXX Tx data? Could be just an argument to the closure instead - txData: nil, + TxData: nil, }) callerClosure.Call(vm, nil, nil) } + +func TestRun5(t *testing.T) { + ethutil.ReadConfig("") + + asm, _ := mutan.Compile(strings.NewReader(` + int32 a = 10 + int32 b = 20 + if a > b { + int32 c = this.caller() + } + exit() + `), false) + script := ethutil.Assemble(asm...) + fmt.Println(Disassemble(script)) +}