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