Skip to content

Commit

Permalink
feat: StatusManager tracks seenTxs
Browse files Browse the repository at this point in the history
- use a composite key of `txHash+chainId` to track unique `EventFeed` submissions
  • Loading branch information
0xpatrickdev committed Nov 11, 2024
1 parent 2ede3a0 commit aa7d0dd
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 2 deletions.
29 changes: 27 additions & 2 deletions packages/fast-usdc/src/exos/status-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { CctpTxEvidenceShape, PendingTxShape } from '../typeGuards.js';
import { PendingTxStatus } from '../constants.js';

/**
* @import {MapStore} from '@agoric/store';
* @import {MapStore, SetStore} from '@agoric/store';
* @import {Zone} from '@agoric/zone';
* @import {CctpTxEvidence, NobleAddress, PendingTxKey, PendingTx} from '../types.js';
* @import {CctpTxEvidence, NobleAddress, SeenTxKey, PendingTxKey, PendingTx} from '../types.js';
*/

/**
Expand All @@ -29,6 +29,15 @@ const getPendingTxKey = evidence => {
return toPendingTxKey(forwardingAddress, amount);
};

/**
* Get the key for the seenTxs SetStore
* @param {CctpTxEvidence} evidence
*/
const getSeenKey = evidence => {
const { txHash, chainId } = evidence;
return /** @type {SeenTxKey} */ (JSON.stringify([txHash, chainId]));
};

/**
* The `StatusManager` keeps track of Pending and Seen Transactions
* via {@link PendingTxStatus} states, aiding in coordination between the `Advancer`
Expand All @@ -45,11 +54,27 @@ export const prepareStatusManager = zone => {
valueShape: M.arrayOf(PendingTxShape),
});

/** @type {SetStore<SeenTxKey>} */
const seenTxs = zone.setStore('SeenTxs', {
keyShape: M.string(),
});

/**
* Ensures that `txHash+chainId` has not been processed
* and adds entry to `seenTxs` set.
*
* Also records the CctpTxEvidence and status in `pendingTxs`.
*
* @param {CctpTxEvidence} evidence
* @param {PendingTxStatus} status
*/
const recordPendingTx = (evidence, status) => {
const seenKey = getSeenKey(evidence);
if (seenTxs.has(seenKey)) {
throw makeError(`Transaction already seen: ${q(seenKey)}`);
}
seenTxs.add(seenKey);

appendToStoredArray(
pendingTxs,
getPendingTxKey(evidence),
Expand Down
3 changes: 3 additions & 0 deletions packages/fast-usdc/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,7 @@ export interface PendingTx extends CctpTxEvidence {
/** composite of NobleAddress and transaction amount value */
export type PendingTxKey = `"["${NobleAddress}",${bigint}]"`;

/** composite of EvmHash and chainId */
export type SeenTxKey = `"["${EvmHash}",${number}]"`;

export type * from './constants.js';
23 changes: 23 additions & 0 deletions packages/fast-usdc/test/exos/status-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,29 @@ test('observe creates new entry with OBSERVED status', t => {
t.is(entries[0]?.status, PendingTxStatus.Observed);
});

test('cannot process same tx twice', t => {
const zone = provideDurableZone('status-test');
const statusManager = prepareStatusManager(zone.subZone('status-manager'));

const evidence = MockCctpTxEvidences.AGORIC_PLUS_OSMO();
statusManager.advance(evidence);

t.throws(() => statusManager.advance(evidence), {
message:
'Transaction already seen: "[[\\"0xc81bc6105b60a234c7c50ac17816ebcd5561d366df8bf3be59ff387552761702\\",1]]"',
});

t.throws(() => statusManager.observe(evidence), {
message:
'Transaction already seen: "[[\\"0xc81bc6105b60a234c7c50ac17816ebcd5561d366df8bf3be59ff387552761702\\",1]]"',
});

// new txHash should not throw
t.notThrows(() => statusManager.advance({ ...evidence, txHash: '0xtest2' }));
// new chainId with existing txHash should not throw
t.notThrows(() => statusManager.advance({ ...evidence, chainId: 9999 }));
});

test('settle removes entries from PendingTxs', t => {
const zone = provideDurableZone('status-test');
const statusManager = prepareStatusManager(zone.subZone('status-manager'));
Expand Down

0 comments on commit aa7d0dd

Please sign in to comment.