Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 24 additions & 14 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1167,24 +1167,34 @@ func opSelfdestruct6780(pc *uint64, cvm *CVM, scope *ScopeContext) ([]byte, erro
return nil, errStopToken
}

// decodeSingle decodes the immediate operand of a backward-compatible DUPN or SWAPN instruction (EIP-8024)
// https://eips.ethereum.org/EIPS/eip-8024
func decodeSingle(x byte) int {
if x <= 90 {
return int(x) + 17
}
return int(x) - 20
}

// Depths 1-16 are already covered by the legacy opcodes. The forbidden byte range [91, 127] removes
// 37 values from the 256 possible immediates, leaving 219 usable values, so this encoding covers depths
// 17 through 235. The immediate is encoded as (x + 111) % 256, where 111 is chosen so that these values
// avoid the forbidden range. Decoding is simply the modular inverse (i.e. 111+145=256).
return (int(x) + 145) % 256
}

// decodePair decodes the immediate operand of a backward-compatible EXCHANGE
// instruction (EIP-8024) into stack indices (n, m) where 1 <= n < m
// and n + m <= 30. The forbidden byte range [82, 127] removes 46 values from
// the 256 possible immediates, leaving exactly 210 usable bytes.
// https://eips.ethereum.org/EIPS/eip-8024
func decodePair(x byte) (int, int) {
var k int
if x <= 79 {
k = int(x)
} else {
k = int(x) - 48
}
// XOR with 143 remaps the forbidden bytes [82, 127] to an unused corner
// of the 16x16 grid below.
k := int(x ^ 143)
// Split into row q and column r of a 16x16 grid. The 210 valid pairs
// occupy two triangles within this grid.
q, r := k/16, k%16
// Upper triangle (q < r): pairs where m <= 16, encoded directly as
// (q+1, r+1).
if q < r {
return q + 1, r + 1
}
// Lower triangle: pairs where m > 16, recovered as (r+1, 29-q).
return r + 1, 29 - q
}

Expand Down Expand Up @@ -1255,8 +1265,8 @@ func opExchange(pc *uint64, evm *CVM, scope *ScopeContext) ([]byte, error) {
}

// This range is excluded both to preserve compatibility with existing opcodes
// and to keep decode_pair’s 16-aligned arithmetic mapping valid (0–79, 128–255).
if x > 79 && x < 128 {
// and to keep decode_pair’s 16-aligned arithmetic mapping valid (0–81, 128–255).
if x > 81 && x < 128 {
return nil, &ErrInvalidOpCode{opcode: OpCode(x)}
}
n, m := decodePair(x)
Expand Down
87 changes: 21 additions & 66 deletions core/vm/instructions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -772,16 +772,7 @@ func TestEIP8024_Execution(t *testing.T) {
}{
{
name: "DUPN",
codeHex: "60016000808080808080808080808080808080e600",
wantVals: []uint64{
1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1,
},
},
{
name: "DUPN_MISSING_IMMEDIATE",
codeHex: "60016000808080808080808080808080808080e6",
codeHex: "60016000808080808080808080808080808080e680",
wantVals: []uint64{
1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Expand All @@ -790,30 +781,31 @@ func TestEIP8024_Execution(t *testing.T) {
},
{
name: "SWAPN",
codeHex: "600160008080808080808080808080808080806002e700",
codeHex: "600160008080808080808080808080808080806002e780",
wantVals: []uint64{
1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2,
},
},
{
name: "SWAPN_MISSING_IMMEDIATE",
codeHex: "600160008080808080808080808080808080806002e7",
name: "EXCHANGE_MISSING_IMMEDIATE",
codeHex: "600260008080808080600160008080808080808080e8",
wantVals: []uint64{
1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2,
0, 0, 0, 0, 0, 0, 0, 0, 0,
2, // 10th from top
0, 0, 0, 0, 0, 0,
1, // bottom
},
},
{
name: "EXCHANGE",
codeHex: "600060016002e801",
codeHex: "600060016002e88e",
wantVals: []uint64{2, 0, 1},
},
{
name: "EXCHANGE_MISSING_IMMEDIATE",
codeHex: "600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060016002e8",
name: "EXCHANGE",
codeHex: "600080808080808080808080808080808080808080808080808080808060016002e88f",
wantVals: []uint64{
2,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Expand All @@ -827,68 +819,31 @@ func TestEIP8024_Execution(t *testing.T) {
wantOpcode: SWAPN,
},
{
name: "JUMP over INVALID_DUPN",
name: "JUMP_OVER_INVALID_DUPN",
codeHex: "600456e65b",
wantErr: nil,
},
{
name: "UNDERFLOW_DUPN_1",
codeHex: "6000808080808080808080808080808080e600",
wantErr: &ErrStackUnderflow{},
wantOpcode: DUPN,
},
// Additional test cases
{
name: "INVALID_DUPN_LOW",
codeHex: "e65b",
wantErr: &ErrInvalidOpCode{},
wantOpcode: DUPN,
},
{
name: "INVALID_EXCHANGE_LOW",
codeHex: "e850",
wantErr: &ErrInvalidOpCode{},
wantOpcode: EXCHANGE,
},
{
name: "INVALID_DUPN_HIGH",
codeHex: "e67f",
wantErr: &ErrInvalidOpCode{},
wantOpcode: DUPN,
},
{
name: "INVALID_SWAPN_HIGH",
codeHex: "e77f",
wantErr: &ErrInvalidOpCode{},
wantOpcode: SWAPN,
name: "EXCHANGE",
codeHex: "60008080e88e15",
wantVals: []uint64{1, 0, 0},
},
{
name: "INVALID_EXCHANGE_HIGH",
codeHex: "e87f",
name: "INVALID_EXCHANGE",
codeHex: "e852",
wantErr: &ErrInvalidOpCode{},
wantOpcode: EXCHANGE,
},
{
name: "UNDERFLOW_DUPN_2",
codeHex: "5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5fe600", // (n=17, need 17 items, have 16)
name: "UNDERFLOW_DUPN",
codeHex: "6000808080808080808080808080808080e680",
wantErr: &ErrStackUnderflow{},
wantOpcode: DUPN,
},
{
name: "UNDERFLOW_SWAPN",
codeHex: "5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5fe700", // (n=17, need 18 items, have 17)
wantErr: &ErrStackUnderflow{},
wantOpcode: SWAPN,
},
{
name: "UNDERFLOW_EXCHANGE",
codeHex: "60016002e801", // (n,m)=(1,2), need 3 items, have 2
wantErr: &ErrStackUnderflow{},
wantOpcode: EXCHANGE,
},
// Additional test cases
{
name: "PC_INCREMENT",
codeHex: "600060006000e80115",
codeHex: "600060006000e88e15",
wantVals: []uint64{1, 0, 0},
},
}
Expand Down
Loading