github.com/klaytn/klaytn@v1.10.2/blockchain/asm/asm.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2017 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum 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-ethereum 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-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from core/asm/asm.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package asm 22 23 import ( 24 "encoding/hex" 25 "fmt" 26 27 "github.com/klaytn/klaytn/blockchain/vm" 28 ) 29 30 // Iterator for disassembled EVM instructions 31 type instructionIterator struct { 32 code []byte 33 pc uint64 34 arg []byte 35 op vm.OpCode 36 error error 37 started bool 38 } 39 40 // Create a new instruction iterator. 41 func NewInstructionIterator(code []byte) *instructionIterator { 42 it := new(instructionIterator) 43 it.code = code 44 return it 45 } 46 47 // Returns true if there is a next instruction and moves on. 48 func (it *instructionIterator) Next() bool { 49 if it.error != nil || uint64(len(it.code)) <= it.pc { 50 // We previously reached an error or the end. 51 return false 52 } 53 54 if it.started { 55 // Since the iteration has been already started we move to the next instruction. 56 if it.arg != nil { 57 it.pc += uint64(len(it.arg)) 58 } 59 it.pc++ 60 } else { 61 // We start the iteration from the first instruction. 62 it.started = true 63 } 64 65 if uint64(len(it.code)) <= it.pc { 66 // We reached the end. 67 return false 68 } 69 70 it.op = vm.OpCode(it.code[it.pc]) 71 if it.op.IsPush() { 72 a := uint64(it.op) - uint64(vm.PUSH1) + 1 73 u := it.pc + 1 + a 74 if uint64(len(it.code)) <= it.pc || uint64(len(it.code)) < u { 75 it.error = fmt.Errorf("incomplete push instruction at %v", it.pc) 76 return false 77 } 78 it.arg = it.code[it.pc+1 : u] 79 } else { 80 it.arg = nil 81 } 82 return true 83 } 84 85 // Returns any error that may have been encountered. 86 func (it *instructionIterator) Error() error { 87 return it.error 88 } 89 90 // Returns the PC of the current instruction. 91 func (it *instructionIterator) PC() uint64 { 92 return it.pc 93 } 94 95 // Returns the opcode of the current instruction. 96 func (it *instructionIterator) Op() vm.OpCode { 97 return it.op 98 } 99 100 // Returns the argument of the current instruction. 101 func (it *instructionIterator) Arg() []byte { 102 return it.arg 103 } 104 105 // Pretty-print all disassembled EVM instructions to stdout. 106 func PrintDisassembled(code string) error { 107 script, err := hex.DecodeString(code) 108 if err != nil { 109 return err 110 } 111 112 it := NewInstructionIterator(script) 113 for it.Next() { 114 if it.Arg() != nil && 0 < len(it.Arg()) { 115 fmt.Printf("0x%06x: %v 0x%x\n", it.PC(), it.Op(), it.Arg()) 116 } else { 117 fmt.Printf("0x%06x: %v\n", it.PC(), it.Op()) 118 } 119 } 120 return it.Error() 121 } 122 123 // Return all disassembled EVM instructions in human-readable format. 124 func Disassemble(script []byte) ([]string, error) { 125 instrs := make([]string, 0) 126 127 it := NewInstructionIterator(script) 128 for it.Next() { 129 if it.Arg() != nil && 0 < len(it.Arg()) { 130 instrs = append(instrs, fmt.Sprintf("0x%06x: %v 0x%x\n", it.PC(), it.Op(), it.Arg())) 131 } else { 132 instrs = append(instrs, fmt.Sprintf("0x%06x: %v\n", it.PC(), it.Op())) 133 } 134 } 135 if err := it.Error(); err != nil { 136 return nil, err 137 } 138 return instrs, nil 139 }