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:
Rafael Cárdenas
2021-10-15 11:14:34 -05:00
committed by GitHub
parent 70464f4c82
commit 26e50fd1b0
5 changed files with 48 additions and 23 deletions

19
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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;
})()

View File

@@ -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 }));
},
};

View File

@@ -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 = {