mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-16 18:00:46 +00:00
internal/ethapi: return InvalidParams for malformed sendRawTransaction RLP
SendRawTransaction and SendRawTransactionSync currently return the bare UnmarshalBinary error to the JSON-RPC layer. Because the underlying decode errors don't implement rpc.Error.ErrorCode(), rpc/json.go:errorMessage falls back to errcodeDefault = -32000. Per JSON-RPC 2.0, malformed method parameters should use -32602 (InvalidParams), which is what Reth and Besu emit for the same input. internal/ethapi/errors.go already defines invalidParamsError with ErrorCode() = errCodeInvalidParams. Wire it into the two decode paths and add a parameterised test covering 5 distinct RLP-decode failure shapes against both entry points.
This commit is contained in:
parent
31d227ea83
commit
1e0855e5ae
2 changed files with 51 additions and 2 deletions
|
|
@ -1735,7 +1735,7 @@ func (api *TransactionAPI) currentBlobSidecarVersion() byte {
|
||||||
func (api *TransactionAPI) SendRawTransaction(ctx context.Context, input hexutil.Bytes) (common.Hash, error) {
|
func (api *TransactionAPI) SendRawTransaction(ctx context.Context, input hexutil.Bytes) (common.Hash, error) {
|
||||||
tx := new(types.Transaction)
|
tx := new(types.Transaction)
|
||||||
if err := tx.UnmarshalBinary(input); err != nil {
|
if err := tx.UnmarshalBinary(input); err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, &invalidParamsError{message: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert legacy blob transaction proofs.
|
// Convert legacy blob transaction proofs.
|
||||||
|
|
@ -1758,7 +1758,7 @@ func (api *TransactionAPI) SendRawTransaction(ctx context.Context, input hexutil
|
||||||
func (api *TransactionAPI) SendRawTransactionSync(ctx context.Context, input hexutil.Bytes, timeoutMs *uint64) (map[string]interface{}, error) {
|
func (api *TransactionAPI) SendRawTransactionSync(ctx context.Context, input hexutil.Bytes, timeoutMs *uint64) (map[string]interface{}, error) {
|
||||||
tx := new(types.Transaction)
|
tx := new(types.Transaction)
|
||||||
if err := tx.UnmarshalBinary(input); err != nil {
|
if err := tx.UnmarshalBinary(input); err != nil {
|
||||||
return nil, err
|
return nil, &invalidParamsError{message: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert legacy blob transaction proofs.
|
// Convert legacy blob transaction proofs.
|
||||||
|
|
|
||||||
|
|
@ -4175,6 +4175,55 @@ func TestSendRawTransactionSync_Timeout(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestSendRawTransaction_InvalidParams_OnMalformedRLP verifies that malformed-RLP
|
||||||
|
// inputs are reported with JSON-RPC error code -32602 (InvalidParams), not the
|
||||||
|
// default -32000. Aligns with JSON-RPC 2.0 spec and the Reth/Besu implementations.
|
||||||
|
func TestSendRawTransaction_InvalidParams_OnMalformedRLP(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
genesis := &core.Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: types.GenesisAlloc{},
|
||||||
|
}
|
||||||
|
b := newTestBackend(t, 0, genesis, ethash.NewFaker(), nil)
|
||||||
|
api := NewTransactionAPI(b, new(AddrLocker))
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
raw string
|
||||||
|
}{
|
||||||
|
{"empty-list", "0xc0"},
|
||||||
|
{"truncated-short-list", "0xd4"},
|
||||||
|
{"unknown-tx-type", "0x09c0"},
|
||||||
|
{"eip4844-truncated-list", "0x03c0"},
|
||||||
|
{"eip7702-truncated-list", "0x04c0"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name+"/SendRawTransaction", func(t *testing.T) {
|
||||||
|
_, err := api.SendRawTransaction(context.Background(), hexutil.MustDecode(tc.raw))
|
||||||
|
assertInvalidParams(t, err)
|
||||||
|
})
|
||||||
|
t.Run(tc.name+"/SendRawTransactionSync", func(t *testing.T) {
|
||||||
|
_, err := api.SendRawTransactionSync(context.Background(), hexutil.MustDecode(tc.raw), nil)
|
||||||
|
assertInvalidParams(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertInvalidParams(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error, got nil")
|
||||||
|
}
|
||||||
|
var ec interface{ ErrorCode() int }
|
||||||
|
if !errors.As(err, &ec) {
|
||||||
|
t.Fatalf("expected error implementing ErrorCode(), got %T: %v", err, err)
|
||||||
|
}
|
||||||
|
if got, want := ec.ErrorCode(), -32602; got != want {
|
||||||
|
t.Fatalf("expected ErrorCode=%d (InvalidParams), got %d (%v)", want, got, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetStorageValues(t *testing.T) {
|
func TestGetStorageValues(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue