internal/ethapi, eth/filters: return -32602 for parameter validation errors

Several RPC parameter validation errors were returning the default
error code -32000 (generic server error) instead of -32602 (invalid
params) as specified by the JSON-RPC 2.0 specification.

This change wraps parameter validation errors in invalidParamsError
so that clients receive the correct -32602 error code, consistent
with the fix in a8ea6319f (eth/filters: return -32602 when exceeding
the block range limit).
This commit is contained in:
ray 2026-04-13 19:38:38 +08:00
parent 735bfd121a
commit ca1fae4dab
3 changed files with 29 additions and 29 deletions

View file

@ -42,9 +42,9 @@ var (
errPendingLogsUnsupported = invalidParamsErr("pending logs are not supported") errPendingLogsUnsupported = invalidParamsErr("pending logs are not supported")
errUnknownBlock = errors.New("unknown block") errUnknownBlock = errors.New("unknown block")
errFilterNotFound = errors.New("filter not found") errFilterNotFound = errors.New("filter not found")
errExceedMaxTopics = errors.New("exceed max topics") errExceedMaxTopics = invalidParamsErr("exceed max topics")
errExceedLogQueryLimit = errors.New("exceed max addresses or topics per search position") errExceedLogQueryLimit = invalidParamsErr("exceed max addresses or topics per search position")
errExceedMaxTxHashes = errors.New("exceed max number of transaction hashes allowed per transactionReceipts subscription") errExceedMaxTxHashes = invalidParamsErr("exceed max number of transaction hashes allowed per transactionReceipts subscription")
) )
type invalidParamsError struct { type invalidParamsError struct {

View file

@ -458,11 +458,11 @@ func decodeStorageKey(s string) (h common.Hash, inputLength int, err error) {
s = "0" + s s = "0" + s
} }
if len(s) > 64 { if len(s) > 64 {
return common.Hash{}, len(s) / 2, errors.New("storage key too long (want at most 32 bytes)") return common.Hash{}, len(s) / 2, &invalidParamsError{"storage key too long (want at most 32 bytes)"}
} }
b, err := hex.DecodeString(s) b, err := hex.DecodeString(s)
if err != nil { if err != nil {
return common.Hash{}, 0, errors.New("invalid hex in storage key") return common.Hash{}, 0, &invalidParamsError{"invalid hex in storage key"}
} }
return common.BytesToHash(b), len(b), nil return common.BytesToHash(b), len(b), nil
} }
@ -1856,13 +1856,13 @@ type SignTransactionResult struct {
// the given from address and it needs to be unlocked. // the given from address and it needs to be unlocked.
func (api *TransactionAPI) SignTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) { func (api *TransactionAPI) SignTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
if args.Gas == nil { if args.Gas == nil {
return nil, errors.New("gas not specified") return nil, &invalidParamsError{"gas not specified"}
} }
if args.GasPrice == nil && (args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil) { if args.GasPrice == nil && (args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil) {
return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas") return nil, &invalidParamsError{"missing gasPrice or maxFeePerGas/maxPriorityFeePerGas"}
} }
if args.Nonce == nil { if args.Nonce == nil {
return nil, errors.New("nonce not specified") return nil, &invalidParamsError{"nonce not specified"}
} }
sidecarVersion := types.BlobSidecarVersion0 sidecarVersion := types.BlobSidecarVersion0
if len(args.Blobs) > 0 { if len(args.Blobs) > 0 {
@ -1931,7 +1931,7 @@ func (api *TransactionAPI) PendingTransactions() ([]*RPCTransaction, error) {
// This API is not capable for submitting blob transaction with sidecar. // This API is not capable for submitting blob transaction with sidecar.
func (api *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, gasPrice *hexutil.Big, gasLimit *hexutil.Uint64) (common.Hash, error) { func (api *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, gasPrice *hexutil.Big, gasLimit *hexutil.Uint64) (common.Hash, error) {
if sendArgs.Nonce == nil { if sendArgs.Nonce == nil {
return common.Hash{}, errors.New("missing transaction nonce in transaction spec") return common.Hash{}, &invalidParamsError{"missing transaction nonce in transaction spec"}
} }
if err := sendArgs.setDefaults(ctx, api.b, sidecarConfig{}); err != nil { if err := sendArgs.setDefaults(ctx, api.b, sidecarConfig{}); err != nil {
return common.Hash{}, err return common.Hash{}, err

View file

@ -119,24 +119,24 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, config
args.Nonce = (*hexutil.Uint64)(&nonce) args.Nonce = (*hexutil.Uint64)(&nonce)
} }
if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) { if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) {
return errors.New(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`) return &invalidParamsError{`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`}
} }
// BlobTx fields // BlobTx fields
if args.BlobHashes != nil && len(args.BlobHashes) == 0 { if args.BlobHashes != nil && len(args.BlobHashes) == 0 {
return errors.New("need at least 1 blob for a blob transaction") return &invalidParamsError{"need at least 1 blob for a blob transaction"}
} }
if args.BlobHashes != nil && len(args.BlobHashes) > params.BlobTxMaxBlobs { if args.BlobHashes != nil && len(args.BlobHashes) > params.BlobTxMaxBlobs {
return fmt.Errorf("too many blobs in transaction (have=%d, max=%d)", len(args.BlobHashes), params.BlobTxMaxBlobs) return &invalidParamsError{fmt.Sprintf("too many blobs in transaction (have=%d, max=%d)", len(args.BlobHashes), params.BlobTxMaxBlobs)}
} }
// create check // create check
if args.To == nil { if args.To == nil {
if args.BlobHashes != nil { if args.BlobHashes != nil {
return errors.New(`missing "to" in blob transaction`) return &invalidParamsError{`missing "to" in blob transaction`}
} }
if len(args.data()) == 0 { if len(args.data()) == 0 {
return errors.New(`contract creation without any data provided`) return &invalidParamsError{`contract creation without any data provided`}
} }
} }
@ -171,7 +171,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, config
want := b.ChainConfig().ChainID want := b.ChainConfig().ChainID
if args.ChainID != nil { if args.ChainID != nil {
if have := (*big.Int)(args.ChainID); have.Cmp(want) != 0 { if have := (*big.Int)(args.ChainID); have.Cmp(want) != 0 {
return fmt.Errorf("chainId does not match node's (have=%v, want=%v)", have, want) return &invalidParamsError{fmt.Sprintf("chainId does not match node's (have=%v, want=%v)", have, want)}
} }
} else { } else {
args.ChainID = (*hexutil.Big)(want) args.ChainID = (*hexutil.Big)(want)
@ -183,14 +183,14 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, config
func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b Backend, head *types.Header) error { func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b Backend, head *types.Header) error {
// Sanity check the EIP-4844 fee parameters. // Sanity check the EIP-4844 fee parameters.
if args.BlobFeeCap != nil && args.BlobFeeCap.ToInt().Sign() == 0 { if args.BlobFeeCap != nil && args.BlobFeeCap.ToInt().Sign() == 0 {
return errors.New("maxFeePerBlobGas, if specified, must be non-zero") return &invalidParamsError{"maxFeePerBlobGas, if specified, must be non-zero"}
} }
if b.ChainConfig().IsCancun(head.Number, head.Time) { if b.ChainConfig().IsCancun(head.Number, head.Time) {
args.setCancunFeeDefaults(b.ChainConfig(), head) args.setCancunFeeDefaults(b.ChainConfig(), head)
} }
// If both gasPrice and at least one of the EIP-1559 fee parameters are specified, error. // If both gasPrice and at least one of the EIP-1559 fee parameters are specified, error.
if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") return &invalidParamsError{"both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"}
} }
// If the tx has completely specified a fee mechanism, no default is needed. // If the tx has completely specified a fee mechanism, no default is needed.
// This allows users who are not yet synced past London to get defaults for // This allows users who are not yet synced past London to get defaults for
@ -200,10 +200,10 @@ func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b Backend, head
// Sanity check the EIP-1559 fee parameters if present. // Sanity check the EIP-1559 fee parameters if present.
if args.GasPrice == nil && eip1559ParamsSet { if args.GasPrice == nil && eip1559ParamsSet {
if args.MaxFeePerGas.ToInt().Sign() == 0 { if args.MaxFeePerGas.ToInt().Sign() == 0 {
return errors.New("maxFeePerGas must be non-zero") return &invalidParamsError{"maxFeePerGas must be non-zero"}
} }
if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 {
return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) return &invalidParamsError{fmt.Sprintf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas)}
} }
return nil // No need to set anything, user already set MaxFeePerGas and MaxPriorityFeePerGas return nil // No need to set anything, user already set MaxFeePerGas and MaxPriorityFeePerGas
} }
@ -274,7 +274,7 @@ func (args *TransactionArgs) setLondonFeeDefaults(ctx context.Context, head *typ
} }
// Both EIP-1559 fee parameters are now set; sanity check them. // Both EIP-1559 fee parameters are now set; sanity check them.
if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 {
return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) return &invalidParamsError{fmt.Sprintf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas)}
} }
return nil return nil
} }
@ -288,24 +288,24 @@ func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context, config sideca
// Passing blobs is not allowed in all contexts, only in specific methods. // Passing blobs is not allowed in all contexts, only in specific methods.
if !config.blobSidecarAllowed { if !config.blobSidecarAllowed {
return errors.New(`"blobs" is not supported for this RPC method`) return &invalidParamsError{`"blobs" is not supported for this RPC method`}
} }
// Assume user provides either only blobs (w/o hashes), or // Assume user provides either only blobs (w/o hashes), or
// blobs together with commitments and proofs. // blobs together with commitments and proofs.
if args.Commitments == nil && args.Proofs != nil { if args.Commitments == nil && args.Proofs != nil {
return errors.New(`blob proofs provided while commitments were not`) return &invalidParamsError{`blob proofs provided while commitments were not`}
} else if args.Commitments != nil && args.Proofs == nil { } else if args.Commitments != nil && args.Proofs == nil {
return errors.New(`blob commitments provided while proofs were not`) return &invalidParamsError{`blob commitments provided while proofs were not`}
} }
// len(blobs) == len(commitments) == len(hashes) // len(blobs) == len(commitments) == len(hashes)
n := len(args.Blobs) n := len(args.Blobs)
if args.BlobHashes != nil && len(args.BlobHashes) != n { if args.BlobHashes != nil && len(args.BlobHashes) != n {
return fmt.Errorf("number of blobs and hashes mismatch (have=%d, want=%d)", len(args.BlobHashes), n) return &invalidParamsError{fmt.Sprintf("number of blobs and hashes mismatch (have=%d, want=%d)", len(args.BlobHashes), n)}
} }
if args.Commitments != nil && len(args.Commitments) != n { if args.Commitments != nil && len(args.Commitments) != n {
return fmt.Errorf("number of blobs and commitments mismatch (have=%d, want=%d)", len(args.Commitments), n) return &invalidParamsError{fmt.Sprintf("number of blobs and commitments mismatch (have=%d, want=%d)", len(args.Commitments), n)}
} }
// if V0: len(blobs) == len(proofs) // if V0: len(blobs) == len(proofs)
@ -316,7 +316,7 @@ func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context, config sideca
} }
if args.Proofs != nil && len(args.Proofs) != proofLen { if args.Proofs != nil && len(args.Proofs) != proofLen {
if len(args.Proofs) != n { if len(args.Proofs) != n {
return fmt.Errorf("number of blobs and proofs mismatch (have=%d, want=%d)", len(args.Proofs), proofLen) return &invalidParamsError{fmt.Sprintf("number of blobs and proofs mismatch (have=%d, want=%d)", len(args.Proofs), proofLen)}
} }
// Unset the commitments and proofs, as they may be submitted in the legacy format // Unset the commitments and proofs, as they may be submitted in the legacy format
log.Debug("Unset legacy commitments and proofs", "blobs", n, "proofs", len(args.Proofs)) log.Debug("Unset legacy commitments and proofs", "blobs", n, "proofs", len(args.Proofs))
@ -378,7 +378,7 @@ func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context, config sideca
if args.BlobHashes != nil { if args.BlobHashes != nil {
for i, h := range hashes { for i, h := range hashes {
if h != args.BlobHashes[i] { if h != args.BlobHashes[i] {
return fmt.Errorf("blob hash verification failed (have=%s, want=%s)", args.BlobHashes[i], h) return &invalidParamsError{fmt.Sprintf("blob hash verification failed (have=%s, want=%s)", args.BlobHashes[i], h)}
} }
} }
} else { } else {
@ -392,13 +392,13 @@ func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context, config sideca
func (args *TransactionArgs) CallDefaults(globalGasCap uint64, baseFee *big.Int, chainID *big.Int) error { func (args *TransactionArgs) CallDefaults(globalGasCap uint64, baseFee *big.Int, chainID *big.Int) error {
// Reject invalid combinations of pre- and post-1559 fee styles // Reject invalid combinations of pre- and post-1559 fee styles
if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") return &invalidParamsError{"both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"}
} }
if args.ChainID == nil { if args.ChainID == nil {
args.ChainID = (*hexutil.Big)(chainID) args.ChainID = (*hexutil.Big)(chainID)
} else { } else {
if have := (*big.Int)(args.ChainID); have.Cmp(chainID) != 0 { if have := (*big.Int)(args.ChainID); have.Cmp(chainID) != 0 {
return fmt.Errorf("chainId does not match node's (have=%v, want=%v)", have, chainID) return &invalidParamsError{fmt.Sprintf("chainId does not match node's (have=%v, want=%v)", have, chainID)}
} }
} }
if args.Gas == nil { if args.Gas == nil {