mirror of
https://github.com/alexgo-io/stacks-blockchain-api.git
synced 2026-01-12 22:43:34 +08:00
feat: add broadcast/confirmed tx logs
* feat: add broadcast/confirmed tx logs * chore: add comment and return type * fix: setting /v2 proxy header on response object * fix: use structured logging (json metadata) for the tx broadcast and tx confirm logs * chore: update 'http-proxy-middleware' lib * fix: http proxy lib types after upgrade * fix: use URL pathname for checking `/v2/transactions` Co-authored-by: Matthew Little <zone117x@gmail.com>
This commit is contained in:
19
package-lock.json
generated
19
package-lock.json
generated
@@ -2435,18 +2435,11 @@
|
||||
}
|
||||
},
|
||||
"@types/http-proxy": {
|
||||
"version": "1.17.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.5.tgz",
|
||||
"integrity": "sha512-GNkDE7bTv6Sf8JbV2GksknKOsk7OznNYHSdrtvPJXO0qJ9odZig6IZKUi5RFGi6d1bf6dgIAe4uXi3DBc7069Q==",
|
||||
"version": "1.17.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.7.tgz",
|
||||
"integrity": "sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz",
|
||||
"integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/istanbul-lib-coverage": {
|
||||
@@ -8913,9 +8906,9 @@
|
||||
}
|
||||
},
|
||||
"http-proxy-middleware": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz",
|
||||
"integrity": "sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.1.tgz",
|
||||
"integrity": "sha512-cfaXRVoZxSed/BmkA7SwBVNI9Kj7HFltaE5rqYOub5kWzWZ+gofV2koVN1j2rMW7pEfSSlCHGJ31xmuyFyfLOg==",
|
||||
"requires": {
|
||||
"@types/http-proxy": "^1.17.5",
|
||||
"http-proxy": "^1.18.1",
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
"express-list-endpoints": "^5.0.0",
|
||||
"express-winston": "^4.1.0",
|
||||
"getopts": "^2.3.0",
|
||||
"http-proxy-middleware": "^1.0.6",
|
||||
"http-proxy-middleware": "^2.0.1",
|
||||
"jsonc-parser": "^3.0.0",
|
||||
"jsonrpc-lite": "^2.1.0",
|
||||
"micro-base58": "^0.5.0",
|
||||
|
||||
@@ -176,7 +176,7 @@ export async function startApiServer(opts: {
|
||||
const router = addAsync(express.Router());
|
||||
router.use(cors());
|
||||
router.use('/prices', createBnsPriceRouter(datastore, chainId));
|
||||
router.use('/', createCoreNodeRpcProxyRouter());
|
||||
router.use('/', createCoreNodeRpcProxyRouter(datastore));
|
||||
|
||||
return router;
|
||||
})()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as express from 'express';
|
||||
import * as cors from 'cors';
|
||||
import { createProxyMiddleware, Options } from 'http-proxy-middleware';
|
||||
import { createProxyMiddleware, Options, responseInterceptor } from 'http-proxy-middleware';
|
||||
import { logError, logger, parsePort, pipelineAsync, REPO_DIR } from '../../helpers';
|
||||
import { Agent } from 'http';
|
||||
import * as fs from 'fs';
|
||||
@@ -9,6 +9,7 @@ import { addAsync } from '@awaitjs/express';
|
||||
import * as chokidar from 'chokidar';
|
||||
import * as jsoncParser from 'jsonc-parser';
|
||||
import fetch, { RequestInit } from 'node-fetch';
|
||||
import { DataStore } from '../../datastore/common';
|
||||
|
||||
export function GetStacksNodeProxyEndpoint() {
|
||||
// Use STACKS_CORE_PROXY env vars if available, otherwise fallback to `STACKS_CORE_RPC
|
||||
@@ -19,7 +20,7 @@ export function GetStacksNodeProxyEndpoint() {
|
||||
return `${proxyHost}:${proxyPort}`;
|
||||
}
|
||||
|
||||
export function createCoreNodeRpcProxyRouter(): express.Router {
|
||||
export function createCoreNodeRpcProxyRouter(db: DataStore): express.Router {
|
||||
const router = addAsync(express.Router());
|
||||
router.use(cors());
|
||||
|
||||
@@ -143,6 +144,22 @@ export function createCoreNodeRpcProxyRouter(): express.Router {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a transaction broadcast event alongside the current block height.
|
||||
*/
|
||||
async function logTxBroadcast(response: string): Promise<void> {
|
||||
const blockHeightQuery = await db.getCurrentBlockHeight();
|
||||
if (!blockHeightQuery.found) {
|
||||
return;
|
||||
}
|
||||
const blockHeight = blockHeightQuery.result;
|
||||
const txId = JSON.parse(response);
|
||||
logger.info('Transaction broadcasted', {
|
||||
txid: `0x${txId}`,
|
||||
first_broadcast_at_stacks_height: blockHeight,
|
||||
});
|
||||
}
|
||||
|
||||
router.postAsync('/transactions', async (req, res, next) => {
|
||||
const extraEndpoints = await getExtraTxPostEndpoints();
|
||||
if (!extraEndpoints) {
|
||||
@@ -211,18 +228,28 @@ export function createCoreNodeRpcProxyRouter(): express.Router {
|
||||
agent: httpAgent,
|
||||
target: `http://${stacksNodeRpcEndpoint}`,
|
||||
changeOrigin: true,
|
||||
onProxyRes: (proxyRes, req, res) => {
|
||||
const header = getCacheControlHeader(res.statusCode, req.url);
|
||||
if (header) {
|
||||
proxyRes.headers['Cache-Control'] = header;
|
||||
selfHandleResponse: true,
|
||||
onProxyRes: responseInterceptor(async (responseBuffer, proxyRes, req, res) => {
|
||||
if (req.url !== undefined) {
|
||||
const header = getCacheControlHeader(res.statusCode, req.url);
|
||||
if (header) {
|
||||
res.setHeader('Cache-Control', header);
|
||||
}
|
||||
const url = new URL(req.url, `http://${req.headers.host}`);
|
||||
if (url.pathname === '/v2/transactions' && res.statusCode === 200) {
|
||||
await logTxBroadcast(responseBuffer.toString());
|
||||
}
|
||||
}
|
||||
},
|
||||
return responseBuffer;
|
||||
}),
|
||||
onError: (error, req, res) => {
|
||||
const msg =
|
||||
(error as any).code === 'ECONNREFUSED'
|
||||
? 'core node unresponsive'
|
||||
: 'cannot connect to core node';
|
||||
res.status(502).json({ message: msg, error: error });
|
||||
res
|
||||
.writeHead(502, { 'Content-Type': 'application/json' })
|
||||
.end(JSON.stringify({ message: msg, error: error }));
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -305,6 +305,11 @@ async function handleBlockMessage(
|
||||
|
||||
parsedTxs.forEach(tx => {
|
||||
logger.verbose(`Received anchor block mined tx: ${tx.core_tx.txid}`);
|
||||
logger.info('Transaction confirmed', {
|
||||
txid: tx.core_tx.txid,
|
||||
in_microblock: tx.microblock_hash != '',
|
||||
stacks_height: dbBlock.block_height,
|
||||
});
|
||||
});
|
||||
|
||||
const dbData: DataStoreBlockUpdateData = {
|
||||
|
||||
Reference in New Issue
Block a user