From 9642661e8f25c67db26c324442dd665f893fd246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 31 Jul 2024 16:55:11 -0300 Subject: [PATCH 01/25] add txID property to global resource cache --- packages/account/src/providers/memory-cache.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/account/src/providers/memory-cache.ts b/packages/account/src/providers/memory-cache.ts index e0050a2513b..e8c6f81a786 100644 --- a/packages/account/src/providers/memory-cache.ts +++ b/packages/account/src/providers/memory-cache.ts @@ -6,6 +6,7 @@ type Cache = { [key: string]: { expires: number; value: BytesLike; + txID: string; }; }; const cache: Cache = {}; // it's a cache hash ~~> cash? @@ -38,12 +39,13 @@ export class MemoryCache { return undefined; } - set(value: BytesLike): number { + set(value: BytesLike, txID: string): number { const expiresAt = Date.now() + this.ttl; const key = hexlify(value); cache[key] = { expires: expiresAt, value, + txID, }; return expiresAt; From eaf33c373ca9c465d6b63b6aa1405aa4a5802074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 31 Jul 2024 16:56:15 -0300 Subject: [PATCH 02/25] implement method delByTxID --- packages/account/src/providers/memory-cache.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/account/src/providers/memory-cache.ts b/packages/account/src/providers/memory-cache.ts index e8c6f81a786..bb3454d26c7 100644 --- a/packages/account/src/providers/memory-cache.ts +++ b/packages/account/src/providers/memory-cache.ts @@ -77,4 +77,12 @@ export class MemoryCache { const key = hexlify(value); delete cache[key]; } + + delByTxID(txID: string) { + Object.keys(cache).forEach((key) => { + if (cache[key].txID === txID) { + delete cache[key]; + } + }); + } } From 35ce52a2675abbf7a41c4c26cb6073001a6b6d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 31 Jul 2024 16:56:57 -0300 Subject: [PATCH 03/25] using TX ID when setting the cache --- packages/account/src/providers/provider.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/account/src/providers/provider.ts b/packages/account/src/providers/provider.ts index 1d514c4ec6b..d9d67b4202a 100644 --- a/packages/account/src/providers/provider.ts +++ b/packages/account/src/providers/provider.ts @@ -683,14 +683,14 @@ Supported fuel-core version: ${supportedVersion}.` /** * @hidden */ - #cacheInputs(inputs: TransactionRequestInput[]): void { + #cacheInputs(inputs: TransactionRequestInput[], transactionId: string): void { if (!this.cache) { return; } inputs.forEach((input) => { if (input.type === InputType.Coin) { - this.cache?.set(input.id); + this.cache?.set(input.id, transactionId); } }); } @@ -727,7 +727,7 @@ Supported fuel-core version: ${supportedVersion}.` const { submit: { id: transactionId }, } = await this.operations.submit({ encodedTransaction }); - this.#cacheInputs(transactionRequest.inputs); + this.#cacheInputs(transactionRequest.inputs, transactionId); return new TransactionResponse(transactionId, this, abis); } From 421260c62238a30accba0b0e2a5f102ab27f91ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 31 Jul 2024 16:58:00 -0300 Subject: [PATCH 04/25] unsettig cached resources when TX fails or squeezed out --- .../providers/transaction-response/transaction-response.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/account/src/providers/transaction-response/transaction-response.ts b/packages/account/src/providers/transaction-response/transaction-response.ts index 201e5b53dec..4199c5b72a3 100644 --- a/packages/account/src/providers/transaction-response/transaction-response.ts +++ b/packages/account/src/providers/transaction-response/transaction-response.ts @@ -228,6 +228,7 @@ export class TransactionResponse { for await (const { statusChange } of subscription) { if (statusChange.type === 'SqueezedOutStatus') { + this.unsetResourceCache(); throw new FuelError( ErrorCode.TRANSACTION_SQUEEZED_OUT, `Transaction Squeezed Out with reason: ${statusChange.reason}` @@ -278,6 +279,7 @@ export class TransactionResponse { const { gqlTransaction, receipts } = transactionResult; if (gqlTransaction.status?.type === 'FailureStatus') { + this.unsetResourceCache(); const { reason } = gqlTransaction.status; throw extractTxError({ receipts, @@ -311,4 +313,8 @@ export class TransactionResponse { ): Promise> { return this.waitForResult(contractsAbiMap); } + + private unsetResourceCache() { + this.provider.cache?.delByTxID(this.id); + } } From cd0aea8364866df84e80121f7de984add56a4309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 31 Jul 2024 23:32:15 -0300 Subject: [PATCH 05/25] update memory cache test --- .../src/providers/memory-cache.test.ts | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/account/src/providers/memory-cache.test.ts b/packages/account/src/providers/memory-cache.test.ts index 11c0571d208..392ec1f88ea 100644 --- a/packages/account/src/providers/memory-cache.test.ts +++ b/packages/account/src/providers/memory-cache.test.ts @@ -1,3 +1,4 @@ +import { getRandomB256 } from '@fuel-ts/address'; import { randomBytes } from '@fuel-ts/crypto'; import type { BytesLike } from '@fuel-ts/interfaces'; import { hexlify } from '@fuel-ts/utils'; @@ -9,6 +10,8 @@ import { MemoryCache } from './memory-cache'; * @group browser */ describe('Memory Cache', () => { + const txID = getRandomB256(); + it('can construct [valid numerical ttl]', () => { const memCache = new MemoryCache(1000); @@ -53,14 +56,14 @@ describe('Memory Cache', () => { const memCache = new MemoryCache(ttl); const value = randomBytes(8); - expect(memCache.set(value)).toBeGreaterThanOrEqual(expiresAt); + expect(memCache.set(value, txID)).toBeGreaterThanOrEqual(expiresAt); }); it('can get [valid key]', () => { const value = randomBytes(8); const memCache = new MemoryCache(100); - memCache.set(value); + memCache.set(value, txID); expect(memCache.get(value)).toEqual(value); }); @@ -69,7 +72,7 @@ describe('Memory Cache', () => { const value = randomBytes(8); const memCache = new MemoryCache(100); - memCache.set(value); + memCache.set(value, txID); expect(memCache.get(value)).toEqual(value); }); @@ -78,7 +81,7 @@ describe('Memory Cache', () => { const value = randomBytes(8); const memCache = new MemoryCache(1); - memCache.set(value); + memCache.set(value, txID); await new Promise((resolve) => { setTimeout(resolve, 10); @@ -91,7 +94,7 @@ describe('Memory Cache', () => { const value = randomBytes(8); const memCache = new MemoryCache(1); - memCache.set(value); + memCache.set(value, txID); await new Promise((resolve) => { setTimeout(resolve, 10); @@ -104,7 +107,7 @@ describe('Memory Cache', () => { const value = randomBytes(8); const memCache = new MemoryCache(100); - memCache.set(value); + memCache.set(value, txID); memCache.del(value); expect(memCache.get(value)).toEqual(undefined); @@ -118,9 +121,9 @@ describe('Memory Cache', () => { const memCache = new MemoryCache(100); - memCache.set(value1); - memCache.set(value2); - memCache.set(value3); + memCache.set(value1, txID); + memCache.set(value2, txID); + memCache.set(value3, txID); expect(memCache.getActiveData()).containSubset(EXPECTED); }); @@ -132,11 +135,11 @@ describe('Memory Cache', () => { const EXPECTED: BytesLike[] = [value1, value2, oldValue]; let memCache = new MemoryCache(500); - memCache.set(value1); - memCache.set(value2); + memCache.set(value1, txID); + memCache.set(value2, txID); memCache = new MemoryCache(1); - memCache.set(oldValue); + memCache.set(oldValue, txID); await new Promise((resolve) => { setTimeout(resolve, 10); @@ -155,7 +158,7 @@ describe('Memory Cache', () => { const oldValue = randomBytes(8); const instance1 = new MemoryCache(1000); - instance1.set(oldValue); + instance1.set(oldValue, txID); await new Promise((resolve) => { setTimeout(resolve, 200); @@ -164,7 +167,7 @@ describe('Memory Cache', () => { const newValue = randomBytes(8); const instance2 = new MemoryCache(100); - instance2.set(newValue); + instance2.set(newValue, txID); const activeData = instance2.getActiveData(); From 6529f617d9aecef3852e7abfd38271d25bfb0d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 31 Jul 2024 23:32:23 -0300 Subject: [PATCH 06/25] add unit test --- .../account/src/providers/provider.test.ts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/packages/account/src/providers/provider.test.ts b/packages/account/src/providers/provider.test.ts index 167a58308e9..5acd09ad1dc 100644 --- a/packages/account/src/providers/provider.test.ts +++ b/packages/account/src/providers/provider.test.ts @@ -482,6 +482,53 @@ describe('Provider', () => { expect(cachedCoins).lengthOf(0); }); + it('should unset cached UTXOs when TX execution fails', async () => { + using launched = await setupTestProviderAndWallets({ + nodeOptions: { + args: ['--poa-instant', 'false', '--poa-interval-period', '2s'], + }, + walletsConfig: { + coinsPerAsset: 1, + amountPerCoin: 100_000_000, + }, + }); + const { + provider, + wallets: [wallet, receiver], + } = launched; + + const baseAssetId = provider.getBaseAssetId(); + const maxFee = 100_000; + const transferAmount = 10_000; + + const { coins } = await wallet.getCoins(baseAssetId); + const uniqueUtxoId = coins[0].id; + + const resources = await wallet.getResourcesToSpend([[transferAmount, baseAssetId]]); + + const request = new ScriptTransactionRequest({ + maxFee, + // No enough gas to execute the TX + gasLimit: 0 + }); + + request.addCoinOutput(receiver.address, transferAmount, baseAssetId); + request.addResources(resources); + + const submitted = await wallet.sendTransaction(request, { estimateTxDependencies: false }); + + expect(provider.cache?.getActiveData()).toContain(uniqueUtxoId); + + await expectToThrowFuelError( + () => submitted.waitForResult(), + { code: ErrorCode.SCRIPT_REVERTED } + ); + + // The caches was cleared + const cachedCoins = provider.cache?.getActiveData(); + expect(cachedCoins).lengthOf(0); + }); + it('should ensure cached UTXOs are not being queried', async () => { // Fund the wallet with 2 UTXOs const totalUtxos = 2; From 6bdbb3bd8cdd42aee58226b5b1b9717f493cb198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:45:17 -0300 Subject: [PATCH 07/25] add test case for memory cache --- .../src/providers/memory-cache.test.ts | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/packages/account/src/providers/memory-cache.test.ts b/packages/account/src/providers/memory-cache.test.ts index 392ec1f88ea..08d2737a868 100644 --- a/packages/account/src/providers/memory-cache.test.ts +++ b/packages/account/src/providers/memory-cache.test.ts @@ -154,6 +154,38 @@ describe('Memory Cache', () => { expect(memCache.getAllData()).containSubset(EXPECTED); }); + it('should delete cache by txID', () => { + const value1 = randomBytes(8); + const value2 = randomBytes(8); + const value3 = randomBytes(8); + + const ttl = 1000; + + const txId1 = 'tx-id-1'; + const txId2 = 'tx-id-2'; + + const memCache1 = new MemoryCache(ttl); + const memCache2 = new MemoryCache(ttl); + + memCache1.set(value1, txId1); + memCache1.set(value2, txId2); + + memCache2.set(value3, txId1); + + expect(memCache1.getActiveData()).toContain(value1); + expect(memCache1.getActiveData()).toContain(value2); + + expect(memCache2.getActiveData()).toContain(value3); + + memCache1.delByTxID(txId1); + + // Value 2 is from TX ID 2, so it should not be deleted + expect(memCache1.getActiveData()).toContain(value2); + + expect(memCache1.getActiveData()).not.toContain(value1); + expect(memCache2.getActiveData()).not.toContain(value3); + }) + it('should validate that MemoryCache uses a global cache', async () => { const oldValue = randomBytes(8); From 414e603014bdee6cf2ef1aaeb52eb347c0fe6352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:45:36 -0300 Subject: [PATCH 08/25] refact test suites --- .../account/src/providers/provider.test.ts | 62 ++++++++++--------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/packages/account/src/providers/provider.test.ts b/packages/account/src/providers/provider.test.ts index 5acd09ad1dc..24e3504eafd 100644 --- a/packages/account/src/providers/provider.test.ts +++ b/packages/account/src/providers/provider.test.ts @@ -435,12 +435,6 @@ describe('Provider', () => { const cachedCoins = provider.cache?.getActiveData() || []; expect(new Set(cachedCoins)).toEqual(new Set(EXPECTED)); - - // clear cache - const activeData = provider.cache?.getActiveData() || []; - activeData.forEach((coin) => { - provider.cache?.del(coin); - }); }); it('should NOT cache UTXOs when TX submission fails', async () => { @@ -462,6 +456,9 @@ describe('Provider', () => { const maxFee = 100_000; const transferAmount = 10_000; + const { coins } = await wallet.getCoins(baseAssetId); + const utxoIds = coins.map((c) => c.id); + // No enough funds to pay for the TX fee const resources = await wallet.getResourcesToSpend([[transferAmount, baseAssetId]]); @@ -478,18 +475,19 @@ describe('Provider', () => { ); // No UTXOs were cached since the TX submission failed - const cachedCoins = provider.cache?.getActiveData() || []; - expect(cachedCoins).lengthOf(0); + utxoIds.forEach((id) => { + expect(provider.cache?.get(id)).toBeUndefined(); + }); }); it('should unset cached UTXOs when TX execution fails', async () => { using launched = await setupTestProviderAndWallets({ nodeOptions: { - args: ['--poa-instant', 'false', '--poa-interval-period', '2s'], + args: ['--poa-instant', 'false', '--poa-interval-period', '1s'], }, walletsConfig: { - coinsPerAsset: 1, - amountPerCoin: 100_000_000, + coinsPerAsset: 2, + amountPerCoin: 100_000, }, }); const { @@ -502,31 +500,37 @@ describe('Provider', () => { const transferAmount = 10_000; const { coins } = await wallet.getCoins(baseAssetId); - const uniqueUtxoId = coins[0].id; + const utxoIds = coins.map((c) => c.id); - const resources = await wallet.getResourcesToSpend([[transferAmount, baseAssetId]]); + // Should fetch resources enough to pay for the TX fee and transfer amount + const resources = await wallet.getResourcesToSpend([[maxFee + transferAmount, baseAssetId]]); const request = new ScriptTransactionRequest({ maxFee, // No enough gas to execute the TX - gasLimit: 0 + gasLimit: 0, }); request.addCoinOutput(receiver.address, transferAmount, baseAssetId); request.addResources(resources); + // TX submission will succeed const submitted = await wallet.sendTransaction(request, { estimateTxDependencies: false }); - expect(provider.cache?.getActiveData()).toContain(uniqueUtxoId); + // UTXOs were cached since the TX submission succeeded + utxoIds.forEach((id) => { + expect(provider.cache?.get(id)).toBeDefined(); + }); - await expectToThrowFuelError( - () => submitted.waitForResult(), - { code: ErrorCode.SCRIPT_REVERTED } - ); + // TX execution will fail + await expectToThrowFuelError(() => submitted.waitForResult(), { + code: ErrorCode.SCRIPT_REVERTED, + }); - // The caches was cleared - const cachedCoins = provider.cache?.getActiveData(); - expect(cachedCoins).lengthOf(0); + // Ensure user's UTXOs were unset from the cache + utxoIds.forEach((id) => { + expect(provider.cache?.get(id)).toBeUndefined(); + }); }); it('should ensure cached UTXOs are not being queried', async () => { @@ -555,14 +559,14 @@ describe('Provider', () => { // One of the UTXOs will be cached as the TX submission was successful await wallet.transfer(receiver.address, transferAmount); - const cachedUtxos = provider.cache?.getActiveData() || []; - - // Ensure the cached UTXO is the only one in the cache - expect(cachedUtxos.length).toBe(1); + const cachedUtxos = provider.cache?.getActiveData(); // Determine the used UTXO and the unused UTXO - const usedUtxo = cachedUtxos[0]; - const unusedUtxos = coins.filter((coin) => coin.id !== usedUtxo); + const usedUtxo = coins.find((coin) => cachedUtxos?.includes(coin.id)); + const unusedUtxos = coins.filter((coin) => coin.id !== usedUtxo?.id); + + expect(usedUtxo).toBeDefined(); + expect(unusedUtxos).toBeDefined(); // Spy on the getCoinsToSpend method to ensure the cached UTXO is not being queried const resourcesToSpendSpy = vi.spyOn(provider.operations, 'getCoinsToSpend'); @@ -583,7 +587,7 @@ describe('Provider', () => { ], excludedIds: { messages: [], - utxos: [usedUtxo], + utxos: expect.arrayContaining([usedUtxo?.id]), }, }); }); From ca102ebfb90467dec5c0b06b53ea7c9735424448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:48:08 -0300 Subject: [PATCH 09/25] add changeset --- .changeset/lazy-plants-fry.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/lazy-plants-fry.md diff --git a/.changeset/lazy-plants-fry.md b/.changeset/lazy-plants-fry.md new file mode 100644 index 00000000000..a7116b45782 --- /dev/null +++ b/.changeset/lazy-plants-fry.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/account": patch +--- + +fix: unset cached resource for failed TX From 3413b65d8cc158a0cbc1e8253d1a0c88ef108078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:55:08 -0300 Subject: [PATCH 10/25] ajust test description --- packages/account/src/providers/memory-cache.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/account/src/providers/memory-cache.test.ts b/packages/account/src/providers/memory-cache.test.ts index 08d2737a868..5846b00ed62 100644 --- a/packages/account/src/providers/memory-cache.test.ts +++ b/packages/account/src/providers/memory-cache.test.ts @@ -154,12 +154,12 @@ describe('Memory Cache', () => { expect(memCache.getAllData()).containSubset(EXPECTED); }); - it('should delete cache by txID', () => { + it('should delete cached values by its txID', () => { const value1 = randomBytes(8); const value2 = randomBytes(8); const value3 = randomBytes(8); - const ttl = 1000; + const ttl = 1000; const txId1 = 'tx-id-1'; const txId2 = 'tx-id-2'; @@ -184,7 +184,7 @@ describe('Memory Cache', () => { expect(memCache1.getActiveData()).not.toContain(value1); expect(memCache2.getActiveData()).not.toContain(value3); - }) + }); it('should validate that MemoryCache uses a global cache', async () => { const oldValue = randomBytes(8); From ff0dc53ca2c792f166358df250da7e9d4e0d1431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Sun, 4 Aug 2024 23:09:14 -0300 Subject: [PATCH 11/25] implement ResourceCache class --- .../src/providers/resource-cache.test.ts | 166 ++++++++++++++++++ .../account/src/providers/resource-cache.ts | 75 ++++++++ 2 files changed, 241 insertions(+) create mode 100644 packages/account/src/providers/resource-cache.test.ts create mode 100644 packages/account/src/providers/resource-cache.ts diff --git a/packages/account/src/providers/resource-cache.test.ts b/packages/account/src/providers/resource-cache.test.ts new file mode 100644 index 00000000000..7742a852a20 --- /dev/null +++ b/packages/account/src/providers/resource-cache.test.ts @@ -0,0 +1,166 @@ +import { randomBytes } from '@fuel-ts/crypto'; +import { hexlify, sleep } from '@fuel-ts/utils'; + +import { ResourceCache } from './resource-cache'; + +/** + * @group node + * @group browser + */ +describe('Resource Cache', () => { + const randomValue = () => hexlify(randomBytes(32)); + + it('can instantiate [valid numerical ttl]', () => { + const memCache = new ResourceCache(1000); + + expect(memCache.ttl).toEqual(1000); + }); + + it('can memCache [invalid numerical ttl]', () => { + expect(() => new ResourceCache(-1)).toThrow(/Invalid TTL: -1. Use a value greater than zero./); + }); + + it('can memCache [invalid mistyped ttl]', () => { + // @ts-expect-error intentional invalid input + expect(() => new ResourceCache('bogus')).toThrow( + /Invalid TTL: bogus. Use a value greater than zero./ + ); + }); + + it('can validade if it is cached [UTXO]', () => { + const resourceCache = new ResourceCache(1000); + const utxoId = randomValue(); + + expect(resourceCache.isCached(utxoId)).toBeFalsy(); + + const txID = randomValue(); + resourceCache.set(txID, { utxos: [utxoId], messages: [] }); + + expect(resourceCache.isCached(utxoId)).toBeTruthy(); + }); + + it('can validade if it is cached [Message]', () => { + const resourceCache = new ResourceCache(1000); + const messageNonce = randomValue(); + + expect(resourceCache.isCached(messageNonce)).toBeFalsy(); + + const txID = randomValue(); + resourceCache.set(txID, { utxos: [], messages: [messageNonce] }); + + expect(resourceCache.isCached(messageNonce)).toBeTruthy(); + }); + + it('can get active [no data]', async () => { + const EXPECTED = { utxos: [], messages: [] }; + const resourceCache = new ResourceCache(1); + + await sleep(1); + + expect(resourceCache.getActiveData()).toStrictEqual(EXPECTED); + }); + + it('can get active', () => { + const EXPECTED = { + utxos: [randomValue(), randomValue()], + messages: [randomValue(), randomValue(), randomValue()], + }; + const resourceCache = new ResourceCache(1000); + + const txId = randomValue(); + resourceCache.set(txId, EXPECTED); + + const activeData = resourceCache.getActiveData(); + + expect(activeData.messages).containSubset(EXPECTED.messages); + expect(activeData.utxos).containSubset(EXPECTED.utxos); + }); + + it('should remove expired when getting active data', async () => { + const ttl = 1000; + const resourceCache = new ResourceCache(ttl); + + const txId1 = randomValue(); + const txId1Resources = { + utxos: [randomValue()], + messages: [randomValue()], + }; + + resourceCache.set(txId1, txId1Resources); + let activeData = resourceCache.getActiveData(); + + expect(activeData.utxos).containSubset(txId1Resources.utxos); + expect(activeData.messages).containSubset(txId1Resources.messages); + + await sleep(ttl); + + activeData = resourceCache.getActiveData(); + + expect(activeData.utxos.length).toEqual(0); + expect(activeData.messages.length).toEqual(0); + }); + + it('should remove cached data based on transaction ID', () => { + const ttl = 1000; + const resourceCache = new ResourceCache(ttl); + + const txId1 = randomValue(); + const txId2 = randomValue(); + + const txId1Resources = { + utxos: [randomValue()], + messages: [randomValue(), randomValue()], + }; + + const txId2Resources = { + utxos: [randomValue(), randomValue()], + messages: [randomValue()], + }; + + resourceCache.set(txId1, txId1Resources); + resourceCache.set(txId2, txId2Resources); + + let activeData = resourceCache.getActiveData(); + + expect(activeData.utxos).containSubset([...txId1Resources.utxos, ...txId2Resources.utxos]); + expect(activeData.messages).containSubset([ + ...txId1Resources.messages, + ...txId2Resources.messages, + ]); + + resourceCache.unset(txId1); + + activeData = resourceCache.getActiveData(); + + expect(activeData.utxos).not.containSubset(txId1Resources.utxos); + expect(activeData.messages).not.containSubset(txId1Resources.messages); + + expect(activeData.utxos).containSubset(txId2Resources.utxos); + expect(activeData.messages).containSubset(txId2Resources.messages); + }); + + it('should validate that ResourceCache uses a global cache', () => { + const oldTxId = randomValue(); + const oldCache = { + utxos: [randomValue(), randomValue()], + messages: [randomValue()], + }; + + const oldInstance = new ResourceCache(800); + oldInstance.set(oldTxId, oldCache); + + const newTxId = randomValue(); + const newCache = { + utxos: [randomValue()], + messages: [randomValue(), randomValue()], + }; + + const newInstance = new ResourceCache(300); + newInstance.set(newTxId, newCache); + + const activeData = newInstance.getActiveData(); + + expect(activeData.utxos).containSubset([...oldCache.utxos, ...newCache.utxos]); + expect(activeData.messages).containSubset([...oldCache.messages, ...newCache.messages]); + }); +}); diff --git a/packages/account/src/providers/resource-cache.ts b/packages/account/src/providers/resource-cache.ts new file mode 100644 index 00000000000..3dd77e78e25 --- /dev/null +++ b/packages/account/src/providers/resource-cache.ts @@ -0,0 +1,75 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { hexlify } from '@fuel-ts/utils'; + +import type { ExcludeResourcesOption } from './resource'; + +interface CachedResource { + utxos: Set; + messages: Set; + timestamp: number; +} + +const cache = new Map(); + +export class ResourceCache { + readonly ttl: number; + + constructor(ttl: number) { + this.ttl = ttl; // TTL in milliseconds + + if (typeof ttl !== 'number' || this.ttl <= 0) { + throw new FuelError( + ErrorCode.INVALID_TTL, + `Invalid TTL: ${this.ttl}. Use a value greater than zero.` + ); + } + } + + // Add resources to the cache + set(transactionId: string, resources: Required): void { + const currentTime = Date.now(); + const existingResources = cache.get(transactionId) || { + utxos: new Set(), + messages: new Set(), + timestamp: currentTime, + }; + + resources.utxos.forEach((utxo) => existingResources.utxos.add(hexlify(utxo))); + resources.messages.forEach((message) => existingResources.messages.add(hexlify(message))); + + cache.set(transactionId, existingResources); + } + + // Remove resources from the cache for a given transaction ID + unset(transactionId: string): void { + cache.delete(transactionId); + } + + // Get all cached resources and remove expired ones + getActiveData() { + const allResources: { utxos: string[]; messages: string[] } = { utxos: [], messages: [] }; + const currentTime = Date.now(); + cache.forEach((resource, transactionId) => { + if (currentTime - resource.timestamp < this.ttl) { + allResources.utxos.push(...resource.utxos); + allResources.messages.push(...resource.messages); + } else { + cache.delete(transactionId); + } + }); + return allResources; + } + + // Check if a UTXO ID or message nonce is already cached and not expired + isCached(key: string): boolean { + const currentTime = Date.now(); + for (const [transactionId, resourceData] of cache.entries()) { + if (currentTime - resourceData.timestamp > this.ttl) { + cache.delete(transactionId); + } else if (resourceData.utxos.has(key) || resourceData.messages.has(key)) { + return true; + } + } + return false; + } +} From 7eeb61fdf6c4e0fc7b8a466f85cac574caf0077d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Sun, 4 Aug 2024 23:10:00 -0300 Subject: [PATCH 12/25] refact code to use ResourceCache --- .../account/src/providers/provider.test.ts | 136 +++++++++++------- packages/account/src/providers/provider.ts | 36 +++-- .../transaction-response.ts | 2 +- .../src/funding-transaction.test.ts | 4 +- 4 files changed, 111 insertions(+), 67 deletions(-) diff --git a/packages/account/src/providers/provider.test.ts b/packages/account/src/providers/provider.test.ts index 24e3504eafd..2c3ee96a317 100644 --- a/packages/account/src/providers/provider.test.ts +++ b/packages/account/src/providers/provider.test.ts @@ -3,7 +3,6 @@ import { ZeroBytes32 } from '@fuel-ts/address/configs'; import { randomBytes } from '@fuel-ts/crypto'; import { FuelError, ErrorCode } from '@fuel-ts/errors'; import { expectToThrowFuelError, safeExec } from '@fuel-ts/errors/test-utils'; -import type { BytesLike } from '@fuel-ts/interfaces'; import { BN, bn } from '@fuel-ts/math'; import type { Receipt } from '@fuel-ts/transactions'; import { InputType, ReceiptType, TransactionType } from '@fuel-ts/transactions'; @@ -26,12 +25,15 @@ import { import { Wallet } from '../wallet'; import type { Coin } from './coin'; +import type { Message } from './message'; import type { ChainInfo, CursorPaginationArgs, NodeInfo } from './provider'; import Provider, { BLOCKS_PAGE_SIZE_LIMIT, - DEFAULT_UTXOS_CACHE_TTL, + DEFAULT_RESOURCE_CACHE_TTL, RESOURCES_PAGE_SIZE_LIMIT, } from './provider'; +import type { ExcludeResourcesOption } from './resource'; +import { isCoin } from './resource'; import type { CoinTransactionRequestInput } from './transaction-request'; import { ScriptTransactionRequest, CreateTransactionRequest } from './transaction-request'; import { TransactionResponse } from './transaction-response'; @@ -375,7 +377,7 @@ describe('Provider', () => { expect(producedBlocks).toEqual(expectedBlocks); }); - it('can cacheUtxo', async () => { + it('can set cache ttl', async () => { const ttl = 10000; using launched = await setupTestProviderAndWallets({ providerOptions: { @@ -387,14 +389,14 @@ describe('Provider', () => { expect(provider.cache?.ttl).toEqual(ttl); }); - it('should use utxos cache by default', async () => { + it('should use resource cache by default', async () => { using launched = await setupTestProviderAndWallets(); const { provider } = launched; - expect(provider.cache?.ttl).toEqual(DEFAULT_UTXOS_CACHE_TTL); + expect(provider.cache?.ttl).toEqual(DEFAULT_RESOURCE_CACHE_TTL); }); - it('should validate cacheUtxo value [invalid numerical]', async () => { + it('should validate resource cache value [invalid numerical]', async () => { const { error } = await safeExec(async () => { await setupTestProviderAndWallets({ providerOptions: { cacheUtxo: -500 } }); }); @@ -412,14 +414,20 @@ describe('Provider', () => { expect(provider.cache).toBeUndefined(); }); - it('should cache UTXOs only when TX is successfully submitted', async () => { + it('should cache resources only when TX is successfully submitted', async () => { + const resourceAmount = 50_000; + const utxosAmount = 2; + + const testMessage = new TestMessage({ amount: resourceAmount, data: '' }); + using launched = await setupTestProviderAndWallets({ nodeOptions: { args: ['--poa-instant', 'false', '--poa-interval-period', '1s'], }, walletsConfig: { - coinsPerAsset: 3, - amountPerCoin: 50_000, + coinsPerAsset: utxosAmount, + amountPerCoin: resourceAmount, + messages: [testMessage], }, }); const { @@ -429,15 +437,24 @@ describe('Provider', () => { const baseAssetId = provider.getBaseAssetId(); const { coins } = await wallet.getCoins(baseAssetId); - const EXPECTED: BytesLike[] = coins.map((coin) => coin.id); + + expect(coins.length).toBe(utxosAmount); + + const EXPECTED = { + utxos: coins.map((coin) => coin.id), + messages: [testMessage.nonce], + }; await wallet.transfer(receiver.address, 10_000); - const cachedCoins = provider.cache?.getActiveData() || []; - expect(new Set(cachedCoins)).toEqual(new Set(EXPECTED)); + const cachedResources = provider.cache?.getActiveData(); + expect(new Set(cachedResources?.utxos)).toEqual(new Set(EXPECTED.utxos)); + expect(new Set(cachedResources?.messages)).toEqual(new Set(EXPECTED.messages)); }); - it('should NOT cache UTXOs when TX submission fails', async () => { + it('should NOT cache resources when TX submission fails', async () => { + const message = new TestMessage({ amount: 100_000, data: '' }); + using launched = await setupTestProviderAndWallets({ nodeOptions: { args: ['--poa-instant', 'false', '--poa-interval-period', '1s'], @@ -445,6 +462,7 @@ describe('Provider', () => { walletsConfig: { coinsPerAsset: 2, amountPerCoin: 20_000, + messages: [message], }, }); const { @@ -457,7 +475,8 @@ describe('Provider', () => { const transferAmount = 10_000; const { coins } = await wallet.getCoins(baseAssetId); - const utxoIds = coins.map((c) => c.id); + const utxos = coins.map((c) => c.id); + const messages = [message.nonce]; // No enough funds to pay for the TX fee const resources = await wallet.getResourcesToSpend([[transferAmount, baseAssetId]]); @@ -474,20 +493,23 @@ describe('Provider', () => { { code: ErrorCode.INVALID_REQUEST } ); - // No UTXOs were cached since the TX submission failed - utxoIds.forEach((id) => { - expect(provider.cache?.get(id)).toBeUndefined(); + // No resources were cached since the TX submission failed + [...utxos, ...messages].forEach((key) => { + expect(provider.cache?.isCached(key)).toBeFalsy(); }); }); - it('should unset cached UTXOs when TX execution fails', async () => { + it('should unset cached resources when TX execution fails', async () => { + const message = new TestMessage({ amount: 100_000, data: '' }); + using launched = await setupTestProviderAndWallets({ nodeOptions: { args: ['--poa-instant', 'false', '--poa-interval-period', '1s'], }, walletsConfig: { - coinsPerAsset: 2, + coinsPerAsset: 1, amountPerCoin: 100_000, + messages: [message], }, }); const { @@ -500,7 +522,8 @@ describe('Provider', () => { const transferAmount = 10_000; const { coins } = await wallet.getCoins(baseAssetId); - const utxoIds = coins.map((c) => c.id); + const utxos = coins.map((c) => c.id); + const messages = [message.nonce]; // Should fetch resources enough to pay for the TX fee and transfer amount const resources = await wallet.getResourcesToSpend([[maxFee + transferAmount, baseAssetId]]); @@ -517,9 +540,9 @@ describe('Provider', () => { // TX submission will succeed const submitted = await wallet.sendTransaction(request, { estimateTxDependencies: false }); - // UTXOs were cached since the TX submission succeeded - utxoIds.forEach((id) => { - expect(provider.cache?.get(id)).toBeDefined(); + // Resources were cached since the TX submission succeeded + [...utxos, ...messages].forEach((key) => { + expect(provider.cache?.isCached(key)).toBeTruthy(); }); // TX execution will fail @@ -527,22 +550,23 @@ describe('Provider', () => { code: ErrorCode.SCRIPT_REVERTED, }); - // Ensure user's UTXOs were unset from the cache - utxoIds.forEach((id) => { - expect(provider.cache?.get(id)).toBeUndefined(); + // Ensure user's resouces were unset from the cache + [...utxos, ...messages].forEach((key) => { + expect(provider.cache?.isCached(key)).toBeFalsy(); }); }); - it('should ensure cached UTXOs are not being queried', async () => { - // Fund the wallet with 2 UTXOs - const totalUtxos = 2; + it('should ensure cached resources are not being queried', async () => { + // Fund the wallet with 2 resources + const testMessage = new TestMessage({ amount: 100_000_000_000, data: '' }); using launched = await setupTestProviderAndWallets({ nodeOptions: { args: ['--poa-instant', 'false', '--poa-interval-period', '1s'], }, walletsConfig: { - coinsPerAsset: totalUtxos, + coinsPerAsset: 1, amountPerCoin: 100_000_000_000, + messages: [testMessage], }, }); @@ -553,29 +577,44 @@ describe('Provider', () => { const baseAssetId = provider.getBaseAssetId(); const transferAmount = 10_000; - const { coins } = await wallet.getCoins(baseAssetId); - expect(coins.length).toBe(totalUtxos); + const { + coins: [coin], + } = await wallet.getCoins(baseAssetId); - // One of the UTXOs will be cached as the TX submission was successful - await wallet.transfer(receiver.address, transferAmount); + const { + messages: [message], + } = await wallet.getMessages(); - const cachedUtxos = provider.cache?.getActiveData(); + // One of the resources will be cached as the TX submission was successful + await wallet.transfer(receiver.address, transferAmount); - // Determine the used UTXO and the unused UTXO - const usedUtxo = coins.find((coin) => cachedUtxos?.includes(coin.id)); - const unusedUtxos = coins.filter((coin) => coin.id !== usedUtxo?.id); + // Determine the used and unused resource + const cachedResource = provider.cache?.isCached(coin.id) ? coin : message; + const uncachedResource = provider.cache?.isCached(coin.id) ? message : coin; - expect(usedUtxo).toBeDefined(); - expect(unusedUtxos).toBeDefined(); + expect(cachedResource).toBeDefined(); + expect(uncachedResource).toBeDefined(); - // Spy on the getCoinsToSpend method to ensure the cached UTXO is not being queried + // Spy on the getCoinsToSpend method to ensure the cached resource is not being queried const resourcesToSpendSpy = vi.spyOn(provider.operations, 'getCoinsToSpend'); - const resources = await wallet.getResourcesToSpend([[transferAmount, baseAssetId]]); - - // Ensure the returned UTXO is the unused UTXO - expect((resources[0]).id).toEqual(unusedUtxos[0].id); + const fetchedResources = await wallet.getResourcesToSpend([[transferAmount, baseAssetId]]); + + // Only one resource is available as the other one was cached + expect(fetchedResources.length).toBe(1); + + // Ensure the returned resource is the non-cached one + const excludedIds: Required = { messages: [], utxos: [] }; + if (isCoin(fetchedResources[0])) { + excludedIds.messages = expect.arrayContaining([(cachedResource).nonce]); + excludedIds.utxos = expect.arrayContaining([]); + expect(fetchedResources[0].id).toEqual((uncachedResource).id); + } else { + excludedIds.utxos = expect.arrayContaining([(cachedResource).id]); + excludedIds.messages = expect.arrayContaining([]); + expect(fetchedResources[0].nonce).toEqual((uncachedResource).nonce); + } - // Ensure the getCoinsToSpend query was called excluding the cached UTXO + // Ensure the getCoinsToSpend query was called excluding the cached resource expect(resourcesToSpendSpy).toHaveBeenCalledWith({ owner: wallet.address.toB256(), queryPerAsset: [ @@ -585,10 +624,7 @@ describe('Provider', () => { max: undefined, }, ], - excludedIds: { - messages: [], - utxos: expect.arrayContaining([usedUtxo?.id]), - }, + excludedIds, }); }); diff --git a/packages/account/src/providers/provider.ts b/packages/account/src/providers/provider.ts index d9d67b4202a..489b9081166 100644 --- a/packages/account/src/providers/provider.ts +++ b/packages/account/src/providers/provider.ts @@ -37,9 +37,9 @@ import type { Coin } from './coin'; import type { CoinQuantity, CoinQuantityLike } from './coin-quantity'; import { coinQuantityfy } from './coin-quantity'; import { FuelGraphqlSubscriber } from './fuel-graphql-subscriber'; -import { MemoryCache } from './memory-cache'; import type { Message, MessageCoin, MessageProof, MessageStatus } from './message'; import type { ExcludeResourcesOption, Resource } from './resource'; +import { ResourceCache } from './resource-cache'; import type { TransactionRequestLike, TransactionRequest, @@ -65,7 +65,7 @@ const MAX_RETRIES = 10; export const RESOURCES_PAGE_SIZE_LIMIT = 512; export const BLOCKS_PAGE_SIZE_LIMIT = 5; -export const DEFAULT_UTXOS_CACHE_TTL = 20_000; // 20 seconds +export const DEFAULT_RESOURCE_CACHE_TTL = 20_000; // 20 seconds export type DryRunFailureStatusFragment = GqlDryRunFailureStatusFragment; export type DryRunSuccessStatusFragment = GqlDryRunSuccessStatusFragment; @@ -373,7 +373,7 @@ type NodeInfoCache = Record; */ export default class Provider { operations: ReturnType; - cache?: MemoryCache; + cache?: ResourceCache; /** @hidden */ static clearChainAndNodeCaches() { @@ -432,12 +432,12 @@ export default class Provider { const { cacheUtxo } = this.options; if (isDefined(cacheUtxo)) { if (cacheUtxo !== -1) { - this.cache = new MemoryCache(cacheUtxo); + this.cache = new ResourceCache(cacheUtxo); } else { this.cache = undefined; } } else { - this.cache = new MemoryCache(DEFAULT_UTXOS_CACHE_TTL); + this.cache = new ResourceCache(DEFAULT_RESOURCE_CACHE_TTL); } } @@ -688,11 +688,19 @@ Supported fuel-core version: ${supportedVersion}.` return; } - inputs.forEach((input) => { - if (input.type === InputType.Coin) { - this.cache?.set(input.id, transactionId); - } - }); + const ExcludeResources = inputs.reduce( + (acc, input) => { + if (input.type === InputType.Coin) { + acc.utxos.push(input.id); + } else if (input.type === InputType.Message) { + acc.messages.push(input.nonce); + } + return acc; + }, + { utxos: [], messages: [] } as Required + ); + + this.cache.set(transactionId, ExcludeResources); } /** @@ -1242,11 +1250,11 @@ Supported fuel-core version: ${supportedVersion}.` }; if (this.cache) { - const uniqueUtxos = new Set( - excludeInput.utxos.concat(this.cache?.getActiveData().map((id) => hexlify(id))) - ); - excludeInput.utxos = Array.from(uniqueUtxos); + const cached = this.cache.getActiveData(); + excludeInput.messages.push(...cached.messages); + excludeInput.utxos.push(...cached.utxos); } + const coinsQuery = { owner: ownerAddress.toB256(), queryPerAsset: quantities diff --git a/packages/account/src/providers/transaction-response/transaction-response.ts b/packages/account/src/providers/transaction-response/transaction-response.ts index 4199c5b72a3..11840da632d 100644 --- a/packages/account/src/providers/transaction-response/transaction-response.ts +++ b/packages/account/src/providers/transaction-response/transaction-response.ts @@ -315,6 +315,6 @@ export class TransactionResponse { } private unsetResourceCache() { - this.provider.cache?.delByTxID(this.id); + this.provider.cache?.unset(this.id); } } diff --git a/packages/fuel-gauge/src/funding-transaction.test.ts b/packages/fuel-gauge/src/funding-transaction.test.ts index 9b9be3bde58..390ce711ecb 100644 --- a/packages/fuel-gauge/src/funding-transaction.test.ts +++ b/packages/fuel-gauge/src/funding-transaction.test.ts @@ -1,7 +1,7 @@ import { FuelError } from '@fuel-ts/errors'; import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; import type { Account, CoinTransactionRequestInput } from 'fuels'; -import { DEFAULT_UTXOS_CACHE_TTL, ScriptTransactionRequest, Wallet, bn, sleep } from 'fuels'; +import { DEFAULT_RESOURCE_CACHE_TTL, ScriptTransactionRequest, Wallet, bn, sleep } from 'fuels'; import { launchTestNode } from 'fuels/test-utils'; /** @@ -457,7 +457,7 @@ describe('Funding Transactions', () => { expect(result1.blockId).toBe(result2.blockId); expect(provider.cache).toBeTruthy(); - expect(provider.cache?.ttl).toBe(DEFAULT_UTXOS_CACHE_TTL); + expect(provider.cache?.ttl).toBe(DEFAULT_RESOURCE_CACHE_TTL); }, 15_000); it('should fail when trying to use the same UTXO in multiple TXs without cache', async () => { From 93ccb88740848ddf64414d974d7aa436a95d5eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Sun, 4 Aug 2024 23:10:10 -0300 Subject: [PATCH 13/25] remove old MemoryCache --- .../src/providers/memory-cache.test.ts | 209 ------------------ .../account/src/providers/memory-cache.ts | 88 -------- 2 files changed, 297 deletions(-) delete mode 100644 packages/account/src/providers/memory-cache.test.ts delete mode 100644 packages/account/src/providers/memory-cache.ts diff --git a/packages/account/src/providers/memory-cache.test.ts b/packages/account/src/providers/memory-cache.test.ts deleted file mode 100644 index 5846b00ed62..00000000000 --- a/packages/account/src/providers/memory-cache.test.ts +++ /dev/null @@ -1,209 +0,0 @@ -import { getRandomB256 } from '@fuel-ts/address'; -import { randomBytes } from '@fuel-ts/crypto'; -import type { BytesLike } from '@fuel-ts/interfaces'; -import { hexlify } from '@fuel-ts/utils'; - -import { MemoryCache } from './memory-cache'; - -/** - * @group node - * @group browser - */ -describe('Memory Cache', () => { - const txID = getRandomB256(); - - it('can construct [valid numerical ttl]', () => { - const memCache = new MemoryCache(1000); - - expect(memCache.ttl).toEqual(1000); - }); - - it('can construct [invalid numerical ttl]', () => { - expect(() => new MemoryCache(-1)).toThrow(/Invalid TTL: -1. Use a value greater than zero./); - }); - - it('can construct [invalid mistyped ttl]', () => { - // @ts-expect-error intentional invalid input - expect(() => new MemoryCache('bogus')).toThrow( - /Invalid TTL: bogus. Use a value greater than zero./ - ); - }); - - it('can construct [missing ttl]', () => { - const memCache = new MemoryCache(); - - expect(memCache.ttl).toEqual(30_000); - }); - - it('can get [unknown key]', () => { - const memCache = new MemoryCache(1000); - - expect( - memCache.get('0xda5d131c490db33333333333333333334444444444444444444455555555556666') - ).toEqual(undefined); - }); - - it('can get active [no data]', () => { - const EXPECTED: BytesLike[] = []; - const memCache = new MemoryCache(100); - - expect(memCache.getActiveData()).toStrictEqual(EXPECTED); - }); - - it('can set', () => { - const ttl = 1000; - const expiresAt = Date.now() + ttl; - const memCache = new MemoryCache(ttl); - const value = randomBytes(8); - - expect(memCache.set(value, txID)).toBeGreaterThanOrEqual(expiresAt); - }); - - it('can get [valid key]', () => { - const value = randomBytes(8); - const memCache = new MemoryCache(100); - - memCache.set(value, txID); - - expect(memCache.get(value)).toEqual(value); - }); - - it('can get [valid key bytes like]', () => { - const value = randomBytes(8); - const memCache = new MemoryCache(100); - - memCache.set(value, txID); - - expect(memCache.get(value)).toEqual(value); - }); - - it('can get [valid key, expired content]', async () => { - const value = randomBytes(8); - const memCache = new MemoryCache(1); - - memCache.set(value, txID); - - await new Promise((resolve) => { - setTimeout(resolve, 10); - }); - - expect(memCache.get(value)).toEqual(undefined); - }); - - it('can get, disabling auto deletion [valid key, expired content]', async () => { - const value = randomBytes(8); - const memCache = new MemoryCache(1); - - memCache.set(value, txID); - - await new Promise((resolve) => { - setTimeout(resolve, 10); - }); - - expect(memCache.get(value, false)).toEqual(value); - }); - - it('can delete', () => { - const value = randomBytes(8); - const memCache = new MemoryCache(100); - - memCache.set(value, txID); - memCache.del(value); - - expect(memCache.get(value)).toEqual(undefined); - }); - - it('can get active [with data]', () => { - const value1 = randomBytes(8); - const value2 = randomBytes(8); - const value3 = hexlify(randomBytes(8)); - const EXPECTED: BytesLike[] = [value1, value2, value3]; - - const memCache = new MemoryCache(100); - - memCache.set(value1, txID); - memCache.set(value2, txID); - memCache.set(value3, txID); - - expect(memCache.getActiveData()).containSubset(EXPECTED); - }); - - it('can get all [with data + expired data]', async () => { - const oldValue = randomBytes(8); - const value1 = randomBytes(8); - const value2 = randomBytes(8); - const EXPECTED: BytesLike[] = [value1, value2, oldValue]; - - let memCache = new MemoryCache(500); - memCache.set(value1, txID); - memCache.set(value2, txID); - - memCache = new MemoryCache(1); - memCache.set(oldValue, txID); - - await new Promise((resolve) => { - setTimeout(resolve, 10); - }); - - /* - MemoryCache uses a global cache with values from - several instances, all returned by `getActiveData()`. - However, we only want to check the ones from this - test, so we use `containSubset`. - */ - expect(memCache.getAllData()).containSubset(EXPECTED); - }); - - it('should delete cached values by its txID', () => { - const value1 = randomBytes(8); - const value2 = randomBytes(8); - const value3 = randomBytes(8); - - const ttl = 1000; - - const txId1 = 'tx-id-1'; - const txId2 = 'tx-id-2'; - - const memCache1 = new MemoryCache(ttl); - const memCache2 = new MemoryCache(ttl); - - memCache1.set(value1, txId1); - memCache1.set(value2, txId2); - - memCache2.set(value3, txId1); - - expect(memCache1.getActiveData()).toContain(value1); - expect(memCache1.getActiveData()).toContain(value2); - - expect(memCache2.getActiveData()).toContain(value3); - - memCache1.delByTxID(txId1); - - // Value 2 is from TX ID 2, so it should not be deleted - expect(memCache1.getActiveData()).toContain(value2); - - expect(memCache1.getActiveData()).not.toContain(value1); - expect(memCache2.getActiveData()).not.toContain(value3); - }); - - it('should validate that MemoryCache uses a global cache', async () => { - const oldValue = randomBytes(8); - - const instance1 = new MemoryCache(1000); - instance1.set(oldValue, txID); - - await new Promise((resolve) => { - setTimeout(resolve, 200); - }); - - const newValue = randomBytes(8); - - const instance2 = new MemoryCache(100); - instance2.set(newValue, txID); - - const activeData = instance2.getActiveData(); - - expect(activeData).toContain(oldValue); - expect(activeData).toContain(newValue); - }); -}); diff --git a/packages/account/src/providers/memory-cache.ts b/packages/account/src/providers/memory-cache.ts deleted file mode 100644 index bb3454d26c7..00000000000 --- a/packages/account/src/providers/memory-cache.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { ErrorCode, FuelError } from '@fuel-ts/errors'; -import type { BytesLike } from '@fuel-ts/interfaces'; -import { hexlify } from '@fuel-ts/utils'; - -type Cache = { - [key: string]: { - expires: number; - value: BytesLike; - txID: string; - }; -}; -const cache: Cache = {}; // it's a cache hash ~~> cash? - -const DEFAULT_TTL_IN_MS = 30 * 1000; // 30seconds - -export class MemoryCache { - ttl: number; - constructor(ttlInMs: number = DEFAULT_TTL_IN_MS) { - this.ttl = ttlInMs; - - if (typeof ttlInMs !== 'number' || this.ttl <= 0) { - throw new FuelError( - ErrorCode.INVALID_TTL, - `Invalid TTL: ${this.ttl}. Use a value greater than zero.` - ); - } - } - - get(value: BytesLike, isAutoExpiring = true): BytesLike | undefined { - const key = hexlify(value); - if (cache[key]) { - if (!isAutoExpiring || cache[key].expires > Date.now()) { - return cache[key].value; - } - - this.del(value); - } - - return undefined; - } - - set(value: BytesLike, txID: string): number { - const expiresAt = Date.now() + this.ttl; - const key = hexlify(value); - cache[key] = { - expires: expiresAt, - value, - txID, - }; - - return expiresAt; - } - - getAllData(): BytesLike[] { - return Object.keys(cache).reduce((list, key) => { - const data = this.get(key, false); - if (data) { - list.push(data); - } - - return list; - }, [] as BytesLike[]); - } - - getActiveData(): BytesLike[] { - return Object.keys(cache).reduce((list, key) => { - const data = this.get(key); - if (data) { - list.push(data); - } - - return list; - }, [] as BytesLike[]); - } - - del(value: BytesLike) { - const key = hexlify(value); - delete cache[key]; - } - - delByTxID(txID: string) { - Object.keys(cache).forEach((key) => { - if (cache[key].txID === txID) { - delete cache[key]; - } - }); - } -} From a1c9ac16cf8ba811c576b73491869fb51e43bf93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Sun, 4 Aug 2024 23:13:09 -0300 Subject: [PATCH 14/25] add new changeset --- .changeset/cuddly-lobsters-warn.md | 5 +++++ .changeset/lazy-plants-fry.md | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 .changeset/cuddly-lobsters-warn.md delete mode 100644 .changeset/lazy-plants-fry.md diff --git a/.changeset/cuddly-lobsters-warn.md b/.changeset/cuddly-lobsters-warn.md new file mode 100644 index 00000000000..55d863e6efa --- /dev/null +++ b/.changeset/cuddly-lobsters-warn.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/account": minor +--- + +feat!: consider message on resources cache diff --git a/.changeset/lazy-plants-fry.md b/.changeset/lazy-plants-fry.md deleted file mode 100644 index a7116b45782..00000000000 --- a/.changeset/lazy-plants-fry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fuel-ts/account": patch ---- - -fix: unset cached resource for failed TX From 2751d92975f08923d7c8ff0f806f5fb0a4f2294c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Sun, 4 Aug 2024 23:35:34 -0300 Subject: [PATCH 15/25] rename cacheUtxo flag to resourceCacheTTL --- .../src/guide/provider/provider.test.ts | 4 ++-- apps/docs/src/guide/provider/provider-options.md | 6 +++--- packages/account/src/providers/provider.test.ts | 6 +++--- packages/account/src/providers/provider.ts | 14 +++++++------- .../src/test-utils/launchNodeAndGetWallets.test.ts | 8 ++++---- .../setup-test-provider-and-wallets.test.ts | 2 +- .../fuel-gauge/src/funding-transaction.test.ts | 2 +- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/apps/docs-snippets/src/guide/provider/provider.test.ts b/apps/docs-snippets/src/guide/provider/provider.test.ts index 0750663e3ae..2d70c000b75 100644 --- a/apps/docs-snippets/src/guide/provider/provider.test.ts +++ b/apps/docs-snippets/src/guide/provider/provider.test.ts @@ -107,10 +107,10 @@ describe('Provider', () => { // #endregion options-fetch }); - it('options: cacheUtxo', async () => { + it('options: resourceCacheTTL', async () => { // #region options-cache-utxo const provider = await Provider.create(FUEL_NETWORK_URL, { - cacheUtxo: 5000, // cache UTXO for 5 seconds + resourceCacheTTL: 5000, // cache UTXO for 5 seconds }); // #endregion options-cache-utxo diff --git a/apps/docs/src/guide/provider/provider-options.md b/apps/docs/src/guide/provider/provider-options.md index f16818f9a30..6efbb25493e 100644 --- a/apps/docs/src/guide/provider/provider-options.md +++ b/apps/docs/src/guide/provider/provider-options.md @@ -40,7 +40,7 @@ _Note: If defined, `requestMiddleware`, `timeout` and `retryOptions` are applied <<< @/../../docs-snippets/src/guide/provider/provider.test.ts#options-fetch{ts:line-numbers} -### `cacheUtxo` +### `resourceCacheTTL` When using the SDK, it may be necessary to submit multiple transactions from the same account in a short period. In such cases, the SDK creates and funds these transactions, then submits them to the node. @@ -54,7 +54,7 @@ Transaction is not inserted. UTXO does not exist: 0xf5... This error indicates that the UTXO(s) used by the second transaction no longer exist, as the first transaction already spent them. -To prevent this issue, the SDK sets a default cache for UTXO(s) to 20 seconds. This default caching mechanism ensures that UTXO(s) used in a submitted transaction are not reused in subsequent transactions within the specified time. You can control the duration of this cache using the `cacheUtxo` flag. If you would like to disable caching, you can pass a value of `-1` to the `cacheUtxo` parameter. +To prevent this issue, the SDK sets a default cache for UTXO(s) to 20 seconds. This default caching mechanism ensures that UTXO(s) used in a submitted transaction are not reused in subsequent transactions within the specified time. You can control the duration of this cache using the `resourceCacheTTL` flag. If you would like to disable caching, you can pass a value of `-1` to the `resourceCacheTTL` parameter. <<< @/../../docs-snippets/src/guide/provider/provider.test.ts#options-cache-utxo{ts:line-numbers} @@ -62,4 +62,4 @@ To prevent this issue, the SDK sets a default cache for UTXO(s) to 20 seconds. T If you would like to submit multiple transactions without waiting for each transaction to be completed, your account must have multiple UTXOs available. If you only have one UTXO, the first transaction will spend it, and any remaining amount will be converted into a new UTXO with a different ID. -By ensuring your account has multiple UTXOs, you can effectively use the `cacheUtxo` flag to manage transactions without conflicts. For more information on UTXOs, refer to the [UTXOs guide](../the-utxo-model/index.md). +By ensuring your account has multiple UTXOs, you can effectively use the `resourceCacheTTL` flag to manage transactions without conflicts. For more information on UTXOs, refer to the [UTXOs guide](../the-utxo-model/index.md). diff --git a/packages/account/src/providers/provider.test.ts b/packages/account/src/providers/provider.test.ts index 2c3ee96a317..02f61b35221 100644 --- a/packages/account/src/providers/provider.test.ts +++ b/packages/account/src/providers/provider.test.ts @@ -381,7 +381,7 @@ describe('Provider', () => { const ttl = 10000; using launched = await setupTestProviderAndWallets({ providerOptions: { - cacheUtxo: ttl, + resourceCacheTTL: ttl, }, }); const { provider } = launched; @@ -398,7 +398,7 @@ describe('Provider', () => { it('should validate resource cache value [invalid numerical]', async () => { const { error } = await safeExec(async () => { - await setupTestProviderAndWallets({ providerOptions: { cacheUtxo: -500 } }); + await setupTestProviderAndWallets({ providerOptions: { resourceCacheTTL: -500 } }); }); expect(error?.message).toMatch(/Invalid TTL: -500\. Use a value greater than zero/); }); @@ -406,7 +406,7 @@ describe('Provider', () => { it('should be possible to disable the cache by using -1', async () => { using launched = await setupTestProviderAndWallets({ providerOptions: { - cacheUtxo: -1, + resourceCacheTTL: -1, }, }); const { provider } = launched; diff --git a/packages/account/src/providers/provider.ts b/packages/account/src/providers/provider.ts index 489b9081166..d31e3d88d56 100644 --- a/packages/account/src/providers/provider.ts +++ b/packages/account/src/providers/provider.ts @@ -302,9 +302,9 @@ export type ProviderOptions = { */ timeout?: number; /** - * Cache UTXOs for the given time [ms]. + * Resources cache for the given time [ms]. If set to -1, the cache will be disabled. */ - cacheUtxo?: number; + resourceCacheTTL?: number; /** * Retry options to use when fetching data from the node. */ @@ -388,7 +388,7 @@ export default class Provider { options: ProviderOptions = { timeout: undefined, - cacheUtxo: undefined, + resourceCacheTTL: undefined, fetch: undefined, retryOptions: undefined, }; @@ -429,10 +429,10 @@ export default class Provider { this.url = url; this.operations = this.createOperations(); - const { cacheUtxo } = this.options; - if (isDefined(cacheUtxo)) { - if (cacheUtxo !== -1) { - this.cache = new ResourceCache(cacheUtxo); + const { resourceCacheTTL } = this.options; + if (isDefined(resourceCacheTTL)) { + if (resourceCacheTTL !== -1) { + this.cache = new ResourceCache(resourceCacheTTL); } else { this.cache = undefined; } diff --git a/packages/account/src/test-utils/launchNodeAndGetWallets.test.ts b/packages/account/src/test-utils/launchNodeAndGetWallets.test.ts index c53b4ebeccc..6bf20942b8f 100644 --- a/packages/account/src/test-utils/launchNodeAndGetWallets.test.ts +++ b/packages/account/src/test-utils/launchNodeAndGetWallets.test.ts @@ -13,7 +13,7 @@ describe('launchNode', () => { test('launchNodeAndGetWallets - empty config', async () => { const { stop, provider, wallets } = await launchNodeAndGetWallets({ providerOptions: { - cacheUtxo: 1, + resourceCacheTTL: 1, }, launchNodeOptions: { loggingEnabled: false, @@ -32,7 +32,7 @@ describe('launchNode', () => { const { stop, provider } = await launchNodeAndGetWallets({ providerOptions: { - cacheUtxo: 1, + resourceCacheTTL: 1, }, launchNodeOptions: { args: ['--snapshot', snapshotDir], @@ -57,7 +57,7 @@ describe('launchNode', () => { const { stop, wallets } = await launchNodeAndGetWallets({ walletCount: 5, providerOptions: { - cacheUtxo: 1, + resourceCacheTTL: 1, }, launchNodeOptions: { loggingEnabled: false, @@ -85,7 +85,7 @@ describe('launchNode', () => { test('launchNodeAndGetWallets - empty config', async () => { const { stop, provider, wallets } = await launchNodeAndGetWallets({ providerOptions: { - cacheUtxo: 1, + resourceCacheTTL: 1, }, launchNodeOptions: { loggingEnabled: false, diff --git a/packages/account/src/test-utils/setup-test-provider-and-wallets.test.ts b/packages/account/src/test-utils/setup-test-provider-and-wallets.test.ts index 59345b2227c..d5bf929d4ae 100644 --- a/packages/account/src/test-utils/setup-test-provider-and-wallets.test.ts +++ b/packages/account/src/test-utils/setup-test-provider-and-wallets.test.ts @@ -46,7 +46,7 @@ describe('setupTestProviderAndWallets', () => { await expectToThrowFuelError( async () => { - await setupTestProviderAndWallets({ providerOptions: { cacheUtxo: -500 } }); + await setupTestProviderAndWallets({ providerOptions: { resourceCacheTTL: -500 } }); }, { code: ErrorCode.INVALID_TTL } ); diff --git a/packages/fuel-gauge/src/funding-transaction.test.ts b/packages/fuel-gauge/src/funding-transaction.test.ts index 390ce711ecb..1b48dce51a4 100644 --- a/packages/fuel-gauge/src/funding-transaction.test.ts +++ b/packages/fuel-gauge/src/funding-transaction.test.ts @@ -468,7 +468,7 @@ describe('Funding Transactions', () => { }, providerOptions: { // Cache will last for 1 millisecond - cacheUtxo: 1, + resourceCacheTTL: 1, }, walletsConfig: { coinsPerAsset: 1, From 8aca9c2697b96ba7f8249abee0580979ef72a287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Mon, 5 Aug 2024 09:21:25 -0300 Subject: [PATCH 16/25] fix comment --- apps/docs-snippets/src/guide/provider/provider.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs-snippets/src/guide/provider/provider.test.ts b/apps/docs-snippets/src/guide/provider/provider.test.ts index 2d70c000b75..df5631a9755 100644 --- a/apps/docs-snippets/src/guide/provider/provider.test.ts +++ b/apps/docs-snippets/src/guide/provider/provider.test.ts @@ -110,7 +110,7 @@ describe('Provider', () => { it('options: resourceCacheTTL', async () => { // #region options-cache-utxo const provider = await Provider.create(FUEL_NETWORK_URL, { - resourceCacheTTL: 5000, // cache UTXO for 5 seconds + resourceCacheTTL: 5000, // cache resources (Coin's and Message's) for 5 seconds }); // #endregion options-cache-utxo From 09526f0502ba393f5adf552af4088d0348721a8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Mon, 5 Aug 2024 09:24:28 -0300 Subject: [PATCH 17/25] add method clear to resource cache --- .../src/providers/resource-cache.test.ts | 32 +++++++++++++++++++ .../account/src/providers/resource-cache.ts | 4 +++ 2 files changed, 36 insertions(+) diff --git a/packages/account/src/providers/resource-cache.test.ts b/packages/account/src/providers/resource-cache.test.ts index 7742a852a20..5ee63be5054 100644 --- a/packages/account/src/providers/resource-cache.test.ts +++ b/packages/account/src/providers/resource-cache.test.ts @@ -139,6 +139,38 @@ describe('Resource Cache', () => { expect(activeData.messages).containSubset(txId2Resources.messages); }); + it('can clear cache', () => { + const resourceCache = new ResourceCache(1000); + + const txId1 = randomValue(); + const txId2 = randomValue(); + + const txId1Resources = { + utxos: [randomValue()], + messages: [randomValue(), randomValue()], + }; + + const txId2Resources = { + utxos: [randomValue(), randomValue()], + messages: [randomValue()], + }; + + resourceCache.set(txId1, txId1Resources); + resourceCache.set(txId2, txId2Resources); + + const activeData = resourceCache.getActiveData(); + + expect(activeData.utxos).containSubset([...txId1Resources.utxos, ...txId2Resources.utxos]); + expect(activeData.messages).containSubset([ + ...txId1Resources.messages, + ...txId2Resources.messages, + ]); + + resourceCache.clear(); + + expect(resourceCache.getActiveData()).toStrictEqual({ utxos: [], messages: [] }); + }); + it('should validate that ResourceCache uses a global cache', () => { const oldTxId = randomValue(); const oldCache = { diff --git a/packages/account/src/providers/resource-cache.ts b/packages/account/src/providers/resource-cache.ts index 3dd77e78e25..b4fd5f95add 100644 --- a/packages/account/src/providers/resource-cache.ts +++ b/packages/account/src/providers/resource-cache.ts @@ -72,4 +72,8 @@ export class ResourceCache { } return false; } + + clear() { + cache.clear(); + } } From 1a8ab1a10f379f06a349108b131ed2e7a33bcf96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Mon, 5 Aug 2024 09:29:45 -0300 Subject: [PATCH 18/25] improve docs --- apps/docs/src/guide/provider/provider-options.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/docs/src/guide/provider/provider-options.md b/apps/docs/src/guide/provider/provider-options.md index 6efbb25493e..b49dc34a2ae 100644 --- a/apps/docs/src/guide/provider/provider-options.md +++ b/apps/docs/src/guide/provider/provider-options.md @@ -44,17 +44,17 @@ _Note: If defined, `requestMiddleware`, `timeout` and `retryOptions` are applied When using the SDK, it may be necessary to submit multiple transactions from the same account in a short period. In such cases, the SDK creates and funds these transactions, then submits them to the node. -However, if a second transaction is created before the first one is processed, there is a chance of using the same UTXO(s) for both transactions. This happens because the UTXO(s) used in the first transaction are still unspent until the transaction is fully processed. +However, if a second transaction is created before the first one is processed, there is a chance of using the same resources (UTXOs or Messages) for both transactions. This happens because the resources used in the first transaction are still unspent until the transaction is fully processed. -If the second transaction attempts to use the same UTXO(s) that the first transaction has already spent, it will result in the following error: +If the second transaction attempts to use the same resources that the first transaction has already spent, it will result in the following error: ```console Transaction is not inserted. UTXO does not exist: 0xf5... ``` -This error indicates that the UTXO(s) used by the second transaction no longer exist, as the first transaction already spent them. +This error indicates that the resources used by the second transaction no longer exist, as the first transaction already spent them. -To prevent this issue, the SDK sets a default cache for UTXO(s) to 20 seconds. This default caching mechanism ensures that UTXO(s) used in a submitted transaction are not reused in subsequent transactions within the specified time. You can control the duration of this cache using the `resourceCacheTTL` flag. If you would like to disable caching, you can pass a value of `-1` to the `resourceCacheTTL` parameter. +To prevent this issue, the SDK sets a default cache for resources to 20 seconds. This default caching mechanism ensures that resources used in a submitted transaction are not reused in subsequent transactions within the specified time. You can control the duration of this cache using the `resourceCacheTTL` flag. If you would like to disable caching, you can pass a value of `-1` to the `resourceCacheTTL` parameter. <<< @/../../docs-snippets/src/guide/provider/provider.test.ts#options-cache-utxo{ts:line-numbers} From cc818e691425735d89f77a0da0e070de0552327a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Mon, 5 Aug 2024 09:52:49 -0300 Subject: [PATCH 19/25] edit provider options docs --- apps/docs/src/guide/provider/provider-options.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/docs/src/guide/provider/provider-options.md b/apps/docs/src/guide/provider/provider-options.md index b49dc34a2ae..2623095aca2 100644 --- a/apps/docs/src/guide/provider/provider-options.md +++ b/apps/docs/src/guide/provider/provider-options.md @@ -46,10 +46,14 @@ When using the SDK, it may be necessary to submit multiple transactions from the However, if a second transaction is created before the first one is processed, there is a chance of using the same resources (UTXOs or Messages) for both transactions. This happens because the resources used in the first transaction are still unspent until the transaction is fully processed. -If the second transaction attempts to use the same resources that the first transaction has already spent, it will result in the following error: +If the second transaction attempts to use the same resources that the first transaction has already spent, it will result in one of the following error: ```console -Transaction is not inserted. UTXO does not exist: 0xf5... +Transaction is not inserted. Hash is already known + +Transaction is not inserted. UTXO does not exist: {{utxoID}} + +Transaction is not inserted. A higher priced tx {{txID}} is already spending this message: {{messageNonce}} ``` This error indicates that the resources used by the second transaction no longer exist, as the first transaction already spent them. From 7e89b4a2ad38446b70cf15291f9b6dcf8e26f8cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Mon, 5 Aug 2024 09:54:15 -0300 Subject: [PATCH 20/25] fix constant name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nedim Salkić --- packages/account/src/providers/provider.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/account/src/providers/provider.ts b/packages/account/src/providers/provider.ts index d31e3d88d56..0c66688d1e5 100644 --- a/packages/account/src/providers/provider.ts +++ b/packages/account/src/providers/provider.ts @@ -688,7 +688,7 @@ Supported fuel-core version: ${supportedVersion}.` return; } - const ExcludeResources = inputs.reduce( + const inputsToCache = inputs.reduce( (acc, input) => { if (input.type === InputType.Coin) { acc.utxos.push(input.id); @@ -700,7 +700,7 @@ Supported fuel-core version: ${supportedVersion}.` { utxos: [], messages: [] } as Required ); - this.cache.set(transactionId, ExcludeResources); + this.cache.set(transactionId, inputsToCache); } /** From 6ab9f2b3cdb8105a5983259d7b273afa126b483d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 7 Aug 2024 08:03:44 -0300 Subject: [PATCH 21/25] Update packages/account/src/providers/provider.test.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nedim Salkić --- packages/account/src/providers/provider.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account/src/providers/provider.test.ts b/packages/account/src/providers/provider.test.ts index 02f61b35221..9c2e78210d8 100644 --- a/packages/account/src/providers/provider.test.ts +++ b/packages/account/src/providers/provider.test.ts @@ -500,7 +500,7 @@ describe('Provider', () => { }); it('should unset cached resources when TX execution fails', async () => { - const message = new TestMessage({ amount: 100_000, data: '' }); + const message = new TestMessage({ amount: 100_000 }); using launched = await setupTestProviderAndWallets({ nodeOptions: { From 99e8e3c96a77c388e025a15a824a384f24029e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 7 Aug 2024 08:03:52 -0300 Subject: [PATCH 22/25] Update packages/account/src/providers/provider.test.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nedim Salkić --- packages/account/src/providers/provider.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account/src/providers/provider.test.ts b/packages/account/src/providers/provider.test.ts index 9c2e78210d8..8ded649b28f 100644 --- a/packages/account/src/providers/provider.test.ts +++ b/packages/account/src/providers/provider.test.ts @@ -418,7 +418,7 @@ describe('Provider', () => { const resourceAmount = 50_000; const utxosAmount = 2; - const testMessage = new TestMessage({ amount: resourceAmount, data: '' }); + const testMessage = new TestMessage({ amount: resourceAmount }); using launched = await setupTestProviderAndWallets({ nodeOptions: { From e05c96c3675c55825ce29a162452d3acc5d6d651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 7 Aug 2024 08:04:12 -0300 Subject: [PATCH 23/25] Update packages/account/src/providers/provider.test.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nedim Salkić --- packages/account/src/providers/provider.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account/src/providers/provider.test.ts b/packages/account/src/providers/provider.test.ts index 8ded649b28f..f69bfc1b17b 100644 --- a/packages/account/src/providers/provider.test.ts +++ b/packages/account/src/providers/provider.test.ts @@ -558,7 +558,7 @@ describe('Provider', () => { it('should ensure cached resources are not being queried', async () => { // Fund the wallet with 2 resources - const testMessage = new TestMessage({ amount: 100_000_000_000, data: '' }); + const testMessage = new TestMessage({ amount: 100_000_000_000 }); using launched = await setupTestProviderAndWallets({ nodeOptions: { args: ['--poa-instant', 'false', '--poa-interval-period', '1s'], From db75955e6239627fe7294ad07d19286014bcd58c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Thu, 8 Aug 2024 08:37:44 -0300 Subject: [PATCH 24/25] Update packages/account/src/providers/provider.test.ts Co-authored-by: Chad Nehemiah --- packages/account/src/providers/provider.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account/src/providers/provider.test.ts b/packages/account/src/providers/provider.test.ts index 00c0d40e006..41ee406edb6 100644 --- a/packages/account/src/providers/provider.test.ts +++ b/packages/account/src/providers/provider.test.ts @@ -454,7 +454,7 @@ describe('Provider', () => { }); it('should NOT cache resources when TX submission fails', async () => { - const message = new TestMessage({ amount: 100_000, data: '' }); + const message = new TestMessage({ amount: 100_000}); using launched = await setupTestProviderAndWallets({ nodeOptions: { From 318fa60c762e080a2c3ddbe425969279df5ccbfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Thu, 8 Aug 2024 08:49:07 -0300 Subject: [PATCH 25/25] lintfix --- packages/account/src/providers/provider.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account/src/providers/provider.test.ts b/packages/account/src/providers/provider.test.ts index 41ee406edb6..9f7198a9736 100644 --- a/packages/account/src/providers/provider.test.ts +++ b/packages/account/src/providers/provider.test.ts @@ -454,7 +454,7 @@ describe('Provider', () => { }); it('should NOT cache resources when TX submission fails', async () => { - const message = new TestMessage({ amount: 100_000}); + const message = new TestMessage({ amount: 100_000 }); using launched = await setupTestProviderAndWallets({ nodeOptions: {