Skip to content

Commit 03d9796

Browse files
committed
fixup! chore(cardano-services): improve BlockfrostChainHistoryProvider perf
1 parent a6b79b6 commit 03d9796

File tree

2 files changed

+464
-42
lines changed

2 files changed

+464
-42
lines changed

packages/cardano-services/src/ChainHistory/BlockrostChainHistoryProvider/BlockfrostChainHistoryProvider.ts

Lines changed: 89 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ import {
1616
Paginated,
1717
ProviderError,
1818
ProviderFailure,
19+
Serialization,
1920
TransactionsByAddressesArgs,
2021
TransactionsByIdsArgs
2122
} from '@cardano-sdk/core';
2223
import { DB_MAX_SAFE_INTEGER } from '../DbSyncChainHistory/queries';
2324
import { Responses } from '@blockfrost/blockfrost-js';
25+
import { Schemas } from '@blockfrost/blockfrost-js/lib/types/open-api';
2426

2527
type WithCertIndex<T> = T & { cert_index: number };
2628

@@ -113,6 +115,29 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
113115
}))
114116
);
115117
}
118+
async fetchCBOR(hash: string): Promise<string> {
119+
return this.blockfrost
120+
.instance<Schemas['script_cbor']>(`/txs/${hash}/cbor`)
121+
.then((response) => {
122+
if (response.body.cbor) return response.body.cbor;
123+
throw new Error('CBOR is null');
124+
})
125+
.catch((_error) => {
126+
throw new Error('CBOR fetch failed');
127+
});
128+
}
129+
protected async fetchDetailsFromCBOR(hash: string) {
130+
return this.fetchCBOR(hash)
131+
.then((cbor) => {
132+
const tx = Serialization.Transaction.fromCbor(Serialization.TxCBOR(cbor)).toCore();
133+
this.logger.info('Fetched details from CBOR for tx', hash);
134+
return tx;
135+
})
136+
.catch((error) => {
137+
this.logger.warn('Failed to fetch details from CBOR for tx', hash, error);
138+
return null;
139+
});
140+
}
116141

