diff --git a/packages/fast-usdc/src/exos/transaction-feed.js b/packages/fast-usdc/src/exos/transaction-feed.js index b991c570022..87eacf07b3f 100644 --- a/packages/fast-usdc/src/exos/transaction-feed.js +++ b/packages/fast-usdc/src/exos/transaction-feed.js @@ -124,7 +124,7 @@ export const prepareTransactionFeedKit = (zone, zcf) => { * @param {string} operatorId */ attest(evidence, operatorId) { - const { pending } = this.state; + const { operators, pending } = this.state; trace('submitEvidence', operatorId, evidence); // TODO validate that it's a valid for Fast USDC before accepting @@ -146,18 +146,32 @@ export const prepareTransactionFeedKit = (zone, zcf) => { const found = [...pending.values()].filter(store => store.has(txHash), ); - // TODO determine the real policy for checking agreement - if (found.length < pending.getSize()) { - // not all have seen it + trace('found these stores with the txHash', found.length); + const minAttestations = Math.ceil(operators.getSize() / 2); + trace( + 'transaction', + txHash, + 'has', + found.length, + 'of', + minAttestations, + 'necessary attestations', + ); + if (found.length < minAttestations) { return; } // TODO verify that all found deep equal - // all agree, so remove from pending and publish - for (const pendingStore of pending.values()) { - pendingStore.delete(txHash); + // sufficient agreement, so remove from pending and publish + for (const store of found) { + store.delete(txHash); } + trace( + 'stores with the txHash after delete()', + [...pending.values()].filter(store => store.has(txHash)).length, + ); + trace('publishing evidence', evidence); publisher.publish(evidence); }, }, diff --git a/packages/fast-usdc/test/exos/transaction-feed.test.ts b/packages/fast-usdc/test/exos/transaction-feed.test.ts index 8e3b04f9271..5c5b74e094b 100644 --- a/packages/fast-usdc/test/exos/transaction-feed.test.ts +++ b/packages/fast-usdc/test/exos/transaction-feed.test.ts @@ -47,31 +47,30 @@ test('happy aggregation', async t => { const evidenceSubscriber = feedKit.public.getEvidenceSubscriber(); const { op1, op2, op3 } = await makeOperators(feedKit); - const evidence = MockCctpTxEvidences.AGORIC_PLUS_OSMO(); - op1.operator.submitEvidence(evidence); - op2.operator.submitEvidence(evidence); - op3.operator.submitEvidence(evidence); + const e1 = MockCctpTxEvidences.AGORIC_PLUS_OSMO(); + op1.operator.submitEvidence(e1); + op2.operator.submitEvidence(e1); + // Publishes with 2 of 3 const accepted = await evidenceSubscriber.getUpdateSince(0); t.deepEqual(accepted, { - value: evidence, + value: e1, updateCount: 1n, }); - // verify that it doesn't publish until three match - // once it publishes, it doesn't remember that it already saw these - op1.operator.submitEvidence(evidence); - op2.operator.submitEvidence(evidence); - // but this time the third is different - op3.operator.submitEvidence(MockCctpTxEvidences.AGORIC_PLUS_DYDX()); - + // Now third operator catches up with same evidence already published + op3.operator.submitEvidence(e1); t.like(await evidenceSubscriber.getUpdateSince(0), { - // Update count is still 1 + // The confirming evidence doesn't change anything updateCount: 1n, }); - op3.operator.submitEvidence(evidence); + + const e2 = MockCctpTxEvidences.AGORIC_PLUS_DYDX(); + assert(e1.txHash !== e2.txHash); + op1.operator.submitEvidence(e2); t.like(await evidenceSubscriber.getUpdateSince(0), { - updateCount: 2n, + // op1 attestation insufficient + updateCount: 1n, }); });