mirror of
https://github.com/alexgo-io/stacks-blockchain-api.git
synced 2026-05-13 20:16:45 +08:00
fix: get abi for all tx queries
* chore: refactor abi column into helper * fix: extend microblock tests * fix: block txs * chore: block with metadata * chore: get tx strict * chore: search * chore: search principal * chore: fixme, not todo * fix: remove count * fix: comma typo * chore: block txs rows * chore: tx search * chore: get block from data store * chore: get txs from block
This commit is contained in:
@@ -302,11 +302,6 @@ const MEMPOOL_TX_COLUMNS = `
|
||||
coinbase_payload
|
||||
`;
|
||||
|
||||
const MEMPOOL_TX_ID_COLUMNS = `
|
||||
-- required columns
|
||||
tx_id
|
||||
`;
|
||||
|
||||
const BLOCK_COLUMNS = `
|
||||
block_hash, index_block_hash,
|
||||
parent_index_block_hash, parent_block_hash, parent_microblock_hash, parent_microblock_sequence,
|
||||
@@ -322,6 +317,24 @@ const MICROBLOCK_COLUMNS = `
|
||||
index_block_hash, block_hash
|
||||
`;
|
||||
|
||||
/**
|
||||
* Shorthand function that returns a column query to retrieve the smart contract abi when querying transactions
|
||||
* that may be of type `contract_call`. Usually used alongside `TX_COLUMNS` or `MEMPOOL_TX_COLUMNS`.
|
||||
* @param tableName - Name of the table that will determine the transaction type.
|
||||
* @returns `string` - abi column select statement portion
|
||||
*/
|
||||
function abiColumn(tableName: string = 'txs'): string {
|
||||
return `
|
||||
CASE WHEN ${tableName}.type_id = ${DbTxTypeId.ContractCall} THEN (
|
||||
SELECT abi
|
||||
FROM smart_contracts
|
||||
WHERE smart_contracts.contract_id = ${tableName}.contract_call_contract_id
|
||||
ORDER BY abi != 'null' DESC, canonical DESC, microblock_canonical DESC, block_height DESC
|
||||
LIMIT 1
|
||||
) END as abi
|
||||
`;
|
||||
}
|
||||
|
||||
interface BlockQueryResult {
|
||||
block_hash: Buffer;
|
||||
index_block_hash: Buffer;
|
||||
@@ -1690,9 +1703,9 @@ export class PgDataStore
|
||||
// Get transactions that have been streamed in microblocks but not yet accepted or rejected in an anchor block.
|
||||
const { blockHeight } = await this.getChainTip(client);
|
||||
const unanchoredBlockHeight = blockHeight + 1;
|
||||
const query = await client.query<TxQueryResult>(
|
||||
const query = await client.query<ContractTxQueryResult>(
|
||||
`
|
||||
SELECT ${TX_COLUMNS}
|
||||
SELECT ${TX_COLUMNS}, ${abiColumn()}
|
||||
FROM txs
|
||||
WHERE canonical = true AND microblock_canonical = true AND block_height = $1
|
||||
ORDER BY block_height DESC, microblock_sequence DESC, tx_index DESC
|
||||
@@ -2574,9 +2587,9 @@ export class PgDataStore
|
||||
let microblocksAccepted: DbMicroblock[] | null = null;
|
||||
let microblocksStreamed: DbMicroblock[] | null = null;
|
||||
if (metadata?.txs) {
|
||||
const txQuery = await client.query<TxQueryResult>(
|
||||
const txQuery = await client.query<ContractTxQueryResult>(
|
||||
`
|
||||
SELECT ${TX_COLUMNS}
|
||||
SELECT ${TX_COLUMNS}, ${abiColumn()}
|
||||
FROM txs
|
||||
WHERE index_block_hash = $1
|
||||
ORDER BY microblock_sequence DESC, tx_index DESC
|
||||
@@ -2833,9 +2846,9 @@ export class PgDataStore
|
||||
|
||||
async getBlockTxsRows(blockHash: string) {
|
||||
return this.query(async client => {
|
||||
const result = await client.query<TxQueryResult>(
|
||||
const result = await client.query<ContractTxQueryResult>(
|
||||
`
|
||||
SELECT ${TX_COLUMNS}
|
||||
SELECT ${TX_COLUMNS}, ${abiColumn()}
|
||||
FROM txs
|
||||
WHERE block_hash = $1 AND canonical = true AND microblock_canonical = true
|
||||
`,
|
||||
@@ -2969,10 +2982,9 @@ export class PgDataStore
|
||||
`,
|
||||
[hexToBuffer(blockQuery.result.index_block_hash)]
|
||||
);
|
||||
|
||||
const result = await client.query<TxQueryResult>(
|
||||
const result = await client.query<ContractTxQueryResult>(
|
||||
`
|
||||
SELECT ${TX_COLUMNS}
|
||||
SELECT ${TX_COLUMNS}, ${abiColumn()}
|
||||
FROM txs
|
||||
WHERE canonical = true AND microblock_canonical = true AND index_block_hash = $1
|
||||
LIMIT $2
|
||||
@@ -2980,10 +2992,7 @@ export class PgDataStore
|
||||
`,
|
||||
[hexToBuffer(blockQuery.result.index_block_hash), limit, offset]
|
||||
);
|
||||
let total = 0;
|
||||
if (totalQuery.rowCount > 0) {
|
||||
total = totalQuery.rows[0].count;
|
||||
}
|
||||
const total = totalQuery.rowCount > 0 ? totalQuery.rows[0].count : 0;
|
||||
const parsed = result.rows.map(r => this.parseTxQueryResult(r));
|
||||
return { results: parsed, total };
|
||||
});
|
||||
@@ -3647,9 +3656,9 @@ export class PgDataStore
|
||||
|
||||
async getTxStrict(args: { txId: string; indexBlockHash: string }): Promise<FoundOrNot<DbTx>> {
|
||||
return this.query(async client => {
|
||||
const result = await client.query<TxQueryResult>(
|
||||
const result = await client.query<ContractTxQueryResult>(
|
||||
`
|
||||
SELECT ${TX_COLUMNS}
|
||||
SELECT ${TX_COLUMNS}, ${abiColumn()}
|
||||
FROM txs
|
||||
WHERE tx_id = $1 AND index_block_hash = $2
|
||||
ORDER BY canonical DESC, microblock_canonical DESC, block_height DESC
|
||||
@@ -3721,7 +3730,7 @@ export class PgDataStore
|
||||
includeUnanchored: boolean;
|
||||
}) {
|
||||
let totalQuery: QueryResult<{ count: number }>;
|
||||
let resultQuery: QueryResult<TxQueryResult>;
|
||||
let resultQuery: QueryResult<ContractTxQueryResult>;
|
||||
return this.queryTx(async client => {
|
||||
const maxHeight = await this.getMaxBlockHeight(client, { includeUnanchored });
|
||||
|
||||
@@ -3734,23 +3743,16 @@ export class PgDataStore
|
||||
`,
|
||||
[maxHeight]
|
||||
);
|
||||
resultQuery = await client.query<TxQueryResult>(
|
||||
resultQuery = await client.query<ContractTxQueryResult>(
|
||||
`
|
||||
SELECT ${TX_COLUMNS},
|
||||
CASE WHEN txs.type_id = $4 THEN (
|
||||
SELECT abi
|
||||
FROM smart_contracts
|
||||
WHERE smart_contracts.contract_id = txs.contract_call_contract_id
|
||||
ORDER BY abi != 'null' DESC, canonical DESC, microblock_canonical DESC, block_height DESC
|
||||
LIMIT 1
|
||||
) END as abi
|
||||
SELECT ${TX_COLUMNS}, ${abiColumn()}
|
||||
FROM txs
|
||||
WHERE canonical = true AND microblock_canonical = true AND block_height <= $3
|
||||
ORDER BY block_height DESC, microblock_sequence DESC, tx_index DESC
|
||||
LIMIT $1
|
||||
OFFSET $2
|
||||
`,
|
||||
[limit, offset, maxHeight, DbTxTypeId.ContractCall]
|
||||
[limit, offset, maxHeight]
|
||||
);
|
||||
} else {
|
||||
const txTypeIds = txTypeFilter.map<number>(t => getTxTypeId(t));
|
||||
@@ -3762,23 +3764,16 @@ export class PgDataStore
|
||||
`,
|
||||
[txTypeIds, maxHeight]
|
||||
);
|
||||
resultQuery = await client.query<TxQueryResult>(
|
||||
resultQuery = await client.query<ContractTxQueryResult>(
|
||||
`
|
||||
SELECT ${TX_COLUMNS}
|
||||
CASE WHEN txs.type_id = $5 THEN (
|
||||
SELECT abi
|
||||
FROM smart_contracts
|
||||
WHERE smart_contracts.contract_id = txs.contract_call_contract_id
|
||||
ORDER BY abi != 'null' DESC, canonical DESC, microblock_canonical DESC, block_height DESC
|
||||
LIMIT 1
|
||||
) END as abi
|
||||
SELECT ${TX_COLUMNS}, ${abiColumn()}
|
||||
FROM txs
|
||||
WHERE canonical = true AND microblock_canonical = true AND type_id = ANY($1) AND block_height <= $4
|
||||
ORDER BY block_height DESC, microblock_sequence DESC, tx_index DESC
|
||||
LIMIT $2
|
||||
OFFSET $3
|
||||
`,
|
||||
[txTypeIds, limit, offset, maxHeight, DbTxTypeId.ContractCall]
|
||||
[txTypeIds, limit, offset, maxHeight]
|
||||
);
|
||||
}
|
||||
const parsed = resultQuery.rows.map(r => this.parseTxQueryResult(r));
|
||||
@@ -5298,24 +5293,14 @@ export class PgDataStore
|
||||
ON txs.tx_id = event_txs.tx_id
|
||||
WHERE txs.canonical = true AND txs.microblock_canonical = true
|
||||
)
|
||||
SELECT ${TX_COLUMNS},
|
||||
CASE
|
||||
WHEN principal_txs.type_id = $5 THEN (
|
||||
SELECT abi
|
||||
FROM smart_contracts
|
||||
WHERE smart_contracts.contract_id = principal_txs.contract_call_contract_id
|
||||
ORDER BY abi != 'null' DESC, canonical DESC, microblock_canonical DESC, block_height DESC
|
||||
LIMIT 1
|
||||
)
|
||||
END as abi,
|
||||
(COUNT(*) OVER())::integer as count
|
||||
SELECT ${TX_COLUMNS}, ${abiColumn('principal_txs')}, (COUNT(*) OVER())::integer as count
|
||||
FROM principal_txs
|
||||
${args.atSingleBlock ? 'WHERE block_height = $4' : 'WHERE block_height <= $4'}
|
||||
ORDER BY block_height DESC, microblock_sequence DESC, tx_index DESC
|
||||
LIMIT $2
|
||||
OFFSET $3
|
||||
`,
|
||||
[args.stxAddress, args.limit, args.offset, args.blockHeight, DbTxTypeId.ContractCall]
|
||||
[args.stxAddress, args.limit, args.offset, args.blockHeight]
|
||||
);
|
||||
const count = resultQuery.rowCount > 0 ? resultQuery.rows[0].count : 0;
|
||||
const parsed = resultQuery.rows.map(r => this.parseTxQueryResult(r));
|
||||
@@ -5331,11 +5316,7 @@ export class PgDataStore
|
||||
tx_id: string;
|
||||
}): Promise<DbTxWithAssetTransfers> {
|
||||
return this.query(async client => {
|
||||
const queryParams: (string | Buffer | DbTxTypeId)[] = [
|
||||
stxAddress,
|
||||
hexToBuffer(tx_id),
|
||||
DbTxTypeId.ContractCall,
|
||||
];
|
||||
const queryParams: (string | Buffer)[] = [stxAddress, hexToBuffer(tx_id)];
|
||||
const resultQuery = await client.query<
|
||||
ContractTxQueryResult & {
|
||||
count: number;
|
||||
@@ -5380,13 +5361,7 @@ export class PgDataStore
|
||||
events.amount as event_amount,
|
||||
events.sender as event_sender,
|
||||
events.recipient as event_recipient,
|
||||
CASE WHEN transactions.type_id = $3 THEN (
|
||||
SELECT abi
|
||||
FROM smart_contracts
|
||||
WHERE smart_contracts.contract_id = transactions.contract_call_contract_id
|
||||
ORDER BY abi != 'null' DESC, canonical DESC, microblock_canonical DESC, block_height DESC
|
||||
LIMIT 1
|
||||
) END as abi
|
||||
${abiColumn('transactions')}
|
||||
FROM transactions
|
||||
LEFT JOIN events ON transactions.tx_id = events.tx_id AND transactions.tx_id = $2
|
||||
ORDER BY block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC
|
||||
@@ -5408,7 +5383,7 @@ export class PgDataStore
|
||||
offset?: number;
|
||||
}): Promise<{ results: DbTxWithAssetTransfers[]; total: number }> {
|
||||
return this.queryTx(async client => {
|
||||
const queryParams: (string | number)[] = [args.stxAddress, DbTxTypeId.ContractCall];
|
||||
const queryParams: (string | number)[] = [args.stxAddress];
|
||||
|
||||
if (args.atSingleBlock) {
|
||||
queryParams.push(args.blockHeight);
|
||||
@@ -5419,7 +5394,7 @@ export class PgDataStore
|
||||
}
|
||||
// Use a JOIN to include stx_events associated with the address's txs
|
||||
const resultQuery = await client.query<
|
||||
TxQueryResult & {
|
||||
ContractTxQueryResult & {
|
||||
count: number;
|
||||
event_index?: number;
|
||||
event_type?: number;
|
||||
@@ -5454,9 +5429,9 @@ export class PgDataStore
|
||||
)
|
||||
SELECT ${TX_COLUMNS}, (COUNT(*) OVER())::integer as count
|
||||
FROM principal_txs
|
||||
${args.atSingleBlock ? 'WHERE block_height = $3' : 'WHERE block_height <= $5'}
|
||||
${args.atSingleBlock ? 'WHERE block_height = $2' : 'WHERE block_height <= $4'}
|
||||
ORDER BY block_height DESC, microblock_sequence DESC, tx_index DESC
|
||||
${!args.atSingleBlock ? 'LIMIT $3 OFFSET $4' : ''}
|
||||
${!args.atSingleBlock ? 'LIMIT $2 OFFSET $3' : ''}
|
||||
), events AS (
|
||||
SELECT
|
||||
tx_id, sender, recipient, event_index, amount,
|
||||
@@ -5481,13 +5456,7 @@ export class PgDataStore
|
||||
)
|
||||
SELECT
|
||||
transactions.*,
|
||||
CASE WHEN transactions.type_id = $2 THEN (
|
||||
SELECT abi
|
||||
FROM smart_contracts
|
||||
WHERE smart_contracts.contract_id = transactions.contract_call_contract_id
|
||||
ORDER BY abi != 'null' DESC, canonical DESC, microblock_canonical DESC, block_height DESC
|
||||
LIMIT 1
|
||||
) END as abi,
|
||||
${abiColumn('transactions')},
|
||||
events.event_index as event_index,
|
||||
events.event_type_id as event_type,
|
||||
events.amount as event_amount,
|
||||
@@ -5703,8 +5672,8 @@ export class PgDataStore
|
||||
async searchHash({ hash }: { hash: string }): Promise<FoundOrNot<DbSearchResult>> {
|
||||
// TODO(mb): add support for searching for microblock by hash
|
||||
return this.query(async client => {
|
||||
const txQuery = await client.query<TxQueryResult>(
|
||||
`SELECT ${TX_COLUMNS} FROM txs WHERE tx_id = $1 LIMIT 1`,
|
||||
const txQuery = await client.query<ContractTxQueryResult>(
|
||||
`SELECT ${TX_COLUMNS}, ${abiColumn()} FROM txs WHERE tx_id = $1 LIMIT 1`,
|
||||
[hexToBuffer(hash)]
|
||||
);
|
||||
if (txQuery.rowCount > 0) {
|
||||
@@ -5781,9 +5750,9 @@ export class PgDataStore
|
||||
},
|
||||
};
|
||||
}
|
||||
const contractTxResult = await client.query<TxQueryResult>(
|
||||
const contractTxResult = await client.query<ContractTxQueryResult>(
|
||||
`
|
||||
SELECT ${TX_COLUMNS}
|
||||
SELECT ${TX_COLUMNS}, ${abiColumn()}
|
||||
FROM txs
|
||||
WHERE smart_contract_contract_id = $1
|
||||
ORDER BY canonical DESC, microblock_canonical DESC, block_height DESC
|
||||
@@ -6135,20 +6104,11 @@ export class PgDataStore
|
||||
const maxBlockHeight = await this.getMaxBlockHeight(client, { includeUnanchored });
|
||||
const result = await client.query<ContractTxQueryResult>(
|
||||
`
|
||||
SELECT ${TX_COLUMNS},
|
||||
CASE
|
||||
WHEN txs.type_id = $3 THEN (
|
||||
SELECT abi
|
||||
FROM smart_contracts
|
||||
WHERE smart_contracts.contract_id = txs.contract_call_contract_id
|
||||
ORDER BY abi != 'null' DESC, canonical DESC, microblock_canonical DESC, block_height DESC
|
||||
LIMIT 1
|
||||
)
|
||||
END as abi
|
||||
SELECT ${TX_COLUMNS}, ${abiColumn()}
|
||||
FROM txs
|
||||
WHERE tx_id = ANY($1) AND block_height <= $2 AND canonical = true AND microblock_canonical = true
|
||||
`,
|
||||
[values, maxBlockHeight, DbTxTypeId.ContractCall]
|
||||
[values, maxBlockHeight]
|
||||
);
|
||||
if (result.rowCount === 0) {
|
||||
return [];
|
||||
|
||||
@@ -4164,7 +4164,7 @@ describe('api tests', () => {
|
||||
abi: JSON.stringify(contractJsonAbi),
|
||||
};
|
||||
const contractCall: DbTx = {
|
||||
tx_id: '0x1232',
|
||||
tx_id: '0x1232000000000000000000000000000000000000000000000000000000000000',
|
||||
tx_index: 5,
|
||||
anchor_mode: 3,
|
||||
nonce: 0,
|
||||
@@ -4175,7 +4175,6 @@ describe('api tests', () => {
|
||||
burn_block_time: block.burn_block_time,
|
||||
parent_burn_block_time: 1626122935,
|
||||
type_id: DbTxTypeId.ContractCall,
|
||||
coinbase_payload: Buffer.from('coinbase hi'),
|
||||
status: 1,
|
||||
raw_result: '0x0100000000000000000000000000000001', // u1
|
||||
canonical: true,
|
||||
@@ -4532,7 +4531,7 @@ describe('api tests', () => {
|
||||
execution_cost_write_length: 0,
|
||||
},
|
||||
{
|
||||
tx_id: '0x1232',
|
||||
tx_id: '0x1232000000000000000000000000000000000000000000000000000000000000',
|
||||
tx_status: 'success',
|
||||
tx_result: {
|
||||
hex: '0x0100000000000000000000000000000001', // u1
|
||||
@@ -4682,7 +4681,7 @@ describe('api tests', () => {
|
||||
total: 1,
|
||||
results: [
|
||||
{
|
||||
tx_id: '0x1232',
|
||||
tx_id: '0x1232000000000000000000000000000000000000000000000000000000000000',
|
||||
tx_status: 'success',
|
||||
tx_result: {
|
||||
hex: '0x0100000000000000000000000000000001', // u1
|
||||
@@ -4810,7 +4809,7 @@ describe('api tests', () => {
|
||||
sender_address: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
|
||||
sponsor_address: 'ST3J8EVYHVKH6XXPD61EE8XEHW4Y2K83861225AB1',
|
||||
sponsored: false,
|
||||
tx_id: '0x1232',
|
||||
tx_id: '0x1232000000000000000000000000000000000000000000000000000000000000',
|
||||
tx_index: 5,
|
||||
tx_result: {
|
||||
hex: '0x0100000000000000000000000000000001',
|
||||
@@ -4825,7 +4824,7 @@ describe('api tests', () => {
|
||||
expect(JSON.parse(fetchAddrTx3.text)).toEqual(expectedResp6);
|
||||
|
||||
const fetchAddrTx4 = await supertest(api.server).get(
|
||||
`/extended/v1/address/${testAddr5}/0x1232/with_transfers`
|
||||
`/extended/v1/address/${testAddr5}/0x1232000000000000000000000000000000000000000000000000000000000000/with_transfers`
|
||||
);
|
||||
expect(fetchAddrTx4.status).toBe(200);
|
||||
expect(fetchAddrTx4.type).toBe('application/json');
|
||||
@@ -4886,7 +4885,7 @@ describe('api tests', () => {
|
||||
sender_address: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
|
||||
sponsor_address: 'ST3J8EVYHVKH6XXPD61EE8XEHW4Y2K83861225AB1',
|
||||
sponsored: false,
|
||||
tx_id: '0x1232',
|
||||
tx_id: '0x1232000000000000000000000000000000000000000000000000000000000000',
|
||||
tx_index: 5,
|
||||
tx_result: {
|
||||
hex: '0x0100000000000000000000000000000001',
|
||||
@@ -4897,6 +4896,77 @@ describe('api tests', () => {
|
||||
},
|
||||
};
|
||||
expect(JSON.parse(fetchAddrTx4.text)).toEqual(expectedResp7);
|
||||
|
||||
const contractCallExpectedResults = {
|
||||
tx_id: '0x1232000000000000000000000000000000000000000000000000000000000000',
|
||||
tx_status: 'success',
|
||||
tx_result: {
|
||||
hex: '0x0100000000000000000000000000000001', // u1
|
||||
repr: 'u1',
|
||||
},
|
||||
tx_type: 'contract_call',
|
||||
fee_rate: '10',
|
||||
is_unanchored: false,
|
||||
nonce: 0,
|
||||
anchor_mode: 'any',
|
||||
sender_address: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
|
||||
sponsor_address: 'ST3J8EVYHVKH6XXPD61EE8XEHW4Y2K83861225AB1',
|
||||
sponsored: false,
|
||||
post_condition_mode: 'allow',
|
||||
post_conditions: [],
|
||||
block_hash: '0x1234',
|
||||
block_height: 1,
|
||||
burn_block_time: 39486,
|
||||
burn_block_time_iso: '1970-01-01T10:58:06.000Z',
|
||||
canonical: true,
|
||||
microblock_canonical: true,
|
||||
microblock_hash: '',
|
||||
microblock_sequence: I32_MAX,
|
||||
parent_block_hash: '',
|
||||
parent_burn_block_time: 1626122935,
|
||||
parent_burn_block_time_iso: '2021-07-12T20:48:55.000Z',
|
||||
tx_index: 5,
|
||||
contract_call: {
|
||||
contract_id: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
|
||||
function_name: 'test-contract-fn',
|
||||
function_signature: '(define-public (test-contract-fn (amount uint) (desc string-ascii)))',
|
||||
function_args: [
|
||||
{
|
||||
hex: '0x010000000000000000000000000001e240',
|
||||
name: 'amount',
|
||||
repr: 'u123456',
|
||||
type: 'uint',
|
||||
},
|
||||
{
|
||||
hex: '0x0d0000000568656c6c6f',
|
||||
name: 'desc',
|
||||
repr: '"hello"',
|
||||
type: 'string-ascii',
|
||||
},
|
||||
],
|
||||
},
|
||||
event_count: 5,
|
||||
execution_cost_read_count: 0,
|
||||
execution_cost_read_length: 0,
|
||||
execution_cost_runtime: 0,
|
||||
execution_cost_write_count: 0,
|
||||
execution_cost_write_length: 0,
|
||||
};
|
||||
|
||||
const blockTxsRows = await api.datastore.getBlockTxsRows(block.block_hash);
|
||||
expect(blockTxsRows.found).toBe(true);
|
||||
const blockTxsRowsResult = blockTxsRows.result as DbTx[];
|
||||
expect(blockTxsRowsResult[6]).toEqual({ ...contractCall, ...{ abi: contractJsonAbi } });
|
||||
|
||||
const searchResult8 = await supertest(api.server).get(
|
||||
`/extended/v1/search/0x1232000000000000000000000000000000000000000000000000000000000000?include_metadata`
|
||||
);
|
||||
expect(searchResult8.status).toBe(200);
|
||||
expect(searchResult8.type).toBe('application/json');
|
||||
expect(JSON.parse(searchResult8.text).result.metadata).toEqual(contractCallExpectedResults);
|
||||
|
||||
const blockTxResult = await db.getTxsFromBlock('0x1234', 20, 0);
|
||||
expect(blockTxResult.results[6]).toEqual({ ...contractCall, ...{ abi: contractJsonAbi } });
|
||||
});
|
||||
|
||||
test('list contract log events', async () => {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import * as supertest from 'supertest';
|
||||
import { ChainID } from '@stacks/transactions';
|
||||
import {
|
||||
bufferCVFromString,
|
||||
ChainID,
|
||||
serializeCV,
|
||||
stringAsciiCV,
|
||||
uintCV,
|
||||
} from '@stacks/transactions';
|
||||
import {
|
||||
DbBlock,
|
||||
DbTx,
|
||||
@@ -9,6 +15,8 @@ import {
|
||||
DbAssetEventTypeId,
|
||||
DbMempoolTx,
|
||||
DbMicroblockPartial,
|
||||
DbSmartContractEvent,
|
||||
DbSmartContract,
|
||||
} from '../datastore/common';
|
||||
import { startApiServer } from '../api/init';
|
||||
import { PgDataStore, cycleMigrations, runMigrations } from '../datastore/postgres-store';
|
||||
@@ -19,6 +27,7 @@ import {
|
||||
AddressStxInboundListResponse,
|
||||
AddressTransactionsListResponse,
|
||||
AddressTransactionsWithTransfersListResponse,
|
||||
ContractCallTransaction,
|
||||
MempoolTransaction,
|
||||
MempoolTransactionListResponse,
|
||||
Microblock,
|
||||
@@ -30,6 +39,7 @@ import {
|
||||
import { useWithCleanup } from './test-helpers';
|
||||
import { startEventServer } from '../event-stream/event-server';
|
||||
import * as fs from 'fs';
|
||||
import { createClarityValueArray } from '../p2p/tx';
|
||||
|
||||
describe('microblock tests', () => {
|
||||
let db: PgDataStore;
|
||||
@@ -248,6 +258,7 @@ describe('microblock tests', () => {
|
||||
async (_, api) => {
|
||||
const addr1 = 'ST28D4Q6RCQSJ6F7TEYWQDS4N1RXYEP9YBWMYSB97';
|
||||
const addr2 = 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6';
|
||||
const contractAddr = 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world';
|
||||
|
||||
const block1: DbBlock = {
|
||||
block_hash: '0x11',
|
||||
@@ -303,6 +314,49 @@ describe('microblock tests', () => {
|
||||
execution_cost_write_count: 0,
|
||||
execution_cost_write_length: 0,
|
||||
};
|
||||
const contractJsonAbi = {
|
||||
maps: [],
|
||||
functions: [
|
||||
{
|
||||
args: [
|
||||
{ type: 'uint128', name: 'amount' },
|
||||
{ type: 'string-ascii', name: 'desc' },
|
||||
],
|
||||
name: 'test-contract-fn',
|
||||
access: 'public',
|
||||
outputs: {
|
||||
type: {
|
||||
response: {
|
||||
ok: 'uint128',
|
||||
error: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
variables: [],
|
||||
fungible_tokens: [],
|
||||
non_fungible_tokens: [],
|
||||
};
|
||||
const contractLogEvent1: DbSmartContractEvent = {
|
||||
event_index: 4,
|
||||
tx_id: tx1.tx_id,
|
||||
tx_index: 0,
|
||||
block_height: block1.block_height,
|
||||
canonical: true,
|
||||
event_type: DbEventTypeId.SmartContractLog,
|
||||
contract_identifier: contractAddr,
|
||||
topic: 'some-topic',
|
||||
value: serializeCV(bufferCVFromString('some val')),
|
||||
};
|
||||
const smartContract1: DbSmartContract = {
|
||||
tx_id: tx1.tx_id,
|
||||
canonical: true,
|
||||
block_height: block1.block_height,
|
||||
contract_id: contractAddr,
|
||||
source_code: '(some-contract-src)',
|
||||
abi: JSON.stringify(contractJsonAbi),
|
||||
};
|
||||
|
||||
await db.update({
|
||||
block: block1,
|
||||
@@ -315,8 +369,8 @@ describe('microblock tests', () => {
|
||||
stxEvents: [],
|
||||
ftEvents: [],
|
||||
nftEvents: [],
|
||||
contractLogEvents: [],
|
||||
smartContracts: [],
|
||||
contractLogEvents: [contractLogEvent1],
|
||||
smartContracts: [smartContract1],
|
||||
names: [],
|
||||
namespaces: [],
|
||||
},
|
||||
@@ -381,13 +435,65 @@ describe('microblock tests', () => {
|
||||
// These properties can be determined with a db query, they are set while the db is inserting them.
|
||||
block_height: -1,
|
||||
};
|
||||
const mbTx2: DbTx = {
|
||||
tx_id: '0x03',
|
||||
tx_index: 1,
|
||||
anchor_mode: 3,
|
||||
nonce: 0,
|
||||
raw_tx: Buffer.alloc(0),
|
||||
type_id: DbTxTypeId.ContractCall,
|
||||
status: 1,
|
||||
raw_result: '0x0100000000000000000000000000000001', // u1
|
||||
canonical: true,
|
||||
post_conditions: Buffer.from([0x01, 0xf5]),
|
||||
fee_rate: 1234n,
|
||||
sponsored: false,
|
||||
sender_address: addr1,
|
||||
sponsor_address: undefined,
|
||||
origin_hash_mode: 1,
|
||||
token_transfer_amount: 50n,
|
||||
token_transfer_memo: Buffer.from('hi'),
|
||||
token_transfer_recipient_address: addr2,
|
||||
event_count: 1,
|
||||
parent_index_block_hash: block1.index_block_hash,
|
||||
parent_block_hash: block1.block_hash,
|
||||
microblock_canonical: true,
|
||||
microblock_sequence: mb1.microblock_sequence,
|
||||
microblock_hash: mb1.microblock_hash,
|
||||
parent_burn_block_time: mb1.parent_burn_block_time,
|
||||
execution_cost_read_count: 0,
|
||||
execution_cost_read_length: 0,
|
||||
execution_cost_runtime: 0,
|
||||
execution_cost_write_count: 0,
|
||||
execution_cost_write_length: 0,
|
||||
contract_call_contract_id: contractAddr,
|
||||
contract_call_function_name: 'test-contract-fn',
|
||||
contract_call_function_args: createClarityValueArray(
|
||||
uintCV(123456),
|
||||
stringAsciiCV('hello')
|
||||
),
|
||||
abi: JSON.stringify(contractJsonAbi),
|
||||
|
||||
// These properties aren't known until the next anchor block that accepts this microblock.
|
||||
index_block_hash: '',
|
||||
block_hash: '',
|
||||
burn_block_time: -1,
|
||||
|
||||
// These properties can be determined with a db query, they are set while the db is inserting them.
|
||||
block_height: -1,
|
||||
};
|
||||
|
||||
const mempoolTx1: DbMempoolTx = {
|
||||
...mbTx1,
|
||||
pruned: false,
|
||||
receipt_time: 123456789,
|
||||
};
|
||||
await db.updateMempoolTxs({ mempoolTxs: [mempoolTx1] });
|
||||
const mempoolTx2: DbMempoolTx = {
|
||||
...mbTx2,
|
||||
pruned: false,
|
||||
receipt_time: 123456789,
|
||||
};
|
||||
await db.updateMempoolTxs({ mempoolTxs: [mempoolTx1, mempoolTx2] });
|
||||
|
||||
const mbTxStxEvent1: DbStxEvent = {
|
||||
canonical: true,
|
||||
@@ -416,6 +522,17 @@ describe('microblock tests', () => {
|
||||
names: [],
|
||||
namespaces: [],
|
||||
},
|
||||
{
|
||||
tx: mbTx2,
|
||||
stxLockEvents: [],
|
||||
stxEvents: [],
|
||||
ftEvents: [],
|
||||
nftEvents: [],
|
||||
contractLogEvents: [],
|
||||
smartContracts: [],
|
||||
names: [],
|
||||
namespaces: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -434,12 +551,41 @@ describe('microblock tests', () => {
|
||||
|
||||
const txListResult2 = await supertest(api.server).get(`/extended/v1/tx?unanchored`);
|
||||
const { body: txListBody2 }: { body: TransactionResults } = txListResult2;
|
||||
expect(txListBody2.results).toHaveLength(2);
|
||||
expect(txListBody2.results[0].tx_id).toBe(mbTx1.tx_id);
|
||||
expect(txListBody2.results).toHaveLength(3);
|
||||
expect(txListBody2.results[0].tx_id).toBe(mbTx2.tx_id);
|
||||
|
||||
const txListResult3 = await supertest(api.server).get(
|
||||
`/extended/v1/microblock/unanchored/txs`
|
||||
);
|
||||
const { body: txListBody3 }: { body: TransactionResults } = txListResult3;
|
||||
expect(txListBody3.results).toHaveLength(2);
|
||||
expect(txListBody3.results[0].tx_id).toBe(mbTx2.tx_id);
|
||||
const expectedContractCallResp = {
|
||||
contract_id: 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y.hello-world',
|
||||
function_args: [
|
||||
{
|
||||
hex: '0x010000000000000000000000000001e240',
|
||||
name: 'amount',
|
||||
repr: 'u123456',
|
||||
type: 'uint',
|
||||
},
|
||||
{
|
||||
hex: '0x0d0000000568656c6c6f',
|
||||
name: 'desc',
|
||||
repr: '"hello"',
|
||||
type: 'string-ascii',
|
||||
},
|
||||
],
|
||||
function_name: 'test-contract-fn',
|
||||
function_signature:
|
||||
'(define-public (test-contract-fn (amount uint) (desc string-ascii)))',
|
||||
};
|
||||
const contractCallResults = txListBody3.results[0] as ContractCallTransaction;
|
||||
expect(contractCallResults.contract_call).toEqual(expectedContractCallResp);
|
||||
|
||||
const mempoolResult1 = await supertest(api.server).get(`/extended/v1/tx/mempool`);
|
||||
const { body: mempoolBody1 }: { body: MempoolTransactionListResponse } = mempoolResult1;
|
||||
expect(mempoolBody1.results).toHaveLength(1);
|
||||
expect(mempoolBody1.results).toHaveLength(2);
|
||||
expect(mempoolBody1.results[0].tx_id).toBe(mempoolTx1.tx_id);
|
||||
expect(mempoolBody1.results[0].tx_status).toBe('pending');
|
||||
|
||||
@@ -471,16 +617,16 @@ describe('microblock tests', () => {
|
||||
const { body: mbListBody1 }: { body: MicroblockListResponse } = mbListResult1;
|
||||
expect(mbListBody1.results).toHaveLength(1);
|
||||
expect(mbListBody1.results[0].microblock_hash).toBe(mb1.microblock_hash);
|
||||
expect(mbListBody1.results[0].txs).toHaveLength(1);
|
||||
expect(mbListBody1.results[0].txs[0]).toBe(mbTx1.tx_id);
|
||||
expect(mbListBody1.results[0].txs).toHaveLength(2);
|
||||
expect(mbListBody1.results[0].txs[0]).toBe(mbTx2.tx_id);
|
||||
|
||||
const mbResult1 = await supertest(api.server).get(
|
||||
`/extended/v1/microblock/${mb1.microblock_hash}`
|
||||
);
|
||||
const { body: mbBody1 }: { body: Microblock } = mbResult1;
|
||||
expect(mbBody1.microblock_hash).toBe(mb1.microblock_hash);
|
||||
expect(mbBody1.txs).toHaveLength(1);
|
||||
expect(mbBody1.txs[0]).toBe(mbTx1.tx_id);
|
||||
expect(mbBody1.txs).toHaveLength(2);
|
||||
expect(mbBody1.txs[0]).toBe(mbTx2.tx_id);
|
||||
|
||||
const addrTxsTransfers1 = await supertest(api.server).get(
|
||||
`/extended/v1/address/${addr2}/transactions_with_transfers`
|
||||
@@ -496,9 +642,9 @@ describe('microblock tests', () => {
|
||||
const {
|
||||
body: addrTxsTransfersBody2,
|
||||
}: { body: AddressTransactionsWithTransfersListResponse } = addrTxsTransfers2;
|
||||
expect(addrTxsTransfersBody2.results).toHaveLength(1);
|
||||
expect(addrTxsTransfersBody2.results[0].tx.tx_id).toBe(mbTx1.tx_id);
|
||||
expect(addrTxsTransfersBody2.results[0].stx_received).toBe(mbTxStxEvent1.amount.toString());
|
||||
expect(addrTxsTransfersBody2.results).toHaveLength(2);
|
||||
expect(addrTxsTransfersBody2.results[1].tx.tx_id).toBe(mbTx1.tx_id);
|
||||
expect(addrTxsTransfersBody2.results[1].stx_received).toBe(mbTxStxEvent1.amount.toString());
|
||||
|
||||
const addrTxs1 = await supertest(api.server).get(
|
||||
`/extended/v1/address/${addr2}/transactions`
|
||||
@@ -510,8 +656,8 @@ describe('microblock tests', () => {
|
||||
`/extended/v1/address/${addr2}/transactions?unanchored`
|
||||
);
|
||||
const { body: addrTxsBody2 }: { body: AddressTransactionsListResponse } = addrTxs2;
|
||||
expect(addrTxsBody2.results).toHaveLength(1);
|
||||
expect(addrTxsBody2.results[0].tx_id).toBe(mbTx1.tx_id);
|
||||
expect(addrTxsBody2.results).toHaveLength(2);
|
||||
expect(addrTxsBody2.results[0].tx_id).toBe(mbTx2.tx_id);
|
||||
|
||||
const addrBalance1 = await supertest(api.server).get(`/extended/v1/address/${addr2}/stx`);
|
||||
const { body: addrBalanceBody1 }: { body: AddressStxBalanceResponse } = addrBalance1;
|
||||
|
||||
Reference in New Issue
Block a user