mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-04-13 13:18:35 +00:00
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:
parent
7d463aedd3
commit
5b7511eeed
3 changed files with 55 additions and 20 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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_pair’s 16-aligned arithmetic mapping valid (0–81, 128–255).
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue