github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/core/asm/asm.go (about) 1 // Provides support for dealing with EVM assembly instructions (e.g., disassembling them). 2 package asm 3 4 import ( 5 "encoding/hex" 6 "fmt" 7 8 "github.com/quickchainproject/quickchain/core/vm" 9 ) 10 11 // Iterator for disassembled EVM instructions 12 type instructionIterator struct { 13 code []byte 14 pc uint64 15 arg []byte 16 op vm.OpCode 17 error error 18 started bool 19 } 20 21 // Create a new instruction iterator. 22 func NewInstructionIterator(code []byte) *instructionIterator { 23 it := new(instructionIterator) 24 it.code = code 25 return it 26 } 27 28 // Returns true if there is a next instruction and moves on. 29 func (it *instructionIterator) Next() bool { 30 if it.error != nil || uint64(len(it.code)) <= it.pc { 31 // We previously reached an error or the end. 32 return false 33 } 34 35 if it.started { 36 // Since the iteration has been already started we move to the next instruction. 37 if it.arg != nil { 38 it.pc += uint64(len(it.arg)) 39 } 40 it.pc++ 41 } else { 42 // We start the iteration from the first instruction. 43 it.started = true 44 } 45 46 if uint64(len(it.code)) <= it.pc { 47 // We reached the end. 48 return false 49 } 50 51 it.op = vm.OpCode(it.code[it.pc]) 52 if it.op.IsPush() { 53 a := uint64(it.op) - uint64(vm.PUSH1) + 1 54 u := it.pc + 1 + a 55 if uint64(len(it.code)) <= it.pc || uint64(len(it.code)) < u { 56 it.error = fmt.Errorf("incomplete push instruction at %v", it.pc) 57 return false 58 } 59 it.arg = it.code[it.pc+1 : u] 60 } else { 61 it.arg = nil 62 } 63 return true 64 } 65 66 // Returns any error that may have been encountered. 67 func (it *instructionIterator) Error() error { 68 return it.error 69 } 70 71 // Returns the PC of the current instruction. 72 func (it *instructionIterator) PC() uint64 { 73 return it.pc 74 } 75 76 // Returns the opcode of the current instruction. 77 func (it *instructionIterator) Op() vm.OpCode { 78 return it.op 79 } 80 81 // Returns the argument of the current instruction. 82 func (it *instructionIterator) Arg() []byte { 83 return it.arg 84 } 85 86 // Pretty-print all disassembled EVM instructions to stdout. 87 func PrintDisassembled(code string) error { 88 script, err := hex.DecodeString(code) 89 if err != nil { 90 return err 91 } 92 93 it := NewInstructionIterator(script) 94 for it.Next() { 95 if it.Arg() != nil && 0 < len(it.Arg()) { 96 fmt.Printf("%06v: %v 0x%x\n", it.PC(), it.Op(), it.Arg()) 97 } else { 98 fmt.Printf("%06v: %v\n", it.PC(), it.Op()) 99 } 100 } 101 return it.Error() 102 } 103 104 // Return all disassembled EVM instructions in human-readable format. 105 func Disassemble(script []byte) ([]string, error) { 106 instrs := make([]string, 0) 107 108 it := NewInstructionIterator(script) 109 for it.Next() { 110 if it.Arg() != nil && 0 < len(it.Arg()) { 111 instrs = append(instrs, fmt.Sprintf("%06v: %v 0x%x\n", it.PC(), it.Op(), it.Arg())) 112 } else { 113 instrs = append(instrs, fmt.Sprintf("%06v: %v\n", it.PC(), it.Op())) 114 } 115 } 116 if err := it.Error(); err != nil { 117 return nil, err 118 } 119 return instrs, nil 120 }