Skip to content

eth_getFinalizedHeader / eth_getFinalizedBlock reject negative verifiedValidatorNum values #262

@MatusKysel

Description

@MatusKysel

Summary

eth_getFinalizedHeader and eth_getFinalizedBlock fail when called with negative integer parameters (e.g., -1, -2, -3) because the verified_validator_num parameter is typed as u64 instead of i64.

Reproduce

curl -X POST http://localhost:8545 \
  -H "Content-Type: application/json" \
  -d '[{"jsonrpc":"2.0","id":1,"method":"eth_getFinalizedHeader","params":[-1]}]'

Reth response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params",
    "data": "invalid value: integer `-1`, expected u64 at line 1 column 2"
  }
}

Geth (BSC) response: Returns the finalized header correctly.

Root cause

The parameter type is defined as u64 in three locations:

  • crates/rpc/rpc-eth-api/src/core.rsEthApiServer trait definition and blanket impl
  • crates/rpc/rpc-eth-api/src/helpers/block.rsrpc_finalized_header / rpc_finalized_block helper functions

In BSC geth (internal/ethapi/api.go), the equivalent parameter is int64 (signed), and negative values carry special semantics:

Value Meaning
-1 At least ceil(validatorCount / 2) validators
-2 At least ceil(validatorCount * 2 / 3) validators
-3 All validators
1..N Exact validator count (where N = len(currentValidators))

Fix

Change u64 to i64 in all 6 occurrences across the two files:

crates/rpc/rpc-eth-api/src/core.rs — trait definition

- async fn finalized_header(&self, verified_validator_num: u64) -> RpcResult<Option<H>>;
+ async fn finalized_header(&self, verified_validator_num: i64) -> RpcResult<Option<H>>;

- async fn finalized_block(&self, verified_validator_num: u64, full: bool) -> RpcResult<Option<B>>;
+ async fn finalized_block(&self, verified_validator_num: i64, full: bool) -> RpcResult<Option<B>>;

crates/rpc/rpc-eth-api/src/core.rs — impl block

- async fn finalized_header(&self, verified_validator_num: u64) -> ...
+ async fn finalized_header(&self, verified_validator_num: i64) -> ...

- async fn finalized_block(&self, verified_validator_num: u64, full: bool) -> ...
+ async fn finalized_block(&self, verified_validator_num: i64, full: bool) -> ...

crates/rpc/rpc-eth-api/src/helpers/block.rs

- fn rpc_finalized_header(&self, _verified_validator_num: u64, ...) -> ...
+ fn rpc_finalized_header(&self, _verified_validator_num: i64, ...) -> ...

- fn rpc_finalized_block(&self, _verified_validator_num: u64, full: bool, ...) -> ...
+ fn rpc_finalized_block(&self, _verified_validator_num: i64, full: bool, ...) -> ...

Note

The current implementation ignores verified_validator_num entirely and delegates to BlockNumberOrTag::Finalized. Implementing the actual probabilistic finality logic (walking headers backward, counting distinct coinbases per the geth behavior) is a separate concern.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions