polkadot-evm / frontier

Ethereum compatibility layer for Substrate.
Apache License 2.0
567 stars 480 forks source link

RPC API Headers doesn't match actual block header #1247

Open Lohann opened 10 months ago

Lohann commented 10 months ago

Description I'm building an ethereum light-client, and one of the steps is verifying the block hash, however there's a mismatch between the block header returned by the RPC API and the actual header stored, as such I cannot recompute the block hash, preventing frontier from working with ethereum light-clients that relies on the RPC API.

After a investigation I realized this happens because of two issues:

  1. The block header stores the timestamp in milliseconds, while the API converts it to seconds: https://github.com/polkadot-evm/frontier/blob/pallet-evm-v5.0.0/client/rpc/src/eth.rs#L135
  2. The RPC API returns the baseFeePerGas field, but this field is not encoded in the header. EIP-1559 says this field MUST be part of the block header:
  3. The timestamp is important for TIMESTAMP opcode, a ethereum light-client relies on getting the contract state using eth_createAccessList and eth_getProof for executing the call locally, by returning the wrong timestamp the light-client and the real node will get different results.

Example, the eth_getBlockByNumber endpoint returns:

{
  "author": "0xf02ddb48eda520c915c0dabadc70ba12d1b49ad2",
  "baseFeePerGas": "0x1e4073dfbf",
  "difficulty": "0x0",
  "extraData": "0x",
  "gasLimit": "0xe4e1c0",
  "gasUsed": "0x4e6b11",
  "hash": "0x84471574a6645fe3c4ea4fa7fb588f53f8cea681dd92d27d2de7c23f3040a7aa",
  "logsBloom": "0x825080001000080802020000108400509000410406003440c08000402084300000021000910005c000300400022000004b000000210228020008001c31208000000008418c21101800082008c04880020408240008008c008200400ac22010448401040002820001000000080000080002030000020180030000001480988003240082802888080802200164000010208024040b0810a0004041208004200060224000400200040002b20b0409016048a1020080100210102001f12c40000ac0880144020048000603280802800014210400001001240001018022008400a100001908900000830420000018000260000800000640001040004600012c000060",
  "miner": "0xf02ddb48eda520c915c0dabadc70ba12d1b49ad2",
  "nonce": "0x0000000000000000",
  "number": "0x4a1dbd",
  "parentHash": "0x93434fec7bb917a3fdf071d43e6bda1061eab7c4d5663192ad88353925b49849",
  "receiptsRoot": "0x088e158e77aa12898ed02e3ec20f030cd18054cd56890ad56a78a7ffadd4f5f3",
  "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  "size": "0x11f8",
  "stateRoot": "0xa7b30b5144ee34730d42b8ea7b5f240d830190ad83b098ddce0e89978ea1055a",
  "timestamp": "0x6551a2be",
  "totalDifficulty": "0x0",
  "transactions": [
      "0xc42bbbe43ffbaa15c3f6fdd335eba12adf37520313d15b5496e77a67fbd35b1d",
      "0x3df5b3ddaab690c68ed9e94cc086f210555d8ecc39c34aad292e8bd623b827d7",
      "0xf708668e3002ec8a079e803e041091d8d5f2a7c3161a75c5c40592c18a9c67d2",
      "0x5411f16773adac1f407d0161d3a0627fece66479e244d040d856e065794d4f04",
      "0x0ba4819248cd220f1beb3f72482ef15f14121d7ab28d2deab0656452945db38b",
      "0x9e01fd45eedbdf68665531dc9795f97c7a130b6bfbe633095b92f94a21c96adb",
      "0xe227e458184c315517ea2c926d6505a6d53528d86acc1b29758f7c85e42a9821"
  ],
  "transactionsRoot": "0xa34082f12ca71f5b7111e910ed9fb1350e5692a09456a37edc791f055e0603e5",
  "uncles": []
}

But for correctly represent the block header, it should omit the baseFeePerGas and return the correct timestamp:

