Skip to content

Commit

Permalink
fixup! chore(cardano-services): improve BlockfrostChainHistoryProvide…
Browse files Browse the repository at this point in the history
…r perf
  • Loading branch information
ginnun committed Oct 4, 2024
1 parent a6b79b6 commit 03d9796
Show file tree
Hide file tree
Showing 2 changed files with 464 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ import {
Paginated,
ProviderError,
ProviderFailure,
Serialization,
TransactionsByAddressesArgs,
TransactionsByIdsArgs
} from '@cardano-sdk/core';
import { DB_MAX_SAFE_INTEGER } from '../DbSyncChainHistory/queries';
import { Responses } from '@blockfrost/blockfrost-js';
import { Schemas } from '@blockfrost/blockfrost-js/lib/types/open-api';

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

Expand Down Expand Up @@ -113,6 +115,29 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
}))
);
}
async fetchCBOR(hash: string): Promise<string> {
return this.blockfrost
.instance<Schemas['script_cbor']>(`/txs/${hash}/cbor`)
.then((response) => {
if (response.body.cbor) return response.body.cbor;
throw new Error('CBOR is null');
})
.catch((_error) => {
throw new Error('CBOR fetch failed');
});
}
protected async fetchDetailsFromCBOR(hash: string) {
return this.fetchCBOR(hash)
.then((cbor) => {
const tx = Serialization.Transaction.fromCbor(Serialization.TxCBOR(cbor)).toCore();
this.logger.info('Fetched details from CBOR for tx', hash);
return tx;
})
.catch((error) => {
this.logger.warn('Failed to fetch details from CBOR for tx', hash, error);
return null;
});
}

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

return Promise.all([
const c = Promise.all([
pool_retire_count ? this.fetchPoolRetireCerts(hash) : [],
pool_update_count ? this.fetchPoolUpdateCerts(hash) : [],
mir_cert_count ? this.fetchMirCerts(hash) : Promise.resolve([]),
stake_cert_count ? this.fetchStakeCerts(hash) : Promise.resolve([]),
delegation_count ? this.fetchDelegationCerts(hash) : Promise.resolve([])
mir_cert_count ? this.fetchMirCerts(hash) : [],
stake_cert_count ? this.fetchStakeCerts(hash) : [],
delegation_count ? this.fetchDelegationCerts(hash) : []
]).then((results) =>
results
.flat()
.sort((a, b) => b.cert_index - a.cert_index)
.map((cert) => cert as Cardano.Certificate)
);
// eslint-disable-next-line no-console
console.debug(JSON.stringify(await c, (_key, value) => (typeof value === 'bigint' ? value.toString() : value)));
return c;
}

protected async fetchJsonMetadata(txHash: Cardano.TransactionId): Promise<Cardano.TxMetadata | null> {
protected async fetchJsonMetadataAsAuxiliaryData(
txHash: Cardano.TransactionId
): Promise<Cardano.AuxiliaryData | undefined> {
const UNDEFINED = undefined;
return this.blockfrost
.txsMetadata(txHash.toString())
.then(blockfrostMetadataToTxMetadata)
.then((m) => {
const metadata = blockfrostMetadataToTxMetadata(m);
return metadata && metadata.size > 0
? {
blob: metadata
}
: UNDEFINED;
})
.catch((error) => {
if (isBlockfrostNotFoundError(error)) {
return null;
return UNDEFINED;
}
throw error;
});
Expand All @@ -199,52 +237,68 @@ export class BlockfrostChainHistoryProvider extends BlockfrostProvider implement
try {
const txContent = await this.blockfrost.txs(hash.toString());

const [certificates, withdrawals, utxos, metadata] = await Promise.all([
this.fetchCertificates(txContent),
this.fetchWithdrawals(txContent),
const txFromCBOR = await this.fetchDetailsFromCBOR(hash.toString());

const [certificates, withdrawals, utxos, auxiliaryData] = await Promise.all([
txFromCBOR ? txFromCBOR.body.certificates : this.fetchCertificates(txContent),
txFromCBOR ? txFromCBOR.body.withdrawals : this.fetchWithdrawals(txContent),
this.blockfrost.txsUtxos(hash.toString()),
this.fetchJsonMetadata(hash)
txFromCBOR ? txFromCBOR.auxiliaryData : this.fetchJsonMetadataAsAuxiliaryData(hash)
]);

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

const inputSource: Cardano.InputSource = txContent.valid_contract
const fee = txFromCBOR ? txFromCBOR.body.fee : BigInt(txContent.fees);
const mint = txFromCBOR ? txFromCBOR.body.mint : this.gatherMintsFromUtxos(txContent, utxos);
const validityInterval = txFromCBOR
? txFromCBOR.body.validityInterval
: {
invalidBefore: this.parseValidityInterval(txContent.invalid_before),
invalidHereafter: this.parseValidityInterval(txContent.invalid_hereafter)
};

const witness = txFromCBOR
? txFromCBOR.witness
: {
redeemers: await this.fetchRedeemers(txContent),
signatures: new Map() // not available in blockfrost
};

// can txFromCBOR.isValid also be used?
const valid_contract = txContent.valid_contract;

const inputSource: Cardano.InputSource = valid_contract
? Cardano.InputSource.inputs
: Cardano.InputSource.collaterals;

// can we get these from cbor?
const index = txContent.index;
const txSize = txContent.size;
const blockHeader = {
blockNo: Cardano.BlockNo(txContent.block_height),
hash: Cardano.BlockId(txContent.block),
slot: Cardano.Slot(txContent.slot)
};

return {
auxiliaryData:
metadata && metadata.size > 0
? {
blob: metadata
}
: undefined,
blockHeader: {
blockNo: Cardano.BlockNo(txContent.block_height),
hash: Cardano.BlockId(txContent.block),
slot: Cardano.Slot(txContent.slot)
},
auxiliaryData,
blockHeader,
body: {
certificates,
collaterals,
fee: BigInt(txContent.fees),
fee,
inputs,
mint: this.gatherMintsFromUtxos(txContent, utxos),
mint,
outputs,
validityInterval: {
invalidBefore: this.parseValidityInterval(txContent.invalid_before),
invalidHereafter: this.parseValidityInterval(txContent.invalid_hereafter)
},
validityInterval,
withdrawals
},
id: hash,
index: txContent.index,
index,
inputSource,
txSize: txContent.size,
witness: {
redeemers: await this.fetchRedeemers(txContent),
signatures: new Map() // not available in blockfrost
}
txSize,
witness
};
} catch (error) {
throw blockfrostToProviderError(error);
Expand Down
Loading

0 comments on commit 03d9796

Please sign in to comment.