117142
protected async fetchMirCerts(hash: string): Promise<WithCertIndex<Cardano.MirCertificate>[]> {
118143
return this.blockfrost.txsMirs(hash).then((response) =>
@@ -166,27 +191,40 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
166191
}: Responses['tx_content']): Promise<Cardano.Certificate[] | undefined> {
167192
if (pool_retire_count + pool_update_count + mir_cert_count + stake_cert_count + delegation_count === 0) return;
168193

169-
return Promise.all([
194+
const c = Promise.all([
170195
pool_retire_count ? this.fetchPoolRetireCerts(hash) : [],
171196
pool_update_count ? this.fetchPoolUpdateCerts(hash) : [],
172-
mir_cert_count ? this.fetchMirCerts(hash) : Promise.resolve([]),
173-
stake_cert_count ? this.fetchStakeCerts(hash) : Promise.resolve([]),
174-
delegation_count ? this.fetchDelegationCerts(hash) : Promise.resolve([])
197+
mir_cert_count ? this.fetchMirCerts(hash) : [],
198+
stake_cert_count ? this.fetchStakeCerts(hash) : [],
199+
delegation_count ? this.fetchDelegationCerts(hash) : []
175200
]).then((results) =>
176201
results
177202
.flat()
178203
.sort((a, b) => b.cert_index - a.cert_index)
179204
.map((cert) => cert as Cardano.Certificate)
180205
);
206+
// eslint-disable-next-line no-console
207+
console.debug(JSON.stringify(await c, (_key, value) => (typeof value === 'bigint' ? value.toString() : value)));
208+
return c;
181209
}
182210

183-
protected async fetchJsonMetadata(txHash: Cardano.TransactionId): Promise<Cardano.TxMetadata | null> {
211+
protected async fetchJsonMetadataAsAuxiliaryData(
212+
txHash: Cardano.TransactionId
213+
): Promise<Cardano.AuxiliaryData | undefined> {
214+
const UNDEFINED = undefined;
184215
return this.blockfrost
185216
.txsMetadata(txHash.toString())
186-
.then(blockfrostMetadataToTxMetadata)
217+
.then((m) => {
218+
const metadata = blockfrostMetadataToTxMetadata(m);
219+
return metadata && metadata.size > 0
220+
? {
221+
blob: metadata
222+
}
223+
: UNDEFINED;
224+
})
187225
.catch((error) => {
188226
if (isBlockfrostNotFoundError(error)) {
189-
return null;
227+
return UNDEFINED;
190228
}
191229
throw error;
192230
});
@@ -199,52 +237,68 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
199237
try {
200238
const txContent = await this.blockfrost.txs(hash.toString());
201239

202-
const [certificates, withdrawals, utxos, metadata] = await Promise.all([
203-
this.fetchCertificates(txContent),
204-
this.fetchWithdrawals(txContent),
240+
const txFromCBOR = await this.fetchDetailsFromCBOR(hash.toString());
241+
242+
const [certificates, withdrawals, utxos, auxiliaryData] = await Promise.all([
243+
txFromCBOR ? txFromCBOR.body.certificates : this.fetchCertificates(txContent),
244+
txFromCBOR ? txFromCBOR.body.withdrawals : this.fetchWithdrawals(txContent),
205245
this.blockfrost.txsUtxos(hash.toString()),
206-
this.fetchJsonMetadata(hash)
246+
txFromCBOR ? txFromCBOR.auxiliaryData : this.fetchJsonMetadataAsAuxiliaryData(hash)
207247
]);
208248

249+
// We can't use txFromCBOR.body.inputs since it misses HydratedTxIn.address
209250
const { inputs, outputs, collaterals } = BlockfrostToCore.transactionUtxos(utxos);
210251

211-
const inputSource: Cardano.InputSource = txContent.valid_contract
252+
const fee = txFromCBOR ? txFromCBOR.body.fee : BigInt(txContent.fees);
253+
const mint = txFromCBOR ? txFromCBOR.body.mint : this.gatherMintsFromUtxos(txContent, utxos);
254+
const validityInterval = txFromCBOR
255+
? txFromCBOR.body.validityInterval
256+
: {
257+
invalidBefore: this.parseValidityInterval(txContent.invalid_before),
258+
invalidHereafter: this.parseValidityInterval(txContent.invalid_hereafter)
259+
};
260+
261+
const witness = txFromCBOR
262+
? txFromCBOR.witness
263+
: {
264+
redeemers: await this.fetchRedeemers(txContent),
265+
signatures: new Map() // not available in blockfrost
266+
};
267+
268+
// can txFromCBOR.isValid also be used?
269+
const valid_contract = txContent.valid_contract;
270+
271+
const inputSource: Cardano.InputSource = valid_contract
212272
? Cardano.InputSource.inputs
213273
: Cardano.InputSource.collaterals;
214274

275+
// can we get these from cbor?
276+
const index = txContent.index;
277+
const txSize = txContent.size;
278+
const blockHeader = {
279+
blockNo: Cardano.BlockNo(txContent.block_height),
280+
hash: Cardano.BlockId(txContent.block),
281+
slot: Cardano.Slot(txContent.slot)
282+
};
283+
215284
return {
216-
auxiliaryData:
217-
metadata && metadata.size > 0
218-
? {
219-
blob: metadata
220-
}
221-
: undefined,
222-
blockHeader: {
223-
blockNo: Cardano.BlockNo(txContent.block_height),
224-
hash: Cardano.BlockId(txContent.block),
225-
slot: Cardano.Slot(txContent.slot)
226-
},
285+
auxiliaryData,
286+
blockHeader,
227287
body: {
228288
certificates,
229289
collaterals,
230-
fee: BigInt(txContent.fees),
290+
fee,
231291
inputs,
232-
mint: this.gatherMintsFromUtxos(txContent, utxos),
292+
mint,
233293
outputs,
234-
validityInterval: {
235-
invalidBefore: this.parseValidityInterval(txContent.invalid_before),
236-
invalidHereafter: this.parseValidityInterval(txContent.invalid_hereafter)
237-
},
294+
validityInterval,
238295
withdrawals
239296
},
240297
id: hash,
241-
index: txContent.index,
298+
index,
242299
inputSource,
243-
txSize: txContent.size,
244-
witness: {
245-
redeemers: await this.fetchRedeemers(txContent),
246-
signatures: new Map() // not available in blockfrost
247-
}
300+
txSize,
301+
witness
248302
};
249303
} catch (error) {
250304
throw blockfrostToProviderError(error);

0 commit comments

Comments
 (0)