Reference apps: the oracle's write helper waits for an empty deployer mempool and re-reads before sending (a duplicate submit landed once)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
09d451a0ec
commit
1c0b4874fa
1 changed files with 11 additions and 4 deletions
|
|
@ -284,6 +284,11 @@ export function recordWrite(entry) {
|
|||
writeFileSync(p, JSON.stringify(d, null, 1) + '\n');
|
||||
}
|
||||
// The public RPC is a pool of nodes; a read right after a receipt can land on one that lags. Poll until it agrees.
|
||||
// Never send while one of our transactions is still pending, then read the pre-check again: a stale read on a
|
||||
// lagging node must not turn into a second, identical submission (seen once on 8 October 2026, nonces 8 and 9).
|
||||
async function quietMempool(pub, address) {
|
||||
await untilVisible(async () => (await pub.getTransactionCount({ address, blockTag: 'pending' })) === (await pub.getTransactionCount({ address, blockTag: 'latest' })), 'an empty mempool for the deployer');
|
||||
}
|
||||
async function untilVisible(check, what) {
|
||||
for (let i = 0; i < 30; i++) { if (await check()) return; await new Promise(r => setTimeout(r, 1000)); }
|
||||
throw new Error(what + ' is not visible on the RPC 30 s after its receipt');
|
||||
|
|
@ -293,6 +298,8 @@ export async function ensureCertificate(pub, wallet, d, index, checkpoint, bitma
|
|||
const verifierAddr = await pub.readContract({ address: d.oracle.address, abi: artifact('IgneumStateOracle').abi, functionName: 'verifier' });
|
||||
const same = async () => (await pub.readContract({ address: verifierAddr, abi: stubAbi, functionName: 'finalCheckpoint', args: [index] })).toLowerCase() === checkpoint.toLowerCase();
|
||||
if (await same()) return { verifier: verifierAddr, already: true };
|
||||
await quietMempool(pub, wallet.account.address);
|
||||
if (await same()) return { verifier: verifierAddr, already: true };
|
||||
const hash = await wallet.writeContract({ address: verifierAddr, abi: stubAbi, functionName: 'submitCertificate', args: [index, checkpoint, bitmap, signature] });
|
||||
const r = await pub.waitForTransactionReceipt({ hash, timeout: 180_000 });
|
||||
if (r.status !== 'success') throw new Error('submitCertificate reverted in ' + hash);
|
||||
|
|
@ -303,10 +310,10 @@ export async function ensureCertificate(pub, wallet, d, index, checkpoint, bitma
|
|||
}
|
||||
export async function ensureStateRoot(pub, wallet, d, v) {
|
||||
const abi = artifact('IgneumStateOracle').abi;
|
||||
try {
|
||||
const [root] = await pub.readContract({ address: d.oracle.address, abi, functionName: 'stateRoot', args: [v.number] });
|
||||
if (root.toLowerCase() === hex0x(v.postRoot).toLowerCase()) return { already: true };
|
||||
} catch {}
|
||||
const stored = async () => { try { const [root] = await pub.readContract({ address: d.oracle.address, abi, functionName: 'stateRoot', args: [v.number] }); return root.toLowerCase() === hex0x(v.postRoot).toLowerCase(); } catch { return false; } };
|
||||
if (await stored()) return { already: true };
|
||||
await quietMempool(pub, wallet.account.address);
|
||||
if (await stored()) return { already: true };
|
||||
const args = proofArgs(v);
|
||||
const gas = await pub.estimateContractGas({ address: d.oracle.address, abi, functionName: 'submitStateRoot', args, account: wallet.account });
|
||||
const hash = await wallet.writeContract({ address: d.oracle.address, abi, functionName: 'submitStateRoot', args, gas: gas + gas / 5n });
|
||||
|
|
|
|||
Loading…
Reference in a new issue