github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/core/asm/asm.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 // Provides support for dealing with EVM assembly instructions (e.g., disassembling them). 19 package asm 20 21 import ( 22 "encoding/hex" 23 "fmt" 24 25 "github.com/AigarNetwork/aigar/core/vm" 26 ) 27 28 // Iterator for disassembled EVM instructions 29 type instructionIterator struct { 30 code []byte 31 pc uint64 32 arg []byte 33 op vm.OpCode 34 error error 35 started bool 36 } 37 38 // Create a new instruction iterator. 39 func NewInstructionIterator(code []byte) *instructionIterator { 40 it := new(instructionIterator) 41 it.code = code 42 return it 43 } 44 45 // Returns true if there is a next instruction and moves on. 46 func (it *instructionIterator) Next() bool { 47 if it.error != nil || uint64(len(it.code)) <= it.pc { 48 // We previously reached an error or the end. 49 return false 50 } 51 52 if it.started { 53 // Since the iteration has been already started we move to the next instruction. 54 if it.arg != nil { 55 it.pc += uint64(len(it.arg)) 56 } 57 it.pc++ 58 } else { 59 // We start the iteration from the first instruction. 60 it.started = true 61 } 62 63 if uint64(len(it.code)) <= it.pc { 64 // We reached the end. 65 return false 66 } 67 68 it.op = vm.OpCode(it.code[it.pc]) 69 if it.op.IsPush() { 70 a := uint64(it.op) - uint64(vm.PUSH1) + 1 71 u := it.pc + 1 + a 72 if uint64(len(it.code)) <= it.pc || uint64(len(it.code)) < u { 73 it.error = fmt.Errorf("incomplete push instruction at %v", it.pc) 74 return false 75 } 76 it.arg = it.code[it.pc+1 : u] 77 } else { 78 it.arg = nil 79 } 80 return true 81 } 82 83 // Returns any error that may have been encountered. 84 func (it *instructionIterator) Error() error { 85 return it.error 86 } 87 88 // Returns the PC of the current instruction. 89 func (it *instructionIterator) PC() uint64 { 90 return it.pc 91 } 92 93 // Returns the opcode of the current instruction. 94 func (it *instructionIterator) Op() vm.OpCode { 95 return it.op 96 } 97 98 // Returns the argument of the current instruction. 99 func (it *instructionIterator) Arg() []byte { 100 return it.arg 101 } 102 103 // Pretty-print all disassembled EVM instructions to stdout. 104 func PrintDisassembled(code string) error { 105 script, err := hex.DecodeString(code) 106 if err != nil { 107 return err 108 } 109 110 it := NewInstructionIterator(script) 111 for it.Next() { 112 if it.Arg() != nil && 0 < len(it.Arg()) { 113 fmt.Printf("%05x: %v 0x%x\n", it.PC(), it.Op(), it.Arg()) 114 } else { 115 fmt.Printf("%05x: %v\n", it.PC(), it.Op()) 116 } 117 } 118 return it.Error() 119 } 120 121 // Return all disassembled EVM instructions in human-readable format. 122 func Disassemble(script []byte) ([]string, error) { 123 instrs := make([]string, 0) 124 125 it := NewInstructionIterator(script) 126 for it.Next() { 127 if it.Arg() != nil && 0 < len(it.Arg()) { 128 instrs = append(instrs, fmt.Sprintf("%05x: %v 0x%x\n", it.PC(), it.Op(), it.Arg())) 129 } else { 130 instrs = append(instrs, fmt.Sprintf("%05x: %v\n", it.PC(), it.Op())) 131 } 132 } 133 if err := it.Error(); err != nil { 134 return nil, err 135 } 136 return instrs, nil 137 }