{
  "author": "0xf02ddb48eda520c915c0dabadc70ba12d1b49ad2",
  "difficulty": "0x0",
  "extraData": "0x",
  "gasLimit": "0xe4e1c0",
  "gasUsed": "0x4e6b11",
  "hash": "0x84471574a6645fe3c4ea4fa7fb588f53f8cea681dd92d27d2de7c23f3040a7aa",
  "logsBloom": "0x825080001000080802020000108400509000410406003440c08000402084300000021000910005c000300400022000004b000000210228020008001c31208000000008418c21101800082008c04880020408240008008c008200400ac22010448401040002820001000000080000080002030000020180030000001480988003240082802888080802200164000010208024040b0810a0004041208004200060224000400200040002b20b0409016048a1020080100210102001f12c40000ac0880144020048000603280802800014210400001001240001018022008400a100001908900000830420000018000260000800000640001040004600012c000060",
  "miner": "0xf02ddb48eda520c915c0dabadc70ba12d1b49ad2",
  "nonce": "0x0000000000000000",
  "number": "0x4a1dbd",
  "parentHash": "0x93434fec7bb917a3fdf071d43e6bda1061eab7c4d5663192ad88353925b49849",
  "receiptsRoot": "0x088e158e77aa12898ed02e3ec20f030cd18054cd56890ad56a78a7ffadd4f5f3",
  "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  "size": "0x11f8",
  "stateRoot": "0xa7b30b5144ee34730d42b8ea7b5f240d830190ad83b098ddce0e89978ea1055a",
  "timestamp": "0x18bc6e3b748",
  "totalDifficulty": "0x0",
  "transactions": [
      "0xc42bbbe43ffbaa15c3f6fdd335eba12adf37520313d15b5496e77a67fbd35b1d",
      "0x3df5b3ddaab690c68ed9e94cc086f210555d8ecc39c34aad292e8bd623b827d7",
      "0xf708668e3002ec8a079e803e041091d8d5f2a7c3161a75c5c40592c18a9c67d2",
      "0x5411f16773adac1f407d0161d3a0627fece66479e244d040d856e065794d4f04",
      "0x0ba4819248cd220f1beb3f72482ef15f14121d7ab28d2deab0656452945db38b",
      "0x9e01fd45eedbdf68665531dc9795f97c7a130b6bfbe633095b92f94a21c96adb",
      "0xe227e458184c315517ea2c926d6505a6d53528d86acc1b29758f7c85e42a9821"
  ],
  "transactionsRoot": "0xa34082f12ca71f5b7111e910ed9fb1350e5692a09456a37edc791f055e0603e5",
  "uncles": []
}

Steps to Reproduce

To reproduce the issue above, simply build the Header using the block returned by the eth_getBlockBy* and notice the hash doesn't match:

use ethereum::Header;
use hex_literal::hex;

let header = Header {
    parent_hash: hex!("93434fec7bb917a3fdf071d43e6bda1061eab7c4d5663192ad88353925b49849").into(),
    ommers_hash: hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").into(),,
    beneficiary: hex!("f02ddb48eda520c915c0dabadc70ba12d1b49ad2").into(),
    state_root: hex!("a7b30b5144ee34730d42b8ea7b5f240d830190ad83b098ddce0e89978ea1055a").into(),
    transactions_root: hex!("a34082f12ca71f5b7111e910ed9fb1350e5692a09456a37edc791f055e0603e5").into(),
    receipts_root: hex!("088e158e77aa12898ed02e3ec20f030cd18054cd56890ad56a78a7ffadd4f5f3").into(),
    logs_bloom: hex!("825080001000080802020000108400509000410406003440c08000402084300000021000910005c000300400022000004b000000210228020008001c31208000000008418c21101800082008c04880020408240008008c008200400ac22010448401040002820001000000080000080002030000020180030000001480988003240082802888080802200164000010208024040b0810a0004041208004200060224000400200040002b20b0409016048a1020080100210102001f12c40000ac0880144020048000603280802800014210400001001240001018022008400a100001908900000830420000018000260000800000640001040004600012c000060").into(),
    difficulty: Default::default(),
    number: 0x004a1dbd.into(),
    gas_limit: 0x00e4e1c0.into(),
    gas_used: 0x004e6b11.into(),
    timestamp: 0x6551a2be,
    extra_data: Default::default(),
    mix_hash: Default::default(),
    nonce: Default::default(),
};

let expected = hex!("84471574a6645fe3c4ea4fa7fb588f53f8cea681dd92d27d2de7c23f3040a7aa").into();
let actual = header.hash();
assert_eq(expected, actual);

For testing the baseFeePerGas example, see the reth RLP encoder as reference: https://github.com/paradigmxyz/reth/blob/v0.1.0-alpha.10/crates/primitives/src/header.rs#L324-L391

arthur-cw commented 10 months ago

hey @sorpaas, this is indeed happening, is it worth it to create a PR to fix the return of the RPC API?

vedhavyas commented 5 months ago

hey @sorpaas, are you currently working on this fix ? wanted to check since we are seeing an issue with forge when trying to send transaction so we are falling back to legacy instead