mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-16 13:06:40 +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.
|
// ErrInvalidOpCode wraps an evm error when an invalid opcode is encountered.
|
||||||
type ErrInvalidOpCode struct {
|
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
|
// 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.
|
// 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.
|
// This range is excluded to preserve compatibility with existing opcodes.
|
||||||
if x > 90 && x < 128 {
|
if x > 90 && x < 128 {
|
||||||
return nil, &ErrInvalidOpCode{opcode: OpCode(x)}
|
operand := x
|
||||||
|
return nil, &ErrInvalidOpCode{opcode: DUPN, operand: &operand}
|
||||||
}
|
}
|
||||||
n := decodeSingle(x)
|
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.
|
// This range is excluded to preserve compatibility with existing opcodes.
|
||||||
if x > 90 && x < 128 {
|
if x > 90 && x < 128 {
|
||||||
return nil, &ErrInvalidOpCode{opcode: OpCode(x)}
|
operand := x
|
||||||
|
return nil, &ErrInvalidOpCode{opcode: SWAPN, operand: &operand}
|
||||||
}
|
}
|
||||||
n := decodeSingle(x)
|
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
|
// 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).
|
// and to keep decode_pair’s 16-aligned arithmetic mapping valid (0–81, 128–255).
|
||||||
if x > 81 && x < 128 {
|
if x > 81 && x < 128 {
|
||||||
return nil, &ErrInvalidOpCode{opcode: OpCode(x)}
|
operand := x
|
||||||
|
return nil, &ErrInvalidOpCode{opcode: EXCHANGE, operand: &operand}
|
||||||
}
|
}
|
||||||
n, m := decodePair(x)
|
n, m := decodePair(x)
|
||||||
need := max(n, m) + 1
|
need := max(n, m) + 1
|
||||||
|
|
|
||||||
|
|
@ -1014,11 +1014,12 @@ func TestEIP8024_Execution(t *testing.T) {
|
||||||
evm := NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
evm := NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
codeHex string
|
codeHex string
|
||||||
wantErr error
|
wantErr error
|
||||||
wantOpcode OpCode
|
wantOpcode OpCode
|
||||||
wantVals []uint64
|
wantOperand *byte
|
||||||
|
wantVals []uint64
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "DUPN",
|
name: "DUPN",
|
||||||
|
|
@ -1063,10 +1064,18 @@ func TestEIP8024_Execution(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "INVALID_SWAPN_LOW",
|
name: "INVALID_DUPN_LOW",
|
||||||
codeHex: "e75b",
|
codeHex: "e65b",
|
||||||
wantErr: &ErrInvalidOpCode{},
|
wantErr: &ErrInvalidOpCode{},
|
||||||
wantOpcode: SWAPN,
|
wantOpcode: DUPN,
|
||||||
|
wantOperand: ptrToByte(0x5b),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "INVALID_SWAPN_LOW",
|
||||||
|
codeHex: "e75b",
|
||||||
|
wantErr: &ErrInvalidOpCode{},
|
||||||
|
wantOpcode: SWAPN,
|
||||||
|
wantOperand: ptrToByte(0x5b),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "JUMP_OVER_INVALID_DUPN",
|
name: "JUMP_OVER_INVALID_DUPN",
|
||||||
|
|
@ -1079,10 +1088,11 @@ func TestEIP8024_Execution(t *testing.T) {
|
||||||
wantVals: []uint64{1, 0, 0},
|
wantVals: []uint64{1, 0, 0},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "INVALID_EXCHANGE",
|
name: "INVALID_EXCHANGE",
|
||||||
codeHex: "e852",
|
codeHex: "e852",
|
||||||
wantErr: &ErrInvalidOpCode{},
|
wantErr: &ErrInvalidOpCode{},
|
||||||
wantOpcode: EXCHANGE,
|
wantOpcode: EXCHANGE,
|
||||||
|
wantOperand: ptrToByte(0x52),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "UNDERFLOW_DUPN",
|
name: "UNDERFLOW_DUPN",
|
||||||
|
|
@ -1150,10 +1160,21 @@ func TestEIP8024_Execution(t *testing.T) {
|
||||||
// Fail if we don't get the error we expect.
|
// Fail if we don't get the error we expect.
|
||||||
switch tc.wantErr.(type) {
|
switch tc.wantErr.(type) {
|
||||||
case *ErrInvalidOpCode:
|
case *ErrInvalidOpCode:
|
||||||
var want *ErrInvalidOpCode
|
var got *ErrInvalidOpCode
|
||||||
if !errors.As(err, &want) {
|
if !errors.As(err, &got) {
|
||||||
t.Fatalf("expected ErrInvalidOpCode, got %v", err)
|
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:
|
case *ErrStackUnderflow:
|
||||||
var want *ErrStackUnderflow
|
var want *ErrStackUnderflow
|
||||||
if !errors.As(err, &want) {
|
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