Skip to content

Commit

Permalink
feat: error on conflicting evidence
Browse files Browse the repository at this point in the history
  • Loading branch information
turadg committed Dec 17, 2024
1 parent bc28201 commit cd2a40c
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 2 deletions.
25 changes: 23 additions & 2 deletions packages/fast-usdc/src/exos/transaction-feed.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { makeTracer } from '@agoric/internal';
import { prepareDurablePublishKit } from '@agoric/notifier';
import { M } from '@endo/patterns';
import { keyEQ, M } from '@endo/patterns';
import { Fail } from '@endo/errors';
import { CctpTxEvidenceShape } from '../type-guards.js';
import { defineInertInvitation } from '../utils/zoe.js';
import { prepareOperatorKit } from './operator-kit.js';
Expand Down Expand Up @@ -127,6 +128,7 @@ export const prepareTransactionFeedKit = (zone, zcf) => {
const { operators, pending } = this.state;
trace('submitEvidence', operatorId, evidence);

// TODO https://github.com/Agoric/agoric-sdk/pull/10720
// TODO validate that it's a valid for Fast USDC before accepting
// E.g. that the `recipientAddress` is the FU settlement account and that
// the EUD is a chain supported by FU.
Expand Down Expand Up @@ -160,7 +162,26 @@ export const prepareTransactionFeedKit = (zone, zcf) => {
return;
}

// TODO verify that all found deep equal
let lastEvidence;
for (const store of found) {
const next = store.get(txHash);
if (lastEvidence) {
if (keyEQ(lastEvidence, next)) {
lastEvidence = next;
} else {
trace(
'🚨 conflicting evidence for',
txHash,
':',
lastEvidence,
'!=',
next,
);
Fail`conflicting evidence for ${txHash}`;
}
}
lastEvidence = next;
}

// sufficient agreement, so remove from pending and publish
for (const store of found) {
Expand Down
43 changes: 43 additions & 0 deletions packages/fast-usdc/test/exos/transaction-feed.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ test('happy aggregation', async t => {
const evidenceSubscriber = feedKit.public.getEvidenceSubscriber();

const { op1, op2, op3 } = await makeOperators(feedKit);

const e1 = MockCctpTxEvidences.AGORIC_PLUS_OSMO();
op1.operator.submitEvidence(e1);
op2.operator.submitEvidence(e1);
Expand Down Expand Up @@ -74,6 +75,48 @@ test('happy aggregation', async t => {
});
});

test('disagreement', async t => {
const feedKit = makeFeedKit();
const { op1, op2 } = await makeOperators(feedKit);
const e1 = MockCctpTxEvidences.AGORIC_PLUS_OSMO();
const e1bad = { ...e1, tx: { ...e1.tx, amount: 999_999_999n } };
assert(e1.txHash === e1bad.txHash);
op1.operator.submitEvidence(e1);

t.throws(() => op2.operator.submitEvidence(e1bad), {
message:
'conflicting evidence for "0xc81bc6105b60a234c7c50ac17816ebcd5561d366df8bf3be59ff387552761702"',
});
});

test('disagreement after publishing', async t => {
const feedKit = makeFeedKit();
const evidenceSubscriber = feedKit.public.getEvidenceSubscriber();
const { op1, op2, op3 } = await makeOperators(feedKit);
const e1 = MockCctpTxEvidences.AGORIC_PLUS_OSMO();
const e1bad = { ...e1, tx: { ...e1.tx, amount: 999_999_999n } };
assert(e1.txHash === e1bad.txHash);
op1.operator.submitEvidence(e1);
op2.operator.submitEvidence(e1);

t.like(await evidenceSubscriber.getUpdateSince(0), {
updateCount: 1n,
});

// it's simply ignored
t.notThrows(() => op3.operator.submitEvidence(e1bad));
t.like(await evidenceSubscriber.getUpdateSince(0), {
updateCount: 1n,
});

// now another op repeats the bad evidence, so it's published to the stream.
// It's the responsibility of the Advancer to fail because it has already processed that tx hash.
op1.operator.submitEvidence(e1bad);
t.like(await evidenceSubscriber.getUpdateSince(0), {
updateCount: 2n,
});
});

test('disabled operator', async t => {
const feedKit = makeFeedKit();
const { op1 } = await makeOperators(feedKit);
Expand Down

0 comments on commit cd2a40c

Please sign in to comment.