core/vm: include operand in error message (#34635)

Return ErrInvalidOpCode with the executing opcode and offending
immediate for forbidden DUPN, SWAPN, and EXCHANGE operands. Extend
TestEIP8024_Execution to assert both opcode and operand for all
invalid-immediate paths.
This commit is contained in:
Daniel Liu 2026-04-13 20:13:33 +08:00 committed by GitHub
parent 7d463aedd3
commit 5b7511eeed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 55 additions and 20 deletions

View file

@ -76,10 +76,16 @@ func (e ErrStackOverflow) Unwrap() error {
// ErrInvalidOpCode wraps an evm error when an invalid opcode is encountered.
type ErrInvalidOpCode struct {
opcode OpCode
opcode OpCode
operand *byte
}
func (e *ErrInvalidOpCode) Error() string { return fmt.Sprintf("invalid opcode: %s", e.opcode) }
func (e *ErrInvalidOpCode) Error() string {
if e.operand != nil {
return fmt.Sprintf("invalid opcode: %s (operand: 0x%02x)", e.opcode, *e.operand)
}
return fmt.Sprintf("invalid opcode: %s", e.opcode)
}
// rpcError is the same interface as the one defined in rpc/errors.go
// but we do not want to depend on rpc package here so we redefine it.

View file

@ -996,7 +996,8 @@ func opDupN(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
// This range is excluded to preserve compatibility with existing opcodes.
if x > 90 && x < 128 {
return nil, &ErrInvalidOpCode{opcode: OpCode(x)}
operand := x
return nil, &ErrInvalidOpCode{opcode: DUPN, operand: &operand}
}
n := decodeSingle(x)
@ -1023,7 +1024,8 @@ func opSwapN(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
// This range is excluded to preserve compatibility with existing opcodes.
if x > 90 && x < 128 {
return nil, &ErrInvalidOpCode{opcode: OpCode(x)}
operand := x
return nil, &ErrInvalidOpCode{opcode: SWAPN, operand: &operand}
}
n := decodeSingle(x)
@ -1053,7 +1055,8 @@ func opExchange(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
// This range is excluded both to preserve compatibility with existing opcodes
// and to keep decode_pairs 16-aligned arithmetic mapping valid (081, 128255).
if x > 81 && x < 128 {
return nil, &ErrInvalidOpCode{opcode: OpCode(x)}
operand := x
return nil, &ErrInvalidOpCode{opcode: EXCHANGE, operand: &operand}
}
n, m := decodePair(x)
need := max(n, m) + 1

View file

@ -1014,11 +1014,12 @@ func TestEIP8024_Execution(t *testing.T) {
evm := NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
tests := []struct {
name string
codeHex string
wantErr error
wantOpcode OpCode
wantVals []uint64
name string
codeHex string
wantErr error
wantOpcode OpCode
wantOperand *byte
wantVals []uint64
}{
{
name: "DUPN",
@ -1063,10 +1064,18 @@ func TestEIP8024_Execution(t *testing.T) {
},
},
{
name: "INVALID_SWAPN_LOW",
codeHex: "e75b",
wantErr: &ErrInvalidOpCode{},
wantOpcode: SWAPN,
name: "INVALID_DUPN_LOW",
codeHex: "e65b",
wantErr: &ErrInvalidOpCode{},
wantOpcode: DUPN,
wantOperand: ptrToByte(0x5b),
},
{
name: "INVALID_SWAPN_LOW",
codeHex: "e75b",
wantErr: &ErrInvalidOpCode{},
wantOpcode: SWAPN,
wantOperand: ptrToByte(0x5b),
},
{
name: "JUMP_OVER_INVALID_DUPN",
@ -1079,10 +1088,11 @@ func TestEIP8024_Execution(t *testing.T) {
wantVals: []uint64{1, 0, 0},
},
{
name: "INVALID_EXCHANGE",
codeHex: "e852",
wantErr: &ErrInvalidOpCode{},
wantOpcode: EXCHANGE,
name: "INVALID_EXCHANGE",
codeHex: "e852",
wantErr: &ErrInvalidOpCode{},
wantOpcode: EXCHANGE,
wantOperand: ptrToByte(0x52),
},
{
name: "UNDERFLOW_DUPN",
@ -1150,10 +1160,21 @@ func TestEIP8024_Execution(t *testing.T) {
// Fail if we don't get the error we expect.
switch tc.wantErr.(type) {
case *ErrInvalidOpCode:
var want *ErrInvalidOpCode
if !errors.As(err, &want) {
var got *ErrInvalidOpCode
if !errors.As(err, &got) {
t.Fatalf("expected ErrInvalidOpCode, got %v", err)
}
if got.opcode != tc.wantOpcode {
t.Fatalf("ErrInvalidOpCode.opcode=%s; want %s", got.opcode, tc.wantOpcode)
}
if tc.wantOperand != nil {
if got.operand == nil {
t.Fatalf("ErrInvalidOpCode.operand=nil; want 0x%02x", *tc.wantOperand)
}
if *got.operand != *tc.wantOperand {
t.Fatalf("ErrInvalidOpCode.operand=0x%02x; want 0x%02x", *got.operand, *tc.wantOperand)
}
}
case *ErrStackUnderflow:
var want *ErrStackUnderflow
if !errors.As(err, &want) {
@ -1183,3 +1204,8 @@ func TestEIP8024_Execution(t *testing.T) {
})
}
}
func ptrToByte(v byte) *byte {
b := v
return &b
}