Skip to content

Commit

Permalink
feat: clean up upon last attest
Browse files Browse the repository at this point in the history
Before this each third attest was treated as a new transaction
  • Loading branch information
turadg committed Mar 3, 2025
1 parent c4259c7 commit 5fe02af
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 160 deletions.
66 changes: 38 additions & 28 deletions packages/fast-usdc/src/exos/transaction-feed.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/** @file Exo for @see {prepareTransactionFeedKit} */
import { makeTracer } from '@agoric/internal';
import { prepareDurablePublishKit } from '@agoric/notifier';
import { keyEQ, M } from '@endo/patterns';
import { Fail, quote } from '@endo/errors';
import { keyEQ, M } from '@endo/patterns';
import { CctpTxEvidenceShape, RiskAssessmentShape } from '../type-guards.js';
import { defineInertInvitation } from '../utils/zoe.js';
import { prepareOperatorKit } from './operator-kit.js';
Expand Down Expand Up @@ -65,6 +65,7 @@ export const stateShape = {
pending: M.remotable(),
risks: M.remotable(),
};
harden(stateShape);

/**
* A TransactionFeed is responsible for finding quorum among oracles.
Expand Down Expand Up @@ -154,11 +155,13 @@ export const prepareTransactionFeedKit = (zone, zcf) => {

/** @param {string} operatorId */
removeOperator(operatorId) {
const { operators } = this.state;
const { operators, pending, risks } = this.state;
trace('removeOperator', operatorId);
const operatorKit = operators.get(operatorId);
operatorKit.admin.disable();
operators.delete(operatorId);
pending.delete(operatorId);
risks.delete(operatorId);
},
},
operatorPowers: {
Expand Down Expand Up @@ -217,40 +220,47 @@ export const prepareTransactionFeedKit = (zone, zcf) => {
}
}

{
const minAttestations = Math.ceil(operators.getSize() / 2);
trace(
'transaction',
txHash,
'has',
found.length,
'of',
minAttestations,
'necessary attestations',
);
if (found.length < minAttestations) {
return;
}
const minAttestations = Math.ceil(operators.getSize() / 2);
trace(
'transaction',
txHash,
'has',
found.length,
'of',
minAttestations,
'necessary attestations',
);

if (found.length < minAttestations) {
// wait for more
return;
}

const riskStores = [...risks.values()].filter(store =>
store.has(txHash),
);
// take the union of risks identified from all operators
const risksIdentified = allRisksIdentified(riskStores, txHash);

// sufficient agreement, so remove from pending risks, then publish
for (const store of found) {
store.delete(txHash);
// Publish at the threshold of agreement
if (found.length === minAttestations) {
// take the union of risks identified from all operators
const risksIdentified = allRisksIdentified(riskStores, txHash);
trace('publishing evidence', evidence, risksIdentified);
publisher.publish({
evidence,
risk: { risksIdentified },
});
return;
}
for (const store of riskStores) {
store.delete(txHash);

if (found.length === pending.getSize()) {
// all have reported so clean up
for (const store of found) {
store.delete(txHash);
}
for (const store of riskStores) {
store.delete(txHash);
}
}
trace('publishing evidence', evidence, risksIdentified);
publisher.publish({
evidence,
risk: { risksIdentified },
});
},
},
public: {
Expand Down
36 changes: 27 additions & 9 deletions packages/fast-usdc/test/exos/transaction-feed.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js';

import { deeplyFulfilledObject } from '@agoric/internal';
import { makeHeapZone } from '@agoric/zone';
import { eventLoopIteration } from '@agoric/internal/src/testing-utils.js';
import {
prepareTransactionFeedKit,
stateShape,
Expand Down Expand Up @@ -185,31 +186,48 @@ test('disagreement after publishing', async t => {
updateCount: 1n,
});

// it's simply ignored
t.notThrows(() => op3.operator.submitEvidence(e1bad));
t.throws(() => op3.operator.submitEvidence(e1bad), {
message: /conflicting evidence/,
});
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);
// Disagreement is still detected after publishing
t.throws(() => op1.operator.submitEvidence(e1bad), {
message: /conflicting evidence/,
});
t.like(await evidenceSubscriber.getUpdateSince(0), {
updateCount: 2n,
updateCount: 1n,
});
});

