• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

unpackdev / solgo / 8960975808

05 May 2024 08:34PM UTC coverage: 64.769% (-0.3%) from 65.065%
8960975808

push

github

web-flow
Opcode functions and instruction tree + JSON normalization (#210)

245 of 449 new or added lines in 61 files covered. (54.57%)

2 existing lines in 1 file now uncovered.

27471 of 42414 relevant lines covered (64.77%)

0.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.74
/opcode/decompiler.go
1
package opcode
2

3
import (
4
        "bytes"
5
        "context"
6
        "fmt"
7
        "strings"
8

9
        "github.com/ethereum/go-ethereum/common"
10
        opcode_pb "github.com/unpackdev/protos/dist/go/opcode"
11
)
12

13
// Decompiler is responsible for decompiling Ethereum bytecode into a set of instructions.
14
type Decompiler struct {
15
        ctx                 context.Context // The context for the decompiler.
16
        bytecode            []byte          // The bytecode to be decompiled.
17
        bytecodeSize        uint64          // The size of the bytecode.
18
        instructions        []Instruction   // The resulting set of instructions after decompilation.
19
        functionEntryPoints []int           // Slice to store function entry points.
20
}
21

22
// NewDecompiler initializes a new Decompiler with the given bytecode.
23
func NewDecompiler(ctx context.Context, b []byte) (*Decompiler, error) {
1✔
24
        return &Decompiler{
1✔
25
                ctx:                 ctx,
1✔
26
                bytecode:            b,
1✔
27
                bytecodeSize:        uint64(len(b)),
1✔
28
                instructions:        []Instruction{},
1✔
29
                functionEntryPoints: make([]int, 0),
1✔
30
        }, nil
1✔
31
}
1✔
32

33
// GetBytecode returns the bytecode associated with the Decompiler.
34
func (d *Decompiler) GetBytecode() []byte {
1✔
35
        return d.bytecode
1✔
36
}
1✔
37

38
// GetBytecodeSize returns the size of the bytecode.
39
func (d *Decompiler) GetBytecodeSize() uint64 {
1✔
40
        return d.bytecodeSize
1✔
41
}
1✔
42

43
// Decompile processes the bytecode, populates the instructions slice, and identifies function entry points.
44
func (d *Decompiler) Decompile() error {
1✔
45
        if d.bytecodeSize < 1 {
2✔
46
                return ErrEmptyBytecode
1✔
47
        }
1✔
48

49
        offset := 0
1✔
50
        for offset < len(d.bytecode) {
2✔
51
                op := OpCode(d.bytecode[offset])
1✔
52
                instruction := Instruction{
1✔
53
                        Offset:      offset,
1✔
54
                        OpCode:      op,
1✔
55
                        Args:        []byte{},
1✔
56
                        Description: op.GetDescription(),
1✔
57
                }
1✔
58

1✔
59
                if op.IsPush() {
2✔
60
                        argSize := int(op) - int(PUSH1) + 1
1✔
61
                        if offset+argSize >= len(d.bytecode) {
1✔
62
                                break
×
63
                        }
64
                        instruction.Args = d.bytecode[offset+1 : offset+argSize+1]
1✔
65
                        offset += argSize
1✔
66
                }
67

68
                d.instructions = append(d.instructions, instruction)
1✔
69

1✔
70
                // Check if the current instruction is a function entry point (JUMPDEST)
1✔
71
                if op == JUMPDEST {
2✔
72
                        d.functionEntryPoints = append(d.functionEntryPoints, offset)
1✔
73
                }
1✔
74

75
                offset++
1✔
76
        }
77
        return nil
1✔
78
}
79

80
// GetInstructionsByOpCode returns all instructions that match the given OpCode.
81
func (d *Decompiler) GetInstructionsByOpCode(op OpCode) []Instruction {
1✔
82
        var callInstructions []Instruction
1✔
83
        for _, instruction := range d.instructions {
2✔
84
                if instruction.OpCode == op {
2✔
85
                        callInstructions = append(callInstructions, instruction)
1✔
86
                }
1✔
87
        }
88
        return callInstructions
1✔
89
}
90

91
// GetInstructions returns all decompiled instructions.
92
func (d *Decompiler) GetInstructions() []Instruction {
1✔
93
        return d.instructions
1✔
94
}
1✔
95

96
// IsOpCode checks if the given instruction matches the provided OpCode.
97
func (d *Decompiler) IsOpCode(instruction Instruction, op OpCode) bool {
1✔
98
        return instruction.OpCode == op
1✔
99
}
1✔
100

101
// OpCodeFound checks if the given OpCode exists in the decompiled instructions.
102
func (d *Decompiler) OpCodeFound(op OpCode) bool {
1✔
103
        for _, instruction := range d.instructions {
2✔
104
                if instruction.OpCode == op {
2✔
105
                        return true
1✔
106
                }
1✔
107
        }
108
        return false
1✔
109
}
110

111
// ToProto converts the decompiled instructions to a protobuf representation.
112
func (d *Decompiler) ToProto() *opcode_pb.Root {
1✔
113
        instructions := make([]*opcode_pb.Instruction, 0)
1✔
114
        for _, instruction := range d.instructions {
2✔
115
                instructions = append(instructions, instruction.ToProto())
1✔
116
        }
1✔
117

118
        return &opcode_pb.Root{
1✔
119
                Instructions: instructions,
1✔
120
        }
1✔
121
}
122

123
// String provides a string representation of the decompiled instructions.
124
func (d *Decompiler) String() string {
1✔
125
        var buf bytes.Buffer
1✔
126

1✔
127
        for _, instr := range d.instructions {
2✔
128
                offset := fmt.Sprintf("0x%04x", instr.Offset)
1✔
129
                opCode := instr.OpCode.String()
1✔
130

1✔
131
                buf.WriteString(offset + " " + opCode)
1✔
132

1✔
133
                if len(instr.Args) > 0 {
2✔
134
                        buf.WriteString(" " + common.Bytes2Hex(instr.Args))
1✔
135
                }
1✔
136

137
                desc := instr.OpCode.GetDescription()
1✔
138
                if desc != "" {
2✔
139
                        buf.WriteString(" // " + desc)
1✔
140
                }
1✔
141

142
                buf.WriteString("\n")
1✔
143
        }
144

145
        return buf.String()
1✔
146
}
147

148
// StringArray returns a string representation of the decompiled instructions as an array of strings.
NEW
149
func (d *Decompiler) StringArray() []string {
×
NEW
150
        var result []string
×
NEW
151

×
NEW
152
        for _, instr := range d.instructions {
×
NEW
153
                offset := fmt.Sprintf("0x%04x", instr.Offset)
×
NEW
154
                opCode := instr.OpCode.String()
×
NEW
155

×
NEW
156
                var strBuilder strings.Builder
×
NEW
157
                strBuilder.WriteString(offset + " " + opCode)
×
NEW
158

×
NEW
159
                if len(instr.Args) > 0 {
×
NEW
160
                        strBuilder.WriteString(" " + common.Bytes2Hex(instr.Args))
×
NEW
161
                }
×
162

NEW
163
                desc := instr.OpCode.GetDescription()
×
NEW
164
                if desc != "" {
×
NEW
165
                        strBuilder.WriteString(" // " + desc)
×
NEW
166
                }
×
167

NEW
168
                result = append(result, strBuilder.String())
×
169
        }
170

NEW
171
        return result
×
172
}
173

174
// GetInstructionTreeFormatted returns a formatted string representation of the opcode execution tree
175
// starting from the provided instruction. The output is indented based on the provided indent string.
176
func (d *Decompiler) GetInstructionTreeFormatted(instruction Instruction, indent string) string {
1✔
177
        var builder strings.Builder
1✔
178
        builder.WriteString(fmt.Sprintf("%s0x%04x %s\n", indent, instruction.Offset, instruction.OpCode.String()))
1✔
179

1✔
180
        childIndent := indent + "   "
1✔
181
        for _, child := range d.GetChildrenByOffset(instruction.Offset) {
2✔
182
                builder.WriteString(d.GetInstructionTreeFormatted(child, childIndent))
1✔
183
        }
1✔
184

185
        return builder.String()
1✔
186
}
187

188
// GetChildrenByOffset retrieves a slice of Instructions that are immediate children (subsequent instructions)
189
// of the instruction at the provided offset.
190
func (d *Decompiler) GetChildrenByOffset(offset int) []Instruction {
1✔
191
        var children []Instruction
1✔
192
        for _, instr := range d.instructions {
2✔
193
                if instr.Offset == offset+1 {
2✔
194
                        children = append(children, instr)
1✔
195
                }
1✔
196
        }
197
        return children
1✔
198
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc