Skip to content

Commit

Permalink
transaction history: add input addresses, metadata and slots filter
Browse files Browse the repository at this point in the history
  • Loading branch information
ecioppettini committed Jan 11, 2024
1 parent f44fd33 commit 4cac134
Show file tree
Hide file tree
Showing 9 changed files with 274 additions and 9 deletions.
39 changes: 37 additions & 2 deletions webserver/server/app/controllers/TransactionHistoryController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Routes } from '../../../shared/routes';
import sortBy from 'lodash/sortBy';
import { getAddressTypes } from '../models/utils';
import { RelationFilterType } from '../../../shared/models/common';
import { slotBoundsPagination } from '../models/pagination/slotBoundsPagination.queries';

const route = Routes.transactionHistory;

Expand Down Expand Up @@ -62,7 +63,7 @@ export class TransactionHistoryController extends Controller {
const cardanoTxs = await tx<
ErrorShape | [TransactionHistoryResponse, TransactionHistoryResponse]
>(pool, async dbTx => {
const [until, pageStart] = await Promise.all([
const [until, pageStart, slotBounds] = await Promise.all([
resolveUntilTransaction({
block_hash: Buffer.from(requestBody.untilBlock, 'hex'),
dbTx,
Expand All @@ -74,6 +75,12 @@ export class TransactionHistoryController extends Controller {
after_tx: Buffer.from(requestBody.after.tx, 'hex'),
dbTx,
}),
!requestBody.slotLimits
? Promise.resolve(undefined)
: slotBoundsPagination.run(
{ low: requestBody.slotLimits.from, high: requestBody.slotLimits.to },
dbTx
),
]);
if (until == null) {
return genErrorMessage(Errors.BlockHashNotFound, {
Expand All @@ -87,8 +94,36 @@ export class TransactionHistoryController extends Controller {
});
}

let pageStartWithSlot = pageStart;

if (requestBody.slotLimits) {
if (!slotBounds || slotBounds.length === 0) {
throw new Error('todo');
} else {
const bounds = slotBounds[0];

const minTxId = Number(bounds.min_tx_id);

if (!pageStartWithSlot) {
pageStartWithSlot = {
block_id: -1,
tx_id: minTxId,
};
} else {
if (minTxId > pageStartWithSlot.tx_id) {
pageStartWithSlot.tx_id = minTxId;
}
}

const maxTxId = Number(bounds.max_tx_id);
if (maxTxId < until.tx_id) {
until.tx_id = maxTxId;
}
}
}

const commonRequest = {
after: pageStart,
after: pageStartWithSlot,
limit: requestBody.limit ?? ADDRESS_LIMIT.RESPONSE,
until,
dbTx,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/** Types generated for queries found in "app/models/pagination/slotBoundsPagination.sql" */
import { PreparedQuery } from '@pgtyped/runtime';

/** 'SlotBoundsPagination' parameters type */
export interface ISlotBoundsPaginationParams {
high: number;
low: number;
}

/** 'SlotBoundsPagination' return type */
export interface ISlotBoundsPaginationResult {
max_slot: number;
max_tx_id: string | null;
min_slot: number;
min_tx_id: string | null;
}

/** 'SlotBoundsPagination' query type */
export interface ISlotBoundsPaginationQuery {
params: ISlotBoundsPaginationParams;
result: ISlotBoundsPaginationResult;
}

const slotBoundsPaginationIR: any = {"usedParamSet":{"low":true,"high":true},"params":[{"name":"low","required":true,"transform":{"type":"scalar"},"locs":[{"a":193,"b":197}]},{"name":"high","required":true,"transform":{"type":"scalar"},"locs":[{"a":449,"b":454}]}],"statement":"WITH MIN_HASH AS\n\t(SELECT COALESCE(\"Transaction\".ID,\n\n\t\t\t\t\t\t\t\t\t\t-1) AS MIN_TX_ID,\n\t\t\tSLOT AS MIN_SLOT\n\t\tFROM \"Transaction\"\n\t\tJOIN \"Block\" ON \"Block\".ID = \"Transaction\".BLOCK_ID\n\t\tWHERE SLOT <= :low!\n\t\tORDER BY \"Block\".ID DESC, \"Transaction\".ID DESC\n\t\tLIMIT 1),\n\tMAX_HASH AS\n\t(SELECT SLOT AS MAX_SLOT,\n\t\t\tCOALESCE(MAX(\"Transaction\".ID),\n\n\t\t\t\t-2) AS MAX_TX_ID\n\t\tFROM \"Transaction\"\n\t\tJOIN \"Block\" ON \"Transaction\".BLOCK_ID = \"Block\".ID\n\t\tWHERE SLOT <= :high!\n\t\tGROUP BY \"Block\".ID\n\t\tORDER BY \"Block\".ID DESC\n\t\tLIMIT 1)\nSELECT *\nFROM MIN_HASH\nLEFT JOIN MAX_HASH ON 1 = 1"};

/**
* Query generated from SQL:
* ```
* WITH MIN_HASH AS
* (SELECT COALESCE("Transaction".ID,
*
* -1) AS MIN_TX_ID,
* SLOT AS MIN_SLOT
* FROM "Transaction"
* JOIN "Block" ON "Block".ID = "Transaction".BLOCK_ID
* WHERE SLOT <= :low!
* ORDER BY "Block".ID DESC, "Transaction".ID DESC
* LIMIT 1),
* MAX_HASH AS
* (SELECT SLOT AS MAX_SLOT,
* COALESCE(MAX("Transaction".ID),
*
* -2) AS MAX_TX_ID
* FROM "Transaction"
* JOIN "Block" ON "Transaction".BLOCK_ID = "Block".ID
* WHERE SLOT <= :high!
* GROUP BY "Block".ID
* ORDER BY "Block".ID DESC
* LIMIT 1)
* SELECT *
* FROM MIN_HASH
* LEFT JOIN MAX_HASH ON 1 = 1
* ```
*/
export const slotBoundsPagination = new PreparedQuery<ISlotBoundsPaginationParams,ISlotBoundsPaginationResult>(slotBoundsPaginationIR);


25 changes: 25 additions & 0 deletions webserver/server/app/models/pagination/slotBoundsPagination.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* @name slotBoundsPagination */
WITH MIN_HASH AS
(SELECT COALESCE("Transaction".ID,

-1) AS MIN_TX_ID,
SLOT AS MIN_SLOT
FROM "Transaction"
JOIN "Block" ON "Block".ID = "Transaction".BLOCK_ID
WHERE SLOT <= :low!
ORDER BY "Block".ID DESC, "Transaction".ID DESC
LIMIT 1),
MAX_HASH AS
(SELECT SLOT AS MAX_SLOT,
COALESCE(MAX("Transaction".ID),

-2) AS MAX_TX_ID
FROM "Transaction"
JOIN "Block" ON "Transaction".BLOCK_ID = "Block".ID
WHERE SLOT <= :high!
GROUP BY "Block".ID
ORDER BY "Block".ID DESC
LIMIT 1)
SELECT *
FROM MIN_HASH
LEFT JOIN MAX_HASH ON 1 = 1;
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { PreparedQuery } from '@pgtyped/runtime';

export type BufferArray = (Buffer)[];

export type Json = null | boolean | number | string | Json[] | { [key: string]: Json };

export type NumberOrString = number | string;

/** 'SqlHistoryForAddresses' parameters type */
Expand All @@ -21,7 +23,9 @@ export interface ISqlHistoryForAddressesResult {
hash: Buffer;
height: number;
id: string;
input_addresses: Json | null;
is_valid: boolean;
metadata: Buffer;
payload: Buffer;
slot: number;
tx_index: number;
Expand All @@ -33,7 +37,7 @@ export interface ISqlHistoryForAddressesQuery {
result: ISqlHistoryForAddressesResult;
}

const sqlHistoryForAddressesIR: any = {"usedParamSet":{"addresses":true,"until_tx_id":true,"after_tx_id":true,"limit":true},"params":[{"name":"addresses","required":false,"transform":{"type":"scalar"},"locs":[{"a":91,"b":100}]},{"name":"until_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":373,"b":384},{"a":788,"b":799},{"a":1250,"b":1261}]},{"name":"after_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":440,"b":451},{"a":854,"b":865},{"a":1325,"b":1336}]},{"name":"limit","required":false,"transform":{"type":"scalar"},"locs":[{"a":516,"b":521},{"a":929,"b":934},{"a":1409,"b":1414},{"a":1924,"b":1929}]}],"statement":"WITH\n address_row AS (\n SELECT *\n FROM \"Address\"\n WHERE \"Address\".payload = ANY (:addresses)\n ),\n outputs AS (\n SELECT DISTINCT ON (\"TransactionOutput\".tx_id) \"TransactionOutput\".tx_id\n FROM \"TransactionOutput\"\n INNER JOIN address_row ON \"TransactionOutput\".address_id = address_row.id\n WHERE\n \"TransactionOutput\".tx_id <= (:until_tx_id)\n AND\n \"TransactionOutput\".tx_id > (:after_tx_id)\n ORDER BY \"TransactionOutput\".tx_id ASC\n LIMIT (:limit)\n ),\n inputs AS (\n SELECT DISTINCT ON (\"TransactionInput\".tx_id) \"TransactionInput\".tx_id\n FROM \"TransactionInput\"\n INNER JOIN address_row ON \"TransactionInput\".address_id = address_row.id\n WHERE\n \"TransactionInput\".tx_id <= (:until_tx_id)\n AND\n \"TransactionInput\".tx_id > (:after_tx_id)\n ORDER BY \"TransactionInput\".tx_id ASC\n LIMIT (:limit)\n ),\n ref_inputs AS (\n SELECT DISTINCT ON (\"TransactionReferenceInput\".tx_id) \"TransactionReferenceInput\".tx_id\n FROM \"TransactionReferenceInput\"\n INNER JOIN address_row ON \"TransactionReferenceInput\".address_id = address_row.id\n WHERE\n \"TransactionReferenceInput\".tx_id <= (:until_tx_id)\n AND\n \"TransactionReferenceInput\".tx_id > (:after_tx_id)\n ORDER BY \"TransactionReferenceInput\".tx_id ASC\n LIMIT (:limit)\n )\nSELECT \"Transaction\".id,\n \"Transaction\".payload,\n \"Transaction\".hash,\n \"Transaction\".tx_index,\n \"Transaction\".is_valid,\n \"Block\".hash AS block_hash,\n \"Block\".epoch,\n \"Block\".slot,\n \"Block\".era,\n \"Block\".height\nFROM \"Transaction\"\nINNER JOIN \"Block\" ON \"Transaction\".block_id = \"Block\".id\nWHERE \"Transaction\".id IN (SELECT * FROM inputs UNION ALL SELECT * from ref_inputs UNION ALL SELECT * from outputs)\nORDER BY \"Transaction\".id ASC\nLIMIT (:limit)"};
const sqlHistoryForAddressesIR: any = {"usedParamSet":{"addresses":true,"until_tx_id":true,"after_tx_id":true,"limit":true},"params":[{"name":"addresses","required":false,"transform":{"type":"scalar"},"locs":[{"a":91,"b":100}]},{"name":"until_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":373,"b":384},{"a":788,"b":799},{"a":1250,"b":1261}]},{"name":"after_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":440,"b":451},{"a":854,"b":865},{"a":1325,"b":1336}]},{"name":"limit","required":false,"transform":{"type":"scalar"},"locs":[{"a":516,"b":521},{"a":929,"b":934},{"a":1409,"b":1414},{"a":2329,"b":2334}]}],"statement":"WITH\n address_row AS (\n SELECT *\n FROM \"Address\"\n WHERE \"Address\".payload = ANY (:addresses)\n ),\n outputs AS (\n SELECT DISTINCT ON (\"TransactionOutput\".tx_id) \"TransactionOutput\".tx_id\n FROM \"TransactionOutput\"\n INNER JOIN address_row ON \"TransactionOutput\".address_id = address_row.id\n WHERE\n \"TransactionOutput\".tx_id <= (:until_tx_id)\n AND\n \"TransactionOutput\".tx_id > (:after_tx_id)\n ORDER BY \"TransactionOutput\".tx_id ASC\n LIMIT (:limit)\n ),\n inputs AS (\n SELECT DISTINCT ON (\"TransactionInput\".tx_id) \"TransactionInput\".tx_id\n FROM \"TransactionInput\"\n INNER JOIN address_row ON \"TransactionInput\".address_id = address_row.id\n WHERE\n \"TransactionInput\".tx_id <= (:until_tx_id)\n AND\n \"TransactionInput\".tx_id > (:after_tx_id)\n ORDER BY \"TransactionInput\".tx_id ASC\n LIMIT (:limit)\n ),\n ref_inputs AS (\n SELECT DISTINCT ON (\"TransactionReferenceInput\".tx_id) \"TransactionReferenceInput\".tx_id\n FROM \"TransactionReferenceInput\"\n INNER JOIN address_row ON \"TransactionReferenceInput\".address_id = address_row.id\n WHERE\n \"TransactionReferenceInput\".tx_id <= (:until_tx_id)\n AND\n \"TransactionReferenceInput\".tx_id > (:after_tx_id)\n ORDER BY \"TransactionReferenceInput\".tx_id ASC\n LIMIT (:limit)\n )\nSELECT \"Transaction\".id,\n \"Transaction\".payload,\n \"Transaction\".hash,\n \"Transaction\".tx_index,\n \"Transaction\".is_valid,\n \"Block\".hash AS block_hash,\n \"Block\".epoch,\n \"Block\".slot,\n \"Block\".era,\n \"Block\".height,\n \"TransactionMetadata\".payload AS metadata,\n json_agg(DISTINCT \"Address\".PAYLOAD) input_addresses\nFROM \"Transaction\"\nINNER JOIN \"Block\" ON \"Transaction\".block_id = \"Block\".id\nINNER JOIN \"TransactionInput\" ON \"TransactionInput\".tx_id = \"Transaction\".id\nINNER JOIN \"Address\" ON \"Address\".id = \"TransactionInput\".address_id\nLEFT JOIN \"TransactionMetadata\" ON \"Transaction\".id = \"TransactionMetadata\".tx_id\nWHERE \"Transaction\".id IN (SELECT * FROM inputs UNION ALL SELECT * from ref_inputs UNION ALL SELECT * from outputs)\nGROUP BY \"Transaction\".id, \"Block\".id, \"TransactionMetadata\".id\nORDER BY \"Transaction\".id ASC\nLIMIT (:limit)"};

/**
* Query generated from SQL:
Expand Down Expand Up @@ -86,10 +90,16 @@ const sqlHistoryForAddressesIR: any = {"usedParamSet":{"addresses":true,"until_t
* "Block".epoch,
* "Block".slot,
* "Block".era,
* "Block".height
* "Block".height,
* "TransactionMetadata".payload AS metadata,
* json_agg(DISTINCT "Address".PAYLOAD) input_addresses
* FROM "Transaction"
* INNER JOIN "Block" ON "Transaction".block_id = "Block".id
* INNER JOIN "TransactionInput" ON "TransactionInput".tx_id = "Transaction".id
* INNER JOIN "Address" ON "Address".id = "TransactionInput".address_id
* LEFT JOIN "TransactionMetadata" ON "Transaction".id = "TransactionMetadata".tx_id
* WHERE "Transaction".id IN (SELECT * FROM inputs UNION ALL SELECT * from ref_inputs UNION ALL SELECT * from outputs)
* GROUP BY "Transaction".id, "Block".id, "TransactionMetadata".id
* ORDER BY "Transaction".id ASC
* LIMIT (:limit)
* ```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,15 @@ SELECT "Transaction".id,
"Block".epoch,
"Block".slot,
"Block".era,
"Block".height
"Block".height,
"TransactionMetadata".payload AS metadata,
json_agg(DISTINCT "Address".PAYLOAD) input_addresses
FROM "Transaction"
INNER JOIN "Block" ON "Transaction".block_id = "Block".id
INNER JOIN "TransactionInput" ON "TransactionInput".tx_id = "Transaction".id
INNER JOIN "Address" ON "Address".id = "TransactionInput".address_id
LEFT JOIN "TransactionMetadata" ON "Transaction".id = "TransactionMetadata".tx_id
WHERE "Transaction".id IN (SELECT * FROM inputs UNION ALL SELECT * from ref_inputs UNION ALL SELECT * from outputs)
GROUP BY "Transaction".id, "Block".id, "TransactionMetadata".id
ORDER BY "Transaction".id ASC
LIMIT (:limit);
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { PreparedQuery } from '@pgtyped/runtime';

export type BufferArray = (Buffer)[];

export type Json = null | boolean | number | string | Json[] | { [key: string]: Json };

export type NumberOrString = number | string;

/** 'SqlHistoryForCredentials' parameters type */
Expand All @@ -22,7 +24,9 @@ export interface ISqlHistoryForCredentialsResult {
hash: Buffer;
height: number;
id: string;
input_addresses: Json | null;
is_valid: boolean;
metadata: Buffer;
payload: Buffer;
slot: number;
tx_index: number;
Expand All @@ -34,7 +38,7 @@ export interface ISqlHistoryForCredentialsQuery {
result: ISqlHistoryForCredentialsResult;
}

const sqlHistoryForCredentialsIR: any = {"usedParamSet":{"credentials":true,"relation":true,"until_tx_id":true,"after_tx_id":true,"limit":true},"params":[{"name":"credentials","required":false,"transform":{"type":"scalar"},"locs":[{"a":288,"b":299}]},{"name":"relation","required":false,"transform":{"type":"scalar"},"locs":[{"a":354,"b":362}]},{"name":"until_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":464,"b":475}]},{"name":"after_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":527,"b":538}]},{"name":"limit","required":false,"transform":{"type":"scalar"},"locs":[{"a":598,"b":603}]}],"statement":"WITH\n tx_relations AS (\n SELECT DISTINCT ON (\"TxCredentialRelation\".tx_id) \"TxCredentialRelation\".tx_id\n FROM \"StakeCredential\"\n INNER JOIN \"TxCredentialRelation\" ON \"TxCredentialRelation\".credential_id = \"StakeCredential\".id\n WHERE\n \"StakeCredential\".credential = ANY (:credentials)\n AND\n (\"TxCredentialRelation\".relation & (:relation)) > 0\n AND\n \n \"TxCredentialRelation\".tx_id <= (:until_tx_id)\n AND \n \"TxCredentialRelation\".tx_id > (:after_tx_id)\n ORDER BY \"TxCredentialRelation\".tx_id ASC\n LIMIT (:limit)\n )\nSELECT \"Transaction\".id,\n \"Transaction\".payload,\n \"Transaction\".hash,\n \"Transaction\".tx_index,\n \"Transaction\".is_valid,\n \"Block\".hash AS block_hash,\n \"Block\".epoch,\n \"Block\".slot,\n \"Block\".era,\n \"Block\".height\nFROM tx_relations\nINNER JOIN \"Transaction\" ON tx_relations.tx_id = \"Transaction\".id\nINNER JOIN \"Block\" ON \"Transaction\".block_id = \"Block\".id"};
const sqlHistoryForCredentialsIR: any = {"usedParamSet":{"credentials":true,"relation":true,"until_tx_id":true,"after_tx_id":true,"limit":true},"params":[{"name":"credentials","required":false,"transform":{"type":"scalar"},"locs":[{"a":288,"b":299}]},{"name":"relation","required":false,"transform":{"type":"scalar"},"locs":[{"a":354,"b":362}]},{"name":"until_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":464,"b":475}]},{"name":"after_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":527,"b":538}]},{"name":"limit","required":false,"transform":{"type":"scalar"},"locs":[{"a":598,"b":603}]}],"statement":"WITH\n tx_relations AS (\n SELECT DISTINCT ON (\"TxCredentialRelation\".tx_id) \"TxCredentialRelation\".tx_id\n FROM \"StakeCredential\"\n INNER JOIN \"TxCredentialRelation\" ON \"TxCredentialRelation\".credential_id = \"StakeCredential\".id\n WHERE\n \"StakeCredential\".credential = ANY (:credentials)\n AND\n (\"TxCredentialRelation\".relation & (:relation)) > 0\n AND\n \n \"TxCredentialRelation\".tx_id <= (:until_tx_id)\n AND \n \"TxCredentialRelation\".tx_id > (:after_tx_id)\n ORDER BY \"TxCredentialRelation\".tx_id ASC\n LIMIT (:limit)\n )\nSELECT \"Transaction\".id,\n \"Transaction\".payload,\n \"Transaction\".hash,\n \"Transaction\".tx_index,\n \"Transaction\".is_valid,\n \"Block\".hash AS block_hash,\n \"Block\".epoch,\n \"Block\".slot,\n \"Block\".era,\n \"Block\".height,\n \"TransactionMetadata\".payload AS metadata,\n json_agg(DISTINCT \"Address\".PAYLOAD) input_addresses\nFROM tx_relations\nINNER JOIN \"Transaction\" ON tx_relations.tx_id = \"Transaction\".id\nINNER JOIN \"TransactionInput\" ON \"TransactionInput\".tx_id = \"Transaction\".id\nINNER JOIN \"Address\" ON \"Address\".id = \"TransactionInput\".address_id\nLEFT JOIN \"TransactionMetadata\" ON \"Transaction\".id = \"TransactionMetadata\".tx_id\nINNER JOIN \"Block\" ON \"Transaction\".block_id = \"Block\".id\nGROUP BY \"Transaction\".id, \"Block\".id, \"TransactionMetadata\".id"};

/**
* Query generated from SQL:
Expand Down Expand Up @@ -65,10 +69,16 @@ const sqlHistoryForCredentialsIR: any = {"usedParamSet":{"credentials":true,"rel
* "Block".epoch,
* "Block".slot,
* "Block".era,
* "Block".height
* "Block".height,
* "TransactionMetadata".payload AS metadata,
* json_agg(DISTINCT "Address".PAYLOAD) input_addresses
* FROM tx_relations
* INNER JOIN "Transaction" ON tx_relations.tx_id = "Transaction".id
* INNER JOIN "TransactionInput" ON "TransactionInput".tx_id = "Transaction".id
* INNER JOIN "Address" ON "Address".id = "TransactionInput".address_id
* LEFT JOIN "TransactionMetadata" ON "Transaction".id = "TransactionMetadata".tx_id
* INNER JOIN "Block" ON "Transaction".block_id = "Block".id
* GROUP BY "Transaction".id, "Block".id, "TransactionMetadata".id
* ```
*/
export const sqlHistoryForCredentials = new PreparedQuery<ISqlHistoryForCredentialsParams,ISqlHistoryForCredentialsResult>(sqlHistoryForCredentialsIR);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,14 @@ SELECT "Transaction".id,
"Block".epoch,
"Block".slot,
"Block".era,
"Block".height
"Block".height,
"TransactionMetadata".payload AS metadata,
json_agg(DISTINCT "Address".PAYLOAD) input_addresses
FROM tx_relations
INNER JOIN "Transaction" ON tx_relations.tx_id = "Transaction".id
INNER JOIN "Block" ON "Transaction".block_id = "Block".id;
INNER JOIN "TransactionInput" ON "TransactionInput".tx_id = "Transaction".id
INNER JOIN "Address" ON "Address".id = "TransactionInput".address_id
LEFT JOIN "TransactionMetadata" ON "Transaction".id = "TransactionMetadata".tx_id
INNER JOIN "Block" ON "Transaction".block_id = "Block".id
GROUP BY "Transaction".id, "Block".id, "TransactionMetadata".id;

Loading

0 comments on commit 4cac134

Please sign in to comment.