test('remove operator', async t => {
const feedKit = makeFeedKit();
const { op1 } = await makeOperators(feedKit);
const evidenceSubscriber = feedKit.public.getEvidenceSubscriber();
const { op1, op2 } = await makeOperators(feedKit);
const evidence = MockCctpTxEvidences.AGORIC_PLUS_OSMO();

let published;
void evidenceSubscriber
.getUpdateSince(0)
.then(accepted => (published = accepted.value.evidence));

// works before disabling
op1.operator.submitEvidence(evidence);
await eventLoopIteration();
t.falsy(published);

await feedKit.creator.removeOperator('op1');

// remove op1 and their in-flight evidence
feedKit.creator.removeOperator('op1');
t.throws(() => op1.operator.submitEvidence(evidence), {
message: 'submitEvidence for disabled operator',
});

// one attestation is now sufficient (half of two) but the only evidence was just removed
await eventLoopIteration();
t.falsy(published);
op2.operator.submitEvidence(evidence);
await eventLoopIteration();
t.deepEqual(published, evidence);
});
140 changes: 17 additions & 123 deletions packages/fast-usdc/test/snapshots/fast-usdc.contract.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -3103,158 +3103,52 @@ Generated by [AVA](https://avajs.dev).
},
},
pending: {
'operator-0': {},
'operator-1': {
'0x000008': {
'operator-0': {
'0x0000010': {
aux: {
forwardingChannel: 'channel-21',
recipientAddress: 'agoric10rchqqgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq06924zr6mmnd4hnzvr5wscqq9qe499xw',
recipientAddress: 'agoric10rchqqgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq06924zr6mmnd4hnzetpwfkrxqq5g04wk9',
},
blockHash: '0x90d7343e04f8160892e94f02d6a9b9f255663ed0ac34caca98544c8143fee665',
blockNumber: 21037671n,
blockNumber: 21037673n,
blockTimestamp: 1632340000n,
chainId: 1,
tx: {
amount: 5000000n,
forwardingAddress: 'noble1x0ydg69dh6fqvr27xjvp6maqmrldam6yfelqkd',
sender: '0xDefaultFakeEthereumAddress',
},
txHash: '0x000008',
txHash: '0x0000010',
},
},
'operator-2': {
'0x000001': {
aux: {
forwardingChannel: 'channel-21',
recipientAddress: 'agoric10rchqqgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq06924zr6mmnd4hnzv3nqq2qhf2ep2',
},
blockHash: '0x90d7343e04f8160892e94f02d6a9b9f255663ed0ac34caca98544c8143fee665',
blockNumber: 21037664n,
blockTimestamp: 1632340000n,
chainId: 1,
tx: {
amount: 1000000n,
forwardingAddress: 'noble1x0ydg69dh6fqvr27xjvp6maqmrldam6yfelqkd',
sender: '0xDefaultFakeEthereumAddress',
},
txHash: '0x000001',
},
'0x000002': {
aux: {
forwardingChannel: 'channel-21',
recipientAddress: 'agoric10rchqqgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq06924zr6mmnd4hnzv3nx3skganpde3k2jrpwpc8jqq5jzq8ex',
},
blockHash: '0x90d7343e04f8160892e94f02d6a9b9f255663ed0ac34caca98544c8143fee665',
blockNumber: 21037665n,
blockTimestamp: 1632340000n,
chainId: 1,
tx: {
amount: 108000000n,
forwardingAddress: 'noble1x0ydg69dh6fqvr27xjvp6maqmrldam6yfelqkd',
sender: '0xDefaultFakeEthereumAddress',
},
txHash: '0x000002',
},
'0x000003': {
aux: {
forwardingChannel: 'channel-21',
recipientAddress: 'agoric10rchqqgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq06924zr6mmnd4hnzv3nx3ssq9qfqjyle',
},
blockHash: '0x90d7343e04f8160892e94f02d6a9b9f255663ed0ac34caca98544c8143fee665',
blockNumber: 21037666n,
blockTimestamp: 1632340000n,
chainId: 1,
tx: {
amount: 110000000n,
forwardingAddress: 'noble1x0ydg69dh6fqvr27xjvp6maqmrldam6yfelqkd',
sender: '0xDefaultFakeEthereumAddress',
},
txHash: '0x000003',
},
'0x000004': {
aux: {
forwardingChannel: 'channel-21',
recipientAddress: 'agoric10rchqqgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq06924zr6mmnd4hnzv3nx33qq9q874uwh',
},
blockHash: '0x90d7343e04f8160892e94f02d6a9b9f255663ed0ac34caca98544c8143fee665',
blockNumber: 21037667n,
blockTimestamp: 1632340000n,
chainId: 1,
tx: {
amount: 120000000n,
forwardingAddress: 'noble1x0ydg69dh6fqvr27xjvp6maqmrldam6yfelqkd',
sender: '0xDefaultFakeEthereumAddress',
},
txHash: '0x000004',
},
'0x000005': {
aux: {
forwardingChannel: 'channel-21',
recipientAddress: 'agoric10rchqqgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq06924zr6mmnd4hnzv3nx33sq9qdp5z6c',
},
blockHash: '0x90d7343e04f8160892e94f02d6a9b9f255663ed0ac34caca98544c8143fee665',
blockNumber: 21037668n,
blockTimestamp: 1632340000n,
chainId: 1,
tx: {
amount: 125000000n,
forwardingAddress: 'noble1x0ydg69dh6fqvr27xjvp6maqmrldam6yfelqkd',
sender: '0xDefaultFakeEthereumAddress',
},
txHash: '0x000005',
},
'0x000006': {
aux: {
forwardingChannel: 'channel-21',
recipientAddress: 'agoric10rchqqgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq06924zr6mmnd4hnzv3nvfhkydgqzs8gv9xu',
},
blockHash: '0x90d7343e04f8160892e94f02d6a9b9f255663ed0ac34caca98544c8143fee665',
blockNumber: 21037669n,
blockTimestamp: 1632340000n,
chainId: 1,
tx: {
amount: 6000000n,
forwardingAddress: 'noble1x0ydg69dh6fqvr27xjvp6maqmrldam6yfelqkd',
sender: '0xDefaultFakeEthereumAddress',
},
txHash: '0x000006',
},
'0x000007': {
'operator-1': {
'0x0000010': {
aux: {
forwardingChannel: 'channel-21',
recipientAddress: 'agoric10rchqqgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq06924zr6mmnd4hnzv3nqq2qhf2ep2',
recipientAddress: 'agoric10rchqqgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq06924zr6mmnd4hnzetpwfkrxqq5g04wk9',
},
blockHash: '0x90d7343e04f8160892e94f02d6a9b9f255663ed0ac34caca98544c8143fee665',
blockNumber: 21037670n,
blockNumber: 21037673n,
blockTimestamp: 1632340000n,
chainId: 1,
tx: {
amount: 150000000n,
amount: 5000000n,
forwardingAddress: 'noble1x0ydg69dh6fqvr27xjvp6maqmrldam6yfelqkd',
sender: '0xDefaultFakeEthereumAddress',
},
txHash: '0x000007',
txHash: '0x0000010',
},
},
'operator-2': {},
},
risks: {
'operator-0': {},
'operator-1': {
'0x000008': {},
'operator-0': {
'0x0000010': {},
},
'operator-2': {
'0x000001': {
risksIdentified: [
'RISK1',
],
},
'0x000002': {},
'0x000003': {},
'0x000004': {},
'0x000005': {},
'0x000006': {},
'0x000007': {},
'operator-1': {
'0x0000010': {},
},
'operator-2': {},
},
settlerKit: {
creator: Object @Alleged: Fast USDC Settler creator {},
Expand Down
Binary file modified packages/fast-usdc/test/snapshots/fast-usdc.contract.test.ts.snap
Binary file not shown.

0 comments on commit 5fe02af

Please sign in to comment.