Unable to re-create receipts root only for block 22982400 #2231
-
Expected Behaviormerkle-patricia-tree root based on receipts should match the block reported receipts root hash Actual Behaviorno match Steps to reproduce the behaviorI'm using merkle-patricia-tree package to generate the tries and proofs. Chain/Network: code: import { BaseTrie as Tree } from 'merkle-patricia-tree';
import { Receipt } from 'eth-object';
import { encode } from 'eth-util-lite';
const targetReceipt = await provider.send('eth_getTransactionReceipt', [txHash]);
const rpcBlock = await provider.send('eth_getBlockByHash', [targetReceipt.blockHash, false]);
const receipts = (
await Promise.all(
rpcBlock.transactions
.map(async (siblingTxHash) => {
return provider.send('eth_getTransactionReceipt', [siblingTxHash]);
})
)
).filter((_) => _);
const tree = new Tree();
await Promise.all(
receipts.map((siblingReceipt, index) => {
const siblingPath = encode(index);
let serializedReceipt = Receipt.fromRpc(siblingReceipt).serialize();
if (siblingReceipt.type && siblingReceipt.type != '0x0') {
serializedReceipt = Buffer.concat([Buffer.from([siblingReceipt.type]), serializedReceipt]);
}
return tree.put(siblingPath, serializedReceipt);
}),
);
const receiptsRoot = '0x' + tree.root.toString('hex');
if (receiptsRoot !== rpcBlock.receiptsRoot) {
console.error(receipts, {
receiptsRoot,
blockReceiptsRoot: blockHeader.block.receiptsRoot,
});
throw new Error('receiptsRoot mismatch');
} |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 3 replies
-
Thanks for the bug report! I can reproduce your observation that the receipts root for block 22982400 does not match the merkle root calculated from the receipts that are returned via RPC. Block 22982400 is an epoch block and it looks like this mismatch happens for all epoch blocks and all other blocks are fine. Epoch blocks have special epoch transactions to distribute the epoch rewards rewards (e.g. rewards for block 22982400). These won't show up as normal transactions in the RPC. I assume that the epoch transactions are the reason for the mismatch, although I have not yet looked up how receipts for them are handled. |
Beta Was this translation helpful? Give feedback.
-
When events are emitted by the block processing outside of user transaction (e.g. epoch rewards), then one "block receipt" is added to the block. This receipt can't be fetched with Combining these receipts makes the receiptsRoot match again: import { ethers } from "ethers";
import { BaseTrie as Tree } from 'merkle-patricia-tree';
import { Receipt } from 'eth-object';
import { encode } from 'eth-util-lite';
const provider = new ethers.JsonRpcProvider('https://forno.celo.org');
const block = '0x' + (22982400).toString(16);
const rpcBlock = await provider.send('eth_getBlockByNumber', [block, false]);
const transactionReceipts = (
await Promise.all(
rpcBlock.transactions
.map(async (siblingTxHash) => {
return provider.send('eth_getTransactionReceipt', [siblingTxHash]);
})
)
)
const blockReceipt = (
await Promise.all(
[rpcBlock.hash]
.map(async (siblingTxHash) => {
return provider.send('eth_getBlockReceipt', [siblingTxHash]);
})
)
)
const receipts = transactionReceipts.concat(blockReceipt);
const tree = new Tree();
await Promise.all(
receipts.map((siblingReceipt, index) => {
const siblingPath = encode(index);
let serializedReceipt = Receipt.fromRpc(siblingReceipt).serialize();
if (siblingReceipt.type && siblingReceipt.type != '0x0') {
serializedReceipt = Buffer.concat([Buffer.from([siblingReceipt.type]), serializedReceipt]);
}
return tree.put(siblingPath, serializedReceipt);
}),
);
const receiptsRoot = '0x' + tree.root.toString('hex');
if (receiptsRoot !== rpcBlock.receiptsRoot) {
console.error(receipts, {
receiptsRoot,
blockReceiptsRoot: rpcBlock.receiptsRoot,
});
throw new Error('receiptsRoot mismatch');
} I admit that this situation is currently under-documented. |
Beta Was this translation helpful? Give feedback.
-
(notes-to-self for future reference) It looks like the Celo-specific eth_getBlockReceipt was added in: From the PR:
|
Beta Was this translation helpful? Give feedback.
-
That sounds wrong to me. I'll investigate.
Yes, it is every 17280th block. See https://github.com/celo-org/epochs?tab=readme-ov-file#epoch-blocks. |
Beta Was this translation helpful? Give feedback.
When events are emitted by the block processing outside of user transaction (e.g. epoch rewards), then one "block receipt" is added to the block. This receipt can't be fetched with
eth_getTransactionReceipt
but with the Celo-specificeth_getBlockReceipt
.Combining these receipts makes the receiptsRoot match again: