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

unpackdev / solgo / 9149435990

19 May 2024 05:39PM UTC coverage: 64.249% (-0.5%) from 64.782%
9149435990

push

github

web-flow
Overall improvements (#214)

27 of 427 new or added lines in 20 files covered. (6.32%)

14 existing lines in 4 files now uncovered.

27494 of 42793 relevant lines covered (64.25%)

0.71 hits per line

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

82.03
/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 fmt.Errorf("bytecode is empty")
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) {
2✔
62
                                instruction.Args = d.bytecode[offset+1 : offset+argSize+1]
1✔
63
                                offset += argSize
1✔
64
                        } else {
1✔
NEW
65
                                // If we don't have enough bytes for PUSH arguments, use the remaining bytes
×
NEW
66
                                instruction.Args = d.bytecode[offset+1:]
×
NEW
67
                                offset = len(d.bytecode) - 1
×
UNCOV
68
                        }
×
69
                }
70

71
                d.instructions = append(d.instructions, instruction)
1✔
72

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

78
                offset++
1✔
79
        }
80

81
        fmt.Printf("Total instructions processed: %d\n", len(d.instructions))
1✔
82
        return nil
1✔
83
}
84

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

96
// GetInstructions returns all decompiled instructions.
97
func (d *Decompiler) GetInstructions() []Instruction {
1✔
98
        return d.instructions
1✔
99
}
1✔
100

101
// IsOpCode checks if the given instruction matches the provided OpCode.
102
func (d *Decompiler) IsOpCode(instruction Instruction, op OpCode) bool {
1✔
103
        return instruction.OpCode == op
1✔
104
}
1✔
105

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

116
// ToProto converts the decompiled instructions to a protobuf representation.
117
func (d *Decompiler) ToProto() *opcode_pb.Root {
1✔
118
        instructions := make([]*opcode_pb.Instruction, 0)
1✔
119
        for _, instruction := range d.instructions {
2✔
120
                instructions = append(instructions, instruction.ToProto())
1✔
121
        }
1✔
122

123
        return &opcode_pb.Root{
1✔
124
                Instructions: instructions,
1✔
125
        }
1✔
126
}
127

128
// String provides a string representation of the decompiled instructions.
129
func (d *Decompiler) String() string {
1✔
130
        var buf bytes.Buffer
1✔
131

1✔
132
        for _, instr := range d.instructions {
2✔
133
                offset := fmt.Sprintf("0x%04x", instr.Offset)
1✔
134
                opCode := instr.OpCode.String()
1✔
135

1✔
136
                buf.WriteString(offset + " " + opCode)
1✔
137

1✔
138
                if len(instr.Args) > 0 {
2✔
139
                        buf.WriteString(" " + common.Bytes2Hex(instr.Args))
1✔
140
                }
1✔
141

142
                desc := instr.OpCode.GetDescription()
1✔
143
                if desc != "" {
2✔
144
                        buf.WriteString(" // " + desc)
1✔
145
                }
1✔
146

147
                buf.WriteString("\n")
1✔
148
        }
149

150
        return buf.String()
1✔
151
}
152

153
// StringArray returns a string representation of the decompiled instructions as an array of strings.
154
func (d *Decompiler) StringArray() []string {
×
155
        var result []string
×
156

×
157
        for _, instr := range d.instructions {
×
158
                offset := fmt.Sprintf("0x%04x", instr.Offset)
×
159
                opCode := instr.OpCode.String()
×
160

×
161
                var strBuilder strings.Builder
×
162
                strBuilder.WriteString(offset + " " + opCode)
×
163

×
164
                if len(instr.Args) > 0 {
×
165
                        strBuilder.WriteString(" " + common.Bytes2Hex(instr.Args))
×
166
                }
×
167

168
                desc := instr.OpCode.GetDescription()
×
169
                if desc != "" {
×
170
                        strBuilder.WriteString(" // " + desc)
×
171
                }
×
172

173
                result = append(result, strBuilder.String())
×
174
        }
175

176
        return result
×
177
}
178

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

1✔
185
        childIndent := indent + "   "
1✔
186
        for _, child := range d.GetChildrenByOffset(instruction.Offset) {
2✔
187
                builder.WriteString(d.GetInstructionTreeFormatted(child, childIndent))
1✔
188
        }
1✔
189

190
        return builder.String()
1✔
191
}
192

193
// GetChildrenByOffset retrieves a slice of Instructions that are immediate children (subsequent instructions)
194
// of the instruction at the provided offset.
195
func (d *Decompiler) GetChildrenByOffset(offset int) []Instruction {
1✔
196
        var children []Instruction
1✔
197
        for _, instr := range d.instructions {
2✔
198
                if instr.Offset == offset+1 {
2✔
199
                        children = append(children, instr)
1✔
200
                }
1✔
201
        }
202
        return children
1✔
203
}
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