Skip to content

Commit

Permalink
Merge pull request #1569 from input-output-hk/feat/cache-data-from-bl…
Browse files Browse the repository at this point in the history
…ockfrost-locally

Feat/cache data from blockfrost locally
  • Loading branch information
AngelCastilloB authored Jan 27, 2025
2 parents 956690e + 8cb0dac commit fbc7429
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type BlockfrostTx = Pick<Responses['address_transactions_content'][0], 'block_he
const compareTx = (a: BlockfrostTx, b: BlockfrostTx) => a.block_height - b.block_height || a.tx_index - b.tx_index;

export class BlockfrostChainHistoryProvider extends BlockfrostProvider implements ChainHistoryProvider {
private readonly cache: Map<string, Cardano.HydratedTx> = new Map();
private networkInfoProvider: NetworkInfoProvider;

constructor(client: BlockfrostClient, networkInfoProvider: NetworkInfoProvider, logger: Logger) {
Expand Down Expand Up @@ -475,11 +476,20 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
}

public async transactionsByHashes({ ids }: TransactionsByIdsArgs): Promise<Cardano.HydratedTx[]> {
try {
return Promise.all(ids.map((id) => this.fetchTransaction(id)));
} catch (error) {
throw this.toProviderError(error);
}
return Promise.all(
ids.map(async (id) => {
if (this.cache.has(id)) {
return this.cache.get(id)!;
}
try {
const fetchedTransaction = await this.fetchTransaction(id);
this.cache.set(id, fetchedTransaction);
return fetchedTransaction;
} catch (error) {
throw this.toProviderError(error);
}
})
);
}

// eslint-disable-next-line sonarjs/cognitive-complexity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Cardano, Serialization, UtxoByAddressesArgs, UtxoProvider } from '@card
import type { Responses } from '@blockfrost/blockfrost-js';

export class BlockfrostUtxoProvider extends BlockfrostProvider implements UtxoProvider {
private readonly cache: Map<string, Cardano.Tx> = new Map();

protected async fetchUtxos(addr: Cardano.PaymentAddress, paginationQueryString: string): Promise<Cardano.Utxo[]> {
const queryString = `addresses/${addr.toString()}/utxos?${paginationQueryString}`;
const utxos = await this.request<Responses['address_utxo_content']>(queryString);
Expand All @@ -27,7 +29,11 @@ export class BlockfrostUtxoProvider extends BlockfrostProvider implements UtxoPr
});
}
protected async fetchDetailsFromCBOR(hash: string) {
return this.fetchCBOR(hash)
if (this.cache.has(hash)) {
return this.cache.get(hash);
}

const result = await this.fetchCBOR(hash)
.then((cbor) => {
const tx = Serialization.Transaction.fromCbor(Serialization.TxCBOR(cbor)).toCore();
this.logger.info('Fetched details from CBOR for tx', hash);
Expand All @@ -37,7 +43,15 @@ export class BlockfrostUtxoProvider extends BlockfrostProvider implements UtxoPr
this.logger.warn('Failed to fetch details from CBOR for tx', hash, error);
return null;
});

if (!result) {
return null;
}

this.cache.set(hash, result);
return result;
}

public async utxoByAddresses({ addresses }: UtxoByAddressesArgs): Promise<Cardano.Utxo[]> {
try {
const utxoResults = await Promise.all(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -590,5 +590,25 @@ describe('blockfrostChainHistoryProvider', () => {
expect(response[0]).toEqual(expectedHydratedTxCBOR);
});
});

test('caches transactions and returns them from the cache on subsequent calls', async () => {
const firstResponse = await provider.transactionsByHashes({
ids: ['1e043f100dce12d107f679685acd2fc0610e10f72a92d412794c9773d11d8477' as Cardano.TransactionId]
});

expect(firstResponse).toHaveLength(1);
expect(firstResponse[0]).toEqual(expectedHydratedTxCBOR);
expect(request).toHaveBeenCalled();

request.mockClear();

const secondResponse = await provider.transactionsByHashes({
ids: ['1e043f100dce12d107f679685acd2fc0610e10f72a92d412794c9773d11d8477' as Cardano.TransactionId]
});

expect(secondResponse).toHaveLength(1);
expect(secondResponse[0]).toEqual(expectedHydratedTxCBOR);
expect(request).not.toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,29 @@ describe('blockfrostUtxoProvider', () => {
}
});
});

test('does not call txs/${hash}/cbor when data is cached', async () => {
const txHash = '0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e78f19d9d5';

mockResponses(request, [
[`txs/${txHash}/cbor`, 'mockedCBORData'],
[`addresses/${address.toString()}/utxos?page=1&count=100`, generateUtxoResponseMock(1)],
[`addresses/${address.toString()}/utxos?page=2&count=100`, generateUtxoResponseMock(0)]
]);

const firstResponse = await provider.utxoByAddresses({ addresses: [address] });

expect(firstResponse).toBeTruthy();
expect(firstResponse.length).toBe(1);

expect(request).toHaveBeenCalled();
request.mockClear();

const secondResponse = await provider.utxoByAddresses({ addresses: [address] });

expect(secondResponse).toEqual(firstResponse);

expect(request).not.toHaveBeenCalledWith(`txs/${txHash}/cbor`);
});
});
});

0 comments on commit fbc7429

Please sign in to comment.