From 1f47164a792b64f5b4a27992156646d87670782c Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Thu, 5 Dec 2024 16:52:59 -0800 Subject: [PATCH 01/10] fix: vstorage fastUsdc path --- packages/fast-usdc/src/util/agoric.js | 2 +- packages/fast-usdc/test/cli/transfer.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/fast-usdc/src/util/agoric.js b/packages/fast-usdc/src/util/agoric.js index 1439f60ffda..4f7e4d65254 100644 --- a/packages/fast-usdc/src/util/agoric.js +++ b/packages/fast-usdc/src/util/agoric.js @@ -5,7 +5,7 @@ export const queryFastUSDCLocalChainAccount = async ( out = console, ) => { const agoricAddr = await vstorage.readLatest( - 'published.fastUSDC.settlementAccount', + 'published.fastUsdc.settlementAccount', ); out.log(`Got Fast USDC Local Chain Account ${agoricAddr}`); return agoricAddr; diff --git a/packages/fast-usdc/test/cli/transfer.test.ts b/packages/fast-usdc/test/cli/transfer.test.ts index d3c319f1f6b..729f40f63cc 100644 --- a/packages/fast-usdc/test/cli/transfer.test.ts +++ b/packages/fast-usdc/test/cli/transfer.test.ts @@ -64,7 +64,7 @@ test('Transfer registers the noble forwarding account if it does not exist', asy const out = mockOut(); const file = mockFile(path, JSON.stringify(config)); const agoricSettlementAccount = 'agoric123456'; - const settlementAccountVstoragePath = 'published.fastUSDC.settlementAccount'; + const settlementAccountVstoragePath = 'published.fastUsdc.settlementAccount'; const vstorageMock = makeVstorageMock({ [settlementAccountVstoragePath]: agoricSettlementAccount, }); @@ -115,7 +115,7 @@ test('Transfer signs and broadcasts the depositForBurn message on Ethereum', asy const out = mockOut(); const file = mockFile(path, JSON.stringify(config)); const agoricSettlementAccount = 'agoric123456'; - const settlementAccountVstoragePath = 'published.fastUSDC.settlementAccount'; + const settlementAccountVstoragePath = 'published.fastUsdc.settlementAccount'; const vstorageMock = makeVstorageMock({ [settlementAccountVstoragePath]: agoricSettlementAccount, }); From fe99b81a0fc4a868485475ca0cc2b623c44a2d19 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Thu, 5 Dec 2024 16:47:37 -0800 Subject: [PATCH 02/10] refactor: makeStatusNode to statusNode eref --- packages/fast-usdc/src/exos/status-manager.js | 7 ++--- packages/fast-usdc/src/fast-usdc.contract.js | 4 +-- packages/fast-usdc/test/exos/advancer.test.ts | 2 +- packages/fast-usdc/test/exos/settler.test.ts | 2 +- .../test/exos/status-manager.test.ts | 31 +++++++++---------- 5 files changed, 22 insertions(+), 24 deletions(-) diff --git a/packages/fast-usdc/src/exos/status-manager.js b/packages/fast-usdc/src/exos/status-manager.js index ad4c33c44d1..7cb2ad6df51 100644 --- a/packages/fast-usdc/src/exos/status-manager.js +++ b/packages/fast-usdc/src/exos/status-manager.js @@ -68,12 +68,12 @@ const seenTxKeyOf = evidence => { * XXX consider separate facets for `Advancing` and `Settling` capabilities. * * @param {Zone} zone - * @param {() => Promise} makeStatusNode + * @param {ERef} transactionsNode * @param {StatusManagerPowers} caps */ export const prepareStatusManager = ( zone, - makeStatusNode, + transactionsNode, { log = makeTracer('Advancer', true), } = /** @type {StatusManagerPowers} */ ({}), @@ -94,8 +94,7 @@ export const prepareStatusManager = ( * @param {TxStatus} status */ const recordStatus = (hash, status) => { - const statusNodeP = makeStatusNode(); - const txnNodeP = E(statusNodeP).makeChildNode(hash); + const txnNodeP = E(transactionsNode).makeChildNode(hash); // Don't await, just writing to vstorage. void E(txnNodeP).setValue(status); }; diff --git a/packages/fast-usdc/src/fast-usdc.contract.js b/packages/fast-usdc/src/fast-usdc.contract.js index 91dbdebdc2c..734bd423a3c 100644 --- a/packages/fast-usdc/src/fast-usdc.contract.js +++ b/packages/fast-usdc/src/fast-usdc.contract.js @@ -95,8 +95,8 @@ export const contract = async (zcf, privateArgs, zone, tools) => { marshaller, ); - const makeStatusNode = () => E(storageNode).makeChildNode(STATUS_NODE); - const statusManager = prepareStatusManager(zone, makeStatusNode); + const statusNode = E(storageNode).makeChildNode(STATUS_NODE); + const statusManager = prepareStatusManager(zone, statusNode); const { USDC } = terms.brands; const { withdrawToSeat } = tools.zoeTools; diff --git a/packages/fast-usdc/test/exos/advancer.test.ts b/packages/fast-usdc/test/exos/advancer.test.ts index 818554dacca..0c8d984c839 100644 --- a/packages/fast-usdc/test/exos/advancer.test.ts +++ b/packages/fast-usdc/test/exos/advancer.test.ts @@ -44,7 +44,7 @@ const createTestExtensions = (t, common: CommonSetup) => { const statusManager = prepareStatusManager( rootZone.subZone('status-manager'), - async () => storageNode.makeChildNode('status'), + storageNode.makeChildNode('status'), ); const mockAccounts = prepareMockOrchAccounts(rootZone.subZone('accounts'), { diff --git a/packages/fast-usdc/test/exos/settler.test.ts b/packages/fast-usdc/test/exos/settler.test.ts index f900c7cd191..2c8075e9e2f 100644 --- a/packages/fast-usdc/test/exos/settler.test.ts +++ b/packages/fast-usdc/test/exos/settler.test.ts @@ -47,7 +47,7 @@ const makeTestContext = async t => { const { log, inspectLogs } = makeTestLogger(t.log); const statusManager = prepareStatusManager( zone.subZone('status-manager'), - async () => common.commonPrivateArgs.storageNode.makeChildNode('status'), + common.commonPrivateArgs.storageNode.makeChildNode('status'), { log }, ); const { zcf, callLog } = mockZcf(zone.subZone('Mock ZCF')); diff --git a/packages/fast-usdc/test/exos/status-manager.test.ts b/packages/fast-usdc/test/exos/status-manager.test.ts index d37015f0d57..7932d81e630 100644 --- a/packages/fast-usdc/test/exos/status-manager.test.ts +++ b/packages/fast-usdc/test/exos/status-manager.test.ts @@ -10,7 +10,7 @@ import type { CctpTxEvidence } from '../../src/types.js'; type Common = Awaited>; type TestContext = { - makeStatusNode: () => Promise; + statusNode: ERef; storage: Common['bootstrap']['storage']; }; @@ -19,8 +19,7 @@ const test = anyTest as TestFn; test.beforeEach(async t => { const common = await commonSetup(t); t.context = { - makeStatusNode: async () => - common.commonPrivateArgs.storageNode.makeChildNode('status'), + statusNode: common.commonPrivateArgs.storageNode.makeChildNode('status'), storage: common.bootstrap.storage, }; }); @@ -29,7 +28,7 @@ test('advance creates new entry with ADVANCED status', t => { const zone = provideDurableZone('status-test'); const statusManager = prepareStatusManager( zone.subZone('status-manager'), - t.context.makeStatusNode, + t.context.statusNode, ); const evidence = MockCctpTxEvidences.AGORIC_PLUS_OSMO(); @@ -47,7 +46,7 @@ test('ADVANCED transactions are published to vstorage', async t => { const zone = provideDurableZone('status-test'); const statusManager = prepareStatusManager( zone.subZone('status-manager'), - t.context.makeStatusNode, + t.context.statusNode, ); const evidence = MockCctpTxEvidences.AGORIC_PLUS_OSMO(); @@ -65,7 +64,7 @@ test('observe creates new entry with OBSERVED status', t => { const zone = provideDurableZone('status-test'); const statusManager = prepareStatusManager( zone.subZone('status-manager'), - t.context.makeStatusNode, + t.context.statusNode, ); const evidence = MockCctpTxEvidences.AGORIC_PLUS_OSMO(); statusManager.observe(evidence); @@ -82,7 +81,7 @@ test('OBSERVED transactions are published to vstorage', async t => { const zone = provideDurableZone('status-test'); const statusManager = prepareStatusManager( zone.subZone('status-manager'), - t.context.makeStatusNode, + t.context.statusNode, ); const evidence = MockCctpTxEvidences.AGORIC_PLUS_OSMO(); @@ -100,7 +99,7 @@ test('cannot process same tx twice', t => { const zone = provideDurableZone('status-test'); const statusManager = prepareStatusManager( zone.subZone('status-manager'), - t.context.makeStatusNode, + t.context.statusNode, ); const evidence = MockCctpTxEvidences.AGORIC_PLUS_OSMO(); statusManager.advance(evidence); @@ -125,7 +124,7 @@ test('isSeen checks if a tx has been processed', t => { const zone = provideDurableZone('status-test'); const statusManager = prepareStatusManager( zone.subZone('status-manager'), - t.context.makeStatusNode, + t.context.statusNode, ); const e1 = MockCctpTxEvidences.AGORIC_PLUS_OSMO(); @@ -143,7 +142,7 @@ test('dequeueStatus removes entries from PendingTxs', t => { const zone = provideDurableZone('status-test'); const statusManager = prepareStatusManager( zone.subZone('status-manager'), - t.context.makeStatusNode, + t.context.statusNode, ); const e1 = MockCctpTxEvidences.AGORIC_PLUS_OSMO(); const e2 = MockCctpTxEvidences.AGORIC_PLUS_DYDX(); @@ -195,7 +194,7 @@ test('cannot advanceOutcome without ADVANCING entry', t => { const zone = provideDurableZone('status-test'); const statusManager = prepareStatusManager( zone.subZone('status-manager'), - t.context.makeStatusNode, + t.context.statusNode, ); const e1 = MockCctpTxEvidences.AGORIC_PLUS_OSMO(); const advanceOutcomeFn = () => @@ -224,7 +223,7 @@ test('advanceOutcome transitions to ADVANCED and ADVANCE_FAILED', async t => { const zone = provideDurableZone('status-test'); const statusManager = prepareStatusManager( zone.subZone('status-manager'), - t.context.makeStatusNode, + t.context.statusNode, ); const e1 = MockCctpTxEvidences.AGORIC_PLUS_OSMO(); const e2 = MockCctpTxEvidences.AGORIC_PLUS_DYDX(); @@ -260,7 +259,7 @@ test('dequeueStatus returns undefined when nothing is settleable', t => { const zone = provideDurableZone('status-test'); const statusManager = prepareStatusManager( zone.subZone('status-manager'), - t.context.makeStatusNode, + t.context.statusNode, ); const e1 = MockCctpTxEvidences.AGORIC_PLUS_OSMO(); @@ -274,7 +273,7 @@ test('dequeueStatus returns first (earliest) matched entry', async t => { const zone = provideDurableZone('status-test'); const statusManager = prepareStatusManager( zone.subZone('status-manager'), - t.context.makeStatusNode, + t.context.statusNode, ); const evidence = MockCctpTxEvidences.AGORIC_PLUS_OSMO(); @@ -371,7 +370,7 @@ test('lookupPending returns empty array when presented a key it has not seen', t const zone = provideDurableZone('status-test'); const statusManager = prepareStatusManager( zone.subZone('status-manager'), - t.context.makeStatusNode, + t.context.statusNode, ); t.deepEqual(statusManager.lookupPending('noble123', 1n), []); }); @@ -380,7 +379,7 @@ test('StatusManagerKey logic handles addresses with hyphens', t => { const zone = provideDurableZone('status-test'); const statusManager = prepareStatusManager( zone.subZone('status-manager'), - t.context.makeStatusNode, + t.context.statusNode, ); const evidence: CctpTxEvidence = MockCctpTxEvidences.AGORIC_PLUS_OSMO(); evidence.tx.forwardingAddress = 'noble1-foo'; From 057977f53612ef925e35ea13c0b61b03f084f8bd Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Fri, 6 Dec 2024 14:34:19 -0800 Subject: [PATCH 03/10] docs: composite keys --- packages/fast-usdc/src/exos/status-manager.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/fast-usdc/src/exos/status-manager.js b/packages/fast-usdc/src/exos/status-manager.js index 7cb2ad6df51..653808d1389 100644 --- a/packages/fast-usdc/src/exos/status-manager.js +++ b/packages/fast-usdc/src/exos/status-manager.js @@ -19,8 +19,7 @@ import { PendingTxStatus, TxStatus } from '../constants.js'; /** * Create the key for the pendingTxs MapStore. * - * The key is a composite of `txHash` and `chainId` and not meant to be - * parsable. + * The key is a composite but not meant to be parsable. * * @param {NobleAddress} addr * @param {bigint} amount @@ -43,8 +42,7 @@ const pendingTxKeyOf = evidence => { /** * Get the key for the seenTxs SetStore. * - * The key is a composite of `NobleAddress` and transaction `amount` and not - * meant to be parsable. + * The key is a composite but not meant to be parsable. * * @param {CctpTxEvidence} evidence * @returns {SeenTxKey} From 6ddc959c3ea83ae5f05495a8f80a88ff72ecde44 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Fri, 6 Dec 2024 15:01:27 -0800 Subject: [PATCH 04/10] test: replace lookupPending with vstorage test --- packages/fast-usdc/test/exos/advancer.test.ts | 50 ++++++------------- 1 file changed, 15 insertions(+), 35 deletions(-) diff --git a/packages/fast-usdc/test/exos/advancer.test.ts b/packages/fast-usdc/test/exos/advancer.test.ts index 0c8d984c839..908880d3fb4 100644 --- a/packages/fast-usdc/test/exos/advancer.test.ts +++ b/packages/fast-usdc/test/exos/advancer.test.ts @@ -162,6 +162,7 @@ test('updates status to ADVANCING in happy path', async t => { mocks: { mockPoolAccount, resolveLocalTransferV }, }, brands: { usdc }, + bootstrap: { storage }, } = t.context; const mockEvidence = MockCctpTxEvidences.AGORIC_PLUS_OSMO(); @@ -174,14 +175,9 @@ test('updates status to ADVANCING in happy path', async t => { // wait for handleTransactionEvent to do work await eventLoopIteration(); - const entries = statusManager.lookupPending( - mockEvidence.tx.forwardingAddress, - mockEvidence.tx.amount, - ); - t.deepEqual( - entries, - [{ ...mockEvidence, status: PendingTxStatus.Advancing }], + storage.data.get(`mockChainStorageRoot.status.${mockEvidence.txHash}`), + PendingTxStatus.Advancing, 'ADVANCED status in happy path', ); @@ -210,6 +206,7 @@ test('updates status to ADVANCING in happy path', async t => { test('updates status to OBSERVED on insufficient pool funds', async t => { const { + bootstrap: { storage }, extensions: { services: { makeAdvancer, statusManager }, helpers: { inspectLogs }, @@ -229,14 +226,9 @@ test('updates status to OBSERVED on insufficient pool funds', async t => { void advancer.handleTransactionEvent(mockEvidence); await eventLoopIteration(); - const entries = statusManager.lookupPending( - mockEvidence.tx.forwardingAddress, - mockEvidence.tx.amount, - ); - t.deepEqual( - entries, - [{ ...mockEvidence, status: PendingTxStatus.Observed }], + storage.data.get(`mockChainStorageRoot.status.${mockEvidence.txHash}`), + PendingTxStatus.Observed, 'OBSERVED status on insufficient pool funds', ); @@ -248,6 +240,7 @@ test('updates status to OBSERVED on insufficient pool funds', async t => { test('updates status to OBSERVED if makeChainAddress fails', async t => { const { + bootstrap: { storage }, extensions: { services: { advancer, statusManager }, helpers: { inspectLogs }, @@ -257,14 +250,9 @@ test('updates status to OBSERVED if makeChainAddress fails', async t => { const mockEvidence = MockCctpTxEvidences.AGORIC_UNKNOWN_EUD(); await advancer.handleTransactionEvent(mockEvidence); - const entries = statusManager.lookupPending( - mockEvidence.tx.forwardingAddress, - mockEvidence.tx.amount, - ); - t.deepEqual( - entries, - [{ ...mockEvidence, status: PendingTxStatus.Observed }], + storage.data.get(`mockChainStorageRoot.status.${mockEvidence.txHash}`), + PendingTxStatus.Observed, 'OBSERVED status on makeChainAddress failure', ); @@ -276,6 +264,7 @@ test('updates status to OBSERVED if makeChainAddress fails', async t => { test('calls notifyAdvancingResult (AdvancedFailed) on failed transfer', async t => { const { + bootstrap: { storage }, extensions: { services: { advancer, feeTools, statusManager }, helpers: { inspectLogs, inspectNotifyCalls }, @@ -291,14 +280,9 @@ test('calls notifyAdvancingResult (AdvancedFailed) on failed transfer', async t resolveLocalTransferV(); await eventLoopIteration(); - const entries = statusManager.lookupPending( - mockEvidence.tx.forwardingAddress, - mockEvidence.tx.amount, - ); - t.deepEqual( - entries, - [{ ...mockEvidence, status: PendingTxStatus.Advancing }], + storage.data.get(`mockChainStorageRoot.status.${mockEvidence.txHash}`), + PendingTxStatus.Advancing, 'tx is Advancing', ); @@ -333,6 +317,7 @@ test('calls notifyAdvancingResult (AdvancedFailed) on failed transfer', async t test('updates status to OBSERVED if pre-condition checks fail', async t => { const { + bootstrap: { storage }, extensions: { services: { advancer, statusManager }, helpers: { inspectLogs }, @@ -343,14 +328,9 @@ test('updates status to OBSERVED if pre-condition checks fail', async t => { await advancer.handleTransactionEvent(mockEvidence); - const entries = statusManager.lookupPending( - mockEvidence.tx.forwardingAddress, - mockEvidence.tx.amount, - ); - t.deepEqual( - entries, - [{ ...mockEvidence, status: PendingTxStatus.Observed }], + storage.data.get(`mockChainStorageRoot.status.${mockEvidence.txHash}`), + PendingTxStatus.Observed, 'tx is recorded as OBSERVED', ); From b04cb9825efb09c3c21d4cf246c0dd0f75512970 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Fri, 6 Dec 2024 15:14:55 -0800 Subject: [PATCH 05/10] refactor: recordStatus -> publishStatus "record" suggests updating local data. "publish" is our term for going out over vstorage. --- packages/fast-usdc/src/exos/status-manager.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/fast-usdc/src/exos/status-manager.js b/packages/fast-usdc/src/exos/status-manager.js index 653808d1389..b5afe7e71db 100644 --- a/packages/fast-usdc/src/exos/status-manager.js +++ b/packages/fast-usdc/src/exos/status-manager.js @@ -91,7 +91,7 @@ export const prepareStatusManager = ( * @param {CctpTxEvidence['txHash']} hash * @param {TxStatus} status */ - const recordStatus = (hash, status) => { + const publishStatus = (hash, status) => { const txnNodeP = E(transactionsNode).makeChildNode(hash); // Don't await, just writing to vstorage. void E(txnNodeP).setValue(status); @@ -118,7 +118,7 @@ export const prepareStatusManager = ( pendingTxKeyOf(evidence), harden({ ...evidence, status }), ); - recordStatus(evidence.txHash, status); + publishStatus(evidence.txHash, status); }; return zone.exo( @@ -185,7 +185,7 @@ export const prepareStatusManager = ( : PendingTxStatus.AdvanceFailed; const txpost = { ...tx, status }; pendingTxs.set(key, harden([...prefix, txpost, ...suffix])); - recordStatus(tx.txHash, status); + publishStatus(tx.txHash, status); }, /** @@ -244,7 +244,7 @@ export const prepareStatusManager = ( * @param {EvmHash} txHash */ disbursed(txHash) { - recordStatus(txHash, TxStatus.Disbursed); + publishStatus(txHash, TxStatus.Disbursed); }, /** @@ -256,7 +256,7 @@ export const prepareStatusManager = ( */ forwarded(txHash, address, amount) { if (txHash) { - recordStatus(txHash, TxStatus.Forwarded); + publishStatus(txHash, TxStatus.Forwarded); } else { // TODO store (early) `Minted` transactions to check against incoming evidence log( From a4dc42f4b76d1f046daf32c850a1f60946bb1a65 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Fri, 6 Dec 2024 16:25:58 -0800 Subject: [PATCH 06/10] docs: advance() skips OBSERVED --- packages/fast-usdc/src/exos/status-manager.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/fast-usdc/src/exos/status-manager.js b/packages/fast-usdc/src/exos/status-manager.js index b5afe7e71db..df0a8979890 100644 --- a/packages/fast-usdc/src/exos/status-manager.js +++ b/packages/fast-usdc/src/exos/status-manager.js @@ -153,6 +153,9 @@ export const prepareStatusManager = ( { /** * Add a new transaction with ADVANCING status + * + * NB: this acts like observe() but skips recording the OBSERVED state + * * @param {CctpTxEvidence} evidence */ advance(evidence) { From b42455078bb83131f337f66fd999f29f30bef7c0 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Fri, 6 Dec 2024 16:46:57 -0800 Subject: [PATCH 07/10] refactor: initPendingTx and setPendingTxStatus --- packages/fast-usdc/src/exos/status-manager.js | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/packages/fast-usdc/src/exos/status-manager.js b/packages/fast-usdc/src/exos/status-manager.js index df0a8979890..ffe742ecd83 100644 --- a/packages/fast-usdc/src/exos/status-manager.js +++ b/packages/fast-usdc/src/exos/status-manager.js @@ -106,7 +106,7 @@ export const prepareStatusManager = ( * @param {CctpTxEvidence} evidence * @param {PendingTxStatus} status */ - const recordPendingTx = (evidence, status) => { + const initPendingTx = (evidence, status) => { const seenKey = seenTxKeyOf(evidence); if (seenTxs.has(seenKey)) { throw makeError(`Transaction already seen: ${q(seenKey)}`); @@ -121,6 +121,28 @@ export const prepareStatusManager = ( publishStatus(evidence.txHash, status); }; + /** + * Update the pending transaction status. + * + * @param {{sender: NobleAddress, amount: bigint}} keyParts + * @param {PendingTxStatus} status + */ + function setPendingTxStatus({ sender, amount }, status) { + const key = makePendingTxKey(sender, amount); + pendingTxs.has(key) || Fail`no advancing tx with ${{ sender, amount }}`; + const pending = pendingTxs.get(key); + const ix = pending.findIndex(tx => tx.status === PendingTxStatus.Advancing); + ix >= 0 || Fail`no advancing tx with ${{ sender, amount }}`; + const [prefix, tx, suffix] = [ + pending.slice(0, ix), + pending[ix], + pending.slice(ix + 1), + ]; + const txpost = { ...tx, status }; + pendingTxs.set(key, harden([...prefix, txpost, ...suffix])); + publishStatus(tx.txHash, status); + } + return zone.exo( 'Fast USDC Status Manager', M.interface('StatusManagerI', { @@ -159,7 +181,7 @@ export const prepareStatusManager = ( * @param {CctpTxEvidence} evidence */ advance(evidence) { - recordPendingTx(evidence, PendingTxStatus.Advancing); + initPendingTx(evidence, PendingTxStatus.Advancing); }, /** @@ -171,24 +193,10 @@ export const prepareStatusManager = ( * @throws {Error} if nothing to advance */ advanceOutcome(sender, amount, success) { - const key = makePendingTxKey(sender, amount); - pendingTxs.has(key) || Fail`no advancing tx with ${{ sender, amount }}`; - const pending = pendingTxs.get(key); - const ix = pending.findIndex( - tx => tx.status === PendingTxStatus.Advancing, + setPendingTxStatus( + { sender, amount }, + success ? PendingTxStatus.Advanced : PendingTxStatus.AdvanceFailed, ); - ix >= 0 || Fail`no advancing tx with ${{ sender, amount }}`; - const [prefix, tx, suffix] = [ - pending.slice(0, ix), - pending[ix], - pending.slice(ix + 1), - ]; - const status = success - ? PendingTxStatus.Advanced - : PendingTxStatus.AdvanceFailed; - const txpost = { ...tx, status }; - pendingTxs.set(key, harden([...prefix, txpost, ...suffix])); - publishStatus(tx.txHash, status); }, /** @@ -196,7 +204,7 @@ export const prepareStatusManager = ( * @param {CctpTxEvidence} evidence */ observe(evidence) { - recordPendingTx(evidence, PendingTxStatus.Observed); + initPendingTx(evidence, PendingTxStatus.Observed); }, /** From c7a31676f5d5eab68c51f6af890dd590a9c4a87d Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Fri, 6 Dec 2024 17:07:07 -0800 Subject: [PATCH 08/10] refactor(types): don't export internal keys --- packages/fast-usdc/src/exos/status-manager.js | 7 ++++++- packages/fast-usdc/src/types.ts | 6 ------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/fast-usdc/src/exos/status-manager.js b/packages/fast-usdc/src/exos/status-manager.js index ffe742ecd83..c49e955b25d 100644 --- a/packages/fast-usdc/src/exos/status-manager.js +++ b/packages/fast-usdc/src/exos/status-manager.js @@ -13,7 +13,12 @@ import { PendingTxStatus, TxStatus } from '../constants.js'; /** * @import {MapStore, SetStore} from '@agoric/store'; * @import {Zone} from '@agoric/zone'; - * @import {CctpTxEvidence, NobleAddress, SeenTxKey, PendingTxKey, PendingTx, EvmHash, LogFn} from '../types.js'; + * @import {CctpTxEvidence, NobleAddress, PendingTx, EvmHash, LogFn} from '../types.js'; + */ + +/** + * @typedef {`pendingTx:${string}`} PendingTxKey + * @typedef {`seenTx:${string}`} SeenTxKey */ /** diff --git a/packages/fast-usdc/src/types.ts b/packages/fast-usdc/src/types.ts index cedb38b9b98..ee9a90efffd 100644 --- a/packages/fast-usdc/src/types.ts +++ b/packages/fast-usdc/src/types.ts @@ -38,12 +38,6 @@ export interface PendingTx extends CctpTxEvidence { status: PendingTxStatus; } -/** internal key for `StatusManager` exo */ -export type PendingTxKey = `pendingTx:${string}`; - -/** internal key for `StatusManager` exo */ -export type SeenTxKey = `seenTx:${string}`; - export type FeeConfig = { flat: Amount<'nat'>; variableRate: Ratio; From 5751bf5fbe55e92bbafbfe83fd91a080df7213e3 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Fri, 6 Dec 2024 17:11:57 -0800 Subject: [PATCH 09/10] refactor: simplify db keys --- packages/fast-usdc/src/exos/status-manager.js | 13 +++++++++---- packages/fast-usdc/test/exos/status-manager.test.ts | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/fast-usdc/src/exos/status-manager.js b/packages/fast-usdc/src/exos/status-manager.js index c49e955b25d..ca17487e01b 100644 --- a/packages/fast-usdc/src/exos/status-manager.js +++ b/packages/fast-usdc/src/exos/status-manager.js @@ -17,8 +17,11 @@ import { PendingTxStatus, TxStatus } from '../constants.js'; */ /** - * @typedef {`pendingTx:${string}`} PendingTxKey - * @typedef {`seenTx:${string}`} SeenTxKey + * @typedef {`pendingTx:${bigint}:${NobleAddress}`} PendingTxKey + * The string template is for developer visibility but not meant to ever be parsed. + * + * @typedef {`seenTx:${string}:${EvmHash}`} SeenTxKey + * The string template is for developer visibility but not meant to ever be parsed. */ /** @@ -31,7 +34,8 @@ import { PendingTxStatus, TxStatus } from '../constants.js'; * @returns {PendingTxKey} */ const makePendingTxKey = (addr, amount) => - `pendingTx:${JSON.stringify([addr, String(amount)])}`; + // amount can't contain colon + `pendingTx:${amount}:${addr}`; /** * Get the key for the pendingTxs MapStore. @@ -54,7 +58,8 @@ const pendingTxKeyOf = evidence => { */ const seenTxKeyOf = evidence => { const { txHash, chainId } = evidence; - return `seenTx:${JSON.stringify([txHash, chainId])}`; + // chainId can't contain colon + return `seenTx:${chainId}:${txHash}`; }; /** diff --git a/packages/fast-usdc/test/exos/status-manager.test.ts b/packages/fast-usdc/test/exos/status-manager.test.ts index 7932d81e630..71757ea731a 100644 --- a/packages/fast-usdc/test/exos/status-manager.test.ts +++ b/packages/fast-usdc/test/exos/status-manager.test.ts @@ -106,12 +106,12 @@ test('cannot process same tx twice', t => { t.throws(() => statusManager.advance(evidence), { message: - 'Transaction already seen: "seenTx:[\\"0xc81bc6105b60a234c7c50ac17816ebcd5561d366df8bf3be59ff387552761702\\",1]"', + 'Transaction already seen: "seenTx:1:0xc81bc6105b60a234c7c50ac17816ebcd5561d366df8bf3be59ff387552761702"', }); t.throws(() => statusManager.observe(evidence), { message: - 'Transaction already seen: "seenTx:[\\"0xc81bc6105b60a234c7c50ac17816ebcd5561d366df8bf3be59ff387552761702\\",1]"', + 'Transaction already seen: "seenTx:1:0xc81bc6105b60a234c7c50ac17816ebcd5561d366df8bf3be59ff387552761702"', }); // new txHash should not throw From f77b0ec1bd2328aff175c92237bbfd528720ff66 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Mon, 9 Dec 2024 17:17:07 -0800 Subject: [PATCH 10/10] lint: cleanup unused eslint-disable --- packages/boot/test/bootstrapTests/vaults-upgrade.test.ts | 1 - packages/boot/test/bootstrapTests/vtransfer.test.ts | 1 - packages/boot/test/upgrading/upgrade-vats.test.ts | 1 - 3 files changed, 3 deletions(-) diff --git a/packages/boot/test/bootstrapTests/vaults-upgrade.test.ts b/packages/boot/test/bootstrapTests/vaults-upgrade.test.ts index 8d1787a1ab1..940339eec69 100644 --- a/packages/boot/test/bootstrapTests/vaults-upgrade.test.ts +++ b/packages/boot/test/bootstrapTests/vaults-upgrade.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable @jessie.js/safe-await-separator */ /** * @file Bootstrap test integration vaults with smart-wallet. The tests in this * file are NOT independent; a single `test.before()` handler creates shared diff --git a/packages/boot/test/bootstrapTests/vtransfer.test.ts b/packages/boot/test/bootstrapTests/vtransfer.test.ts index fd0bec558ac..7f865e596fe 100644 --- a/packages/boot/test/bootstrapTests/vtransfer.test.ts +++ b/packages/boot/test/bootstrapTests/vtransfer.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable @jessie.js/safe-await-separator -- confused by casting 'as' */ import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js'; import type { TestFn } from 'ava'; diff --git a/packages/boot/test/upgrading/upgrade-vats.test.ts b/packages/boot/test/upgrading/upgrade-vats.test.ts index d5a3d22c14f..9711c7dd121 100644 --- a/packages/boot/test/upgrading/upgrade-vats.test.ts +++ b/packages/boot/test/upgrading/upgrade-vats.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable @jessie.js/safe-await-separator -- test */ import { test } from '@agoric/swingset-vat/tools/prepare-test-env-ava.js'; import { BridgeId, deepCopyJsonable } from '@agoric/internal';