Skip to content

Commit 412903d

Browse files
committed
rebuild from master, handle mempool, add try/catch
1 parent ac2e21d commit 412903d

File tree

11 files changed

+179
-69
lines changed

11 files changed

+179
-69
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,6 @@ src/schemas
114114

115115
# Webstorm editor files
116116
.idea
117+
118+
# OS specific
119+
.DS_Store

docs/index.d.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@ export interface TokenTransferTransaction {
205205
tx_id: string;
206206
tx_index?: number;
207207
tx_status: TransactionStatus;
208+
tx_result?: { // return both hex value and deserialized value
209+
hex: string;
210+
repr: string;
211+
};
208212
/**
209213
* Integer string (64-bit unsigned integer).
210214
*/
@@ -244,6 +248,10 @@ export interface SmartContractTransaction {
244248
tx_id: string;
245249
tx_index?: number;
246250
tx_status: TransactionStatus;
251+
tx_result?: { // return both hex value and deserialized value
252+
hex: string;
253+
repr: string;
254+
};
247255
/**
248256
* Integer string (64-bit unsigned integer).
249257
*/
@@ -280,6 +288,10 @@ export interface ContractCallTransaction {
280288
tx_id: string;
281289
tx_index?: number;
282290
tx_status: TransactionStatus;
291+
tx_result?: { // return both hex value and deserialized value
292+
hex: string;
293+
repr: string;
294+
};
283295
/**
284296
* Integer string (64-bit unsigned integer).
285297
*/
@@ -323,6 +335,10 @@ export interface PoisonMicroblockTransaction {
323335
tx_id: string;
324336
tx_index?: number;
325337
tx_status: TransactionStatus;
338+
tx_result?: { // return both hex value and deserialized value
339+
hex: string;
340+
repr: string;
341+
};
326342
/**
327343
* Integer string (64-bit unsigned integer).
328344
*/
@@ -360,6 +376,11 @@ export interface CoinbaseTransaction {
360376
tx_id: string;
361377
tx_index?: number;
362378
tx_status: TransactionStatus;
379+
tx_result?: { // return both hex value and deserialized value
380+
hex: string;
381+
repr: string;
382+
};
383+
363384
/**
364385
* Integer string (64-bit unsigned integer).
365386
*/

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"lint:eslint": "eslint . --ext .js,.jsx,.ts,.tsx -f codeframe",
1616
"lint:prettier": "prettier --check src/**/*.{ts,json}",
1717
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx -f codeframe --fix",
18+
"migrate": "node-pg-migrate -m src/migrations",
1819
"devenv:build": "docker-compose -f docker-compose.dev.postgres.yml -f docker-compose.dev.stacks-blockchain.yml -f docker-compose.dev.bitcoind.yml build --no-cache",
1920
"devenv:deploy": "docker-compose -f docker-compose.dev.postgres.yml -f docker-compose.dev.stacks-blockchain.yml -f docker-compose.dev.bitcoind.yml up",
2021
"devenv:stop": "docker-compose -f docker-compose.dev.postgres.yml -f docker-compose.dev.stacks-blockchain.yml -f docker-compose.dev.bitcoind.yml down -v -t 0",
@@ -56,7 +57,7 @@
5657
"node-fetch": "^2.6.0",
5758
"node-pg-migrate": "^4.2.3",
5859
"p-queue": "^6.3.0",
59-
"pg": "^7.18.2",
60+
"pg": "^8.2.1",
6061
"rpc-bitcoin": "^2.0.0",
6162
"smart-buffer": "^4.1.0",
6263
"strict-event-emitter-types": "^2.0.0",

src/api/controllers/db-controller.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,19 @@ export async function getTxFromDataStore(
250250
dbTxEvents = eventsQuery.results;
251251
}
252252

253+
let tx_result
254+
if ((dbTx as DbTx).raw_result !== undefined && (dbTx as DbTx).raw_result !== null) {
255+
const valueHex = (dbTx as DbTx).raw_result;
256+
tx_result = {
257+
hex: valueHex,
258+
repr: cvToString(deserializeCV(Buffer.from(valueHex.substring(2), 'hex'))),
259+
};
260+
}
261+
253262
const apiTx: Partial<Transaction> = {
254263
tx_id: dbTx.tx_id,
255264
tx_status: getTxStatusString(dbTx.status),
265+
tx_result,
256266
tx_type: getTxTypeString(dbTx.type_id),
257267

258268
fee_rate: dbTx.fee_rate.toString(10),

src/api/routes/tx.ts

Lines changed: 52 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,37 +19,43 @@ export function createTxRouter(db: DataStore): RouterWithAsync {
1919
const router = addAsync(express.Router());
2020

2121
router.getAsync('/', async (req, res) => {
22-
const limit = parseTxQueryLimit(req.query.limit ?? 96);
23-
const offset = parsePagingQueryInput(req.query.offset ?? 0);
24-
25-
const typeQuery = req.query.type;
26-
let txTypeFilter: TransactionType[];
27-
if (Array.isArray(typeQuery)) {
28-
txTypeFilter = parseTxTypeStrings(typeQuery as string[]);
29-
} else if (typeof typeQuery === 'string') {
30-
txTypeFilter = parseTxTypeStrings([typeQuery]);
31-
} else if (typeQuery) {
32-
throw new Error(`Unexpected tx type query value: ${JSON.stringify(typeQuery)}`);
33-
} else {
34-
txTypeFilter = [];
35-
}
22+
try {
23+
const limit = parseTxQueryLimit(req.query.limit ?? 96);
24+
const offset = parsePagingQueryInput(req.query.offset ?? 0);
25+
26+
const typeQuery = req.query.type;
27+
let txTypeFilter: TransactionType[];
28+
if (Array.isArray(typeQuery)) {
29+
txTypeFilter = parseTxTypeStrings(typeQuery as string[]);
30+
} else if (typeof typeQuery === 'string') {
31+
txTypeFilter = parseTxTypeStrings([typeQuery]);
32+
} else if (typeQuery) {
33+
throw new Error(`Unexpected tx type query value: ${JSON.stringify(typeQuery)}`);
34+
} else {
35+
txTypeFilter = [];
36+
}
3637

37-
const { results: txResults, total } = await db.getTxList({ offset, limit, txTypeFilter });
38+
const { results: txResults, total } = await db.getTxList({ offset, limit, txTypeFilter });
39+
40+
// TODO: fix these duplicate db queries
41+
const results = await Bluebird.mapSeries(txResults, async tx => {
42+
const txQuery = await getTxFromDataStore(tx.tx_id, db);
43+
if (!txQuery.found) {
44+
throw new Error('unexpected tx not found -- fix tx enumeration query');
45+
}
46+
return txQuery.result;
47+
});
48+
const response: TransactionResults = { limit, offset, total, results };
49+
const schemaPath = require.resolve(
50+
'@blockstack/stacks-blockchain-sidecar-types/api/transaction/get-transactions.schema.json'
51+
);
52+
await validate(schemaPath, response);
53+
res.json(response);
54+
} catch(error) {
55+
logError('error getting tx', error);
56+
res.status(500);
57+
}
3858

39-
// TODO: fix these duplicate db queries
40-
const results = await Bluebird.mapSeries(txResults, async tx => {
41-
const txQuery = await getTxFromDataStore(tx.tx_id, db);
42-
if (!txQuery.found) {
43-
throw new Error('unexpected tx not found -- fix tx enumeration query');
44-
}
45-
return txQuery.result;
46-
});
47-
const response: TransactionResults = { limit, offset, total, results };
48-
const schemaPath = require.resolve(
49-
'@blockstack/stacks-blockchain-sidecar-types/api/transaction/get-transactions.schema.json'
50-
);
51-
await validate(schemaPath, response);
52-
res.json(response);
5359
});
5460

5561
router.getAsync('/stream', async (req, res) => {
@@ -102,22 +108,26 @@ export function createTxRouter(db: DataStore): RouterWithAsync {
102108
});
103109

104110
router.getAsync('/:tx_id', async (req, res) => {
105-
const { tx_id } = req.params;
111+
try {
112+
const { tx_id } = req.params;
106113

107-
if (!has0xPrefix(tx_id)) {
108-
return res.redirect('/sidecar/v1/tx/0x' + tx_id);
109-
}
114+
if (!has0xPrefix(tx_id)) {
115+
return res.redirect('/sidecar/v1/tx/0x' + tx_id);
116+
}
110117

111-
const txQuery = await getTxFromDataStore(tx_id, db);
112-
if (!txQuery.found) {
113-
res.status(404).json({ error: `could not find transaction by ID ${tx_id}` });
114-
return;
118+
const txQuery = await getTxFromDataStore(tx_id, db);
119+
if (!txQuery.found) {
120+
res.status(404).json({ error: `could not find transaction by ID ${tx_id}` });
121+
return;
122+
}
123+
const schemaPath = require.resolve(
124+
'@blockstack/stacks-blockchain-sidecar-types/entities/transactions/transaction.schema.json'
125+
);
126+
await validate(schemaPath, txQuery.result);
127+
res.json(txQuery.result);
128+
} catch(error) {
129+
res.status(500)
115130
}
116-
const schemaPath = require.resolve(
117-
'@blockstack/stacks-blockchain-sidecar-types/entities/transactions/transaction.schema.json'
118-
);
119-
await validate(schemaPath, txQuery.result);
120-
res.json(txQuery.result);
121131
});
122132

123133
return router;

src/datastore/common.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export interface DbTx {
5454
type_id: DbTxTypeId;
5555

5656
status: DbTxStatus;
57+
raw_result: string;
5758
/** Set to `true` if entry corresponds to the canonical chain tip */
5859
canonical: boolean;
5960
post_conditions: Buffer;
@@ -361,6 +362,7 @@ export function createDbMempoolTxFromCoreMsg(msg: {
361362
txId: string;
362363
sender: string;
363364
}): DbMempoolTx {
365+
364366
const dbTx: DbMempoolTx = {
365367
tx_id: msg.txId,
366368
type_id: parseEnum(DbTxTypeId, msg.txData.payload.typeId as number),
@@ -387,6 +389,7 @@ export function createDbTxFromCoreMsg(msg: CoreNodeParsedTxMessage): DbTx {
387389
burn_block_time: msg.burn_block_time,
388390
type_id: parseEnum(DbTxTypeId, rawTx.payload.typeId as number),
389391
status: getTxDbStatus(coreTx.status),
392+
raw_result: coreTx.raw_result,
390393
fee_rate: rawTx.auth.originCondition.feeRate,
391394
sender_address: msg.sender_address,
392395
origin_hash_mode: rawTx.auth.originCondition.hashMode as number,

src/datastore/postgres-store.ts

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export async function cycleMigrations(): Promise<void> {
9797

9898
const TX_COLUMNS = `
9999
-- required columns
100-
tx_id, tx_index, index_block_hash, block_hash, block_height, burn_block_time, type_id, status,
100+
tx_id, tx_index, index_block_hash, block_hash, block_height, burn_block_time, type_id, status,
101101
canonical, post_conditions, fee_rate, sponsored, sender_address, origin_hash_mode,
102102
103103
-- token-transfer tx columns
@@ -113,12 +113,15 @@ const TX_COLUMNS = `
113113
poison_microblock_header_1, poison_microblock_header_2,
114114
115115
-- coinbase tx columns
116-
coinbase_payload
116+
coinbase_payload,
117+
118+
-- tx result
119+
raw_result
117120
`;
118121

119122
const MEMPOOL_TX_COLUMNS = `
120123
-- required columns
121-
tx_id, type_id, status,
124+
tx_id, type_id, status,
122125
post_conditions, fee_rate, sponsored, sender_address, origin_hash_mode,
123126
124127
-- token-transfer tx columns
@@ -157,6 +160,7 @@ interface MempoolTxQueryResult {
157160

158161
type_id: number;
159162
status: number;
163+
raw_result: Buffer;
160164
canonical: boolean;
161165
post_conditions: Buffer;
162166
fee_rate: string;
@@ -195,6 +199,7 @@ interface TxQueryResult {
195199
burn_block_time: number;
196200
type_id: number;
197201
status: number;
202+
raw_result: Buffer;
198203
canonical: boolean;
199204
post_conditions: Buffer;
200205
fee_rate: string;
@@ -532,9 +537,9 @@ export class PgDataStore extends (EventEmitter as { new (): DataStoreEventEmitte
532537
-- check if the parent block is also orphaned
533538
SELECT index_block_hash
534539
FROM blocks
535-
WHERE
536-
block_height = $1 AND
537-
index_block_hash = $2 AND
540+
WHERE
541+
block_height = $1 AND
542+
index_block_hash = $2 AND
538543
canonical = false
539544
`,
540545
[blockResult.rows[0].block_height - 1, blockResult.rows[0].parent_index_block_hash]
@@ -805,7 +810,7 @@ export class PgDataStore extends (EventEmitter as { new (): DataStoreEventEmitte
805810
`
806811
INSERT INTO txs(
807812
${TX_COLUMNS}
808-
) values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25)
813+
) values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26)
809814
ON CONFLICT ON CONSTRAINT unique_tx_id_index_block_hash
810815
DO NOTHING
811816
`,
@@ -835,6 +840,7 @@ export class PgDataStore extends (EventEmitter as { new (): DataStoreEventEmitte
835840
tx.poison_microblock_header_1,
836841
tx.poison_microblock_header_2,
837842
tx.coinbase_payload,
843+
hexToBuffer(tx.raw_result),
838844
]
839845
);
840846
return result.rowCount;
@@ -923,6 +929,7 @@ export class PgDataStore extends (EventEmitter as { new (): DataStoreEventEmitte
923929
burn_block_time: result.burn_block_time,
924930
type_id: result.type_id as DbTxTypeId,
925931
status: result.status,
932+
raw_result: result.raw_result ? bufferToHexPrefixString(result.raw_result) : '',
926933
canonical: result.canonical,
927934
post_conditions: result.post_conditions,
928935
fee_rate: BigInt(result.fee_rate),
@@ -1074,9 +1081,9 @@ export class PgDataStore extends (EventEmitter as { new (): DataStoreEventEmitte
10741081
amount: string;
10751082
}>(
10761083
`
1077-
SELECT
1078-
event_index, tx_id, tx_index, block_height, canonical, asset_event_type_id, sender, recipient, amount
1079-
FROM stx_events
1084+
SELECT
1085+
event_index, tx_id, tx_index, block_height, canonical, asset_event_type_id, sender, recipient, amount
1086+
FROM stx_events
10801087
WHERE tx_id = $1 AND index_block_hash = $2
10811088
`,
10821089
[txIdBuffer, blockHashBuffer]
@@ -1094,9 +1101,9 @@ export class PgDataStore extends (EventEmitter as { new (): DataStoreEventEmitte
10941101
amount: string;
10951102
}>(
10961103
`
1097-
SELECT
1098-
event_index, tx_id, tx_index, block_height, canonical, asset_event_type_id, sender, recipient, asset_identifier, amount
1099-
FROM ft_events
1104+
SELECT
1105+
event_index, tx_id, tx_index, block_height, canonical, asset_event_type_id, sender, recipient, asset_identifier, amount
1106+
FROM ft_events
11001107
WHERE tx_id = $1 AND index_block_hash = $2
11011108
`,
11021109
[txIdBuffer, blockHashBuffer]
@@ -1114,9 +1121,9 @@ export class PgDataStore extends (EventEmitter as { new (): DataStoreEventEmitte
11141121
value: Buffer;
11151122
}>(
11161123
`
1117-
SELECT
1118-
event_index, tx_id, tx_index, block_height, canonical, asset_event_type_id, sender, recipient, asset_identifier, value
1119-
FROM nft_events
1124+
SELECT
1125+
event_index, tx_id, tx_index, block_height, canonical, asset_event_type_id, sender, recipient, asset_identifier, value
1126+
FROM nft_events
11201127
WHERE tx_id = $1 AND index_block_hash = $2
11211128
`,
11221129
[txIdBuffer, blockHashBuffer]
@@ -1132,9 +1139,9 @@ export class PgDataStore extends (EventEmitter as { new (): DataStoreEventEmitte
11321139
value: Buffer;
11331140
}>(
11341141
`
1135-
SELECT
1136-
event_index, tx_id, tx_index, block_height, canonical, contract_identifier, topic, value
1137-
FROM contract_logs
1142+
SELECT
1143+
event_index, tx_id, tx_index, block_height, canonical, contract_identifier, topic, value
1144+
FROM contract_logs
11381145
WHERE tx_id = $1 AND index_block_hash = $2
11391146
`,
11401147
[txIdBuffer, blockHashBuffer]
@@ -1413,19 +1420,19 @@ export class PgDataStore extends (EventEmitter as { new (): DataStoreEventEmitte
14131420
}>(
14141421
`
14151422
SELECT * FROM (
1416-
SELECT
1423+
SELECT
14171424
'stx' as asset_type, event_index, tx_id, tx_index, block_height, canonical, asset_event_type_id, sender, recipient, '<stx>' as asset_identifier, amount::numeric(78, 0), null::bytea as value
1418-
FROM stx_events
1425+
FROM stx_events
14191426
WHERE canonical = true AND (sender = $1 OR recipient = $1)
14201427
UNION ALL
1421-
SELECT
1428+
SELECT
14221429
'ft' as asset_type, event_index, tx_id, tx_index, block_height, canonical, asset_event_type_id, sender, recipient, asset_identifier, amount, null::bytea as value
1423-
FROM ft_events
1430+
FROM ft_events
14241431
WHERE canonical = true AND (sender = $1 OR recipient = $1)
14251432
UNION ALL
1426-
SELECT
1427-
'nft' as asset_type, event_index, tx_id, tx_index, block_height, canonical, asset_event_type_id, sender, recipient, asset_identifier, null::numeric(78, 0) as amount, value
1428-
FROM nft_events
1433+
SELECT
1434+
'nft' as asset_type, event_index, tx_id, tx_index, block_height, canonical, asset_event_type_id, sender, recipient, asset_identifier, null::numeric(78, 0) as amount, value
1435+
FROM nft_events
14291436
WHERE canonical = true AND (sender = $1 OR recipient = $1)
14301437
) asset_events
14311438
ORDER BY block_height DESC, tx_index DESC, event_index DESC

0 commit comments

Comments
 (0)