Skip to content

Commit

Permalink
feat: nakamoto block timestamps (#1886)
Browse files Browse the repository at this point in the history
* feat: nakamoto block timestamps

* fix: `CONNECTION_DESTROYED` during background mempool stats update

* fix: proper shutdown of debounce background query in pg-write-store

* chore: fix tests

* chore: fix tests

* fix: use `isEventReplay` to determine block time

* chore: rename stacks_block_time to block_time
  • Loading branch information
zone117x committed Mar 15, 2024
1 parent a548731 commit f547832
Show file tree
Hide file tree
Showing 24 changed files with 363 additions and 5 deletions.
10 changes: 10 additions & 0 deletions docs/entities/blocks/block.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"canonical",
"height",
"hash",
"block_time",
"block_time_iso",
"index_block_hash",
"parent_block_hash",
"txs",
Expand Down Expand Up @@ -39,6 +41,14 @@
"type": "string",
"description": "Hash representing the block"
},
"block_time": {
"type": "number",
"description": "Unix timestamp (in seconds) indicating when this block was mined."
},
"block_time_iso": {
"type": "string",
"description": "An ISO 8601 (YYYY-MM-DDTHH:mm:ss.sssZ) indicating when this block was mined."
},
"index_block_hash": {
"type": "string",
"description": "The only hash that can uniquely identify an anchored block or an unconfirmed state trie"
Expand Down
10 changes: 10 additions & 0 deletions docs/entities/transactions/abstract-transaction.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"tx_index",
"block_hash",
"block_height",
"block_time",
"block_time_iso",
"burn_block_time",
"burn_block_time_iso",
"parent_burn_block_time",
Expand Down Expand Up @@ -41,6 +43,14 @@
"type": "integer",
"description": "Height of the block this transactions was associated with"
},
"block_time": {
"type": "number",
"description": "Unix timestamp (in seconds) indicating when this block was mined."
},
"block_time_iso": {
"type": "string",
"description": "An ISO 8601 (YYYY-MM-DDTHH:mm:ss.sssZ) indicating when this block was mined."
},
"burn_block_time": {
"type": "integer",
"description": "Unix timestamp (in seconds) indicating when this block was mined"
Expand Down
16 changes: 16 additions & 0 deletions docs/generated.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,14 @@ export type AbstractTransaction = BaseTransaction & {
* Height of the block this transactions was associated with
*/
block_height: number;
/**
* Unix timestamp (in seconds) indicating when this block was mined.
*/
block_time: number;
/**
* An ISO 8601 (YYYY-MM-DDTHH:mm:ss.sssZ) indicating when this block was mined.
*/
block_time_iso: string;
/**
* Unix timestamp (in seconds) indicating when this block was mined
*/
Expand Down Expand Up @@ -1179,6 +1187,14 @@ export interface Block {
* Hash representing the block
*/
hash: string;
/**
* Unix timestamp (in seconds) indicating when this block was mined.
*/
block_time: number;
/**
* An ISO 8601 (YYYY-MM-DDTHH:mm:ss.sssZ) indicating when this block was mined.
*/
block_time_iso: string;
/**
* The only hash that can uniquely identify an anchored block or an unconfirmed state trie
*/
Expand Down
21 changes: 21 additions & 0 deletions migrations/1710443563029_stacks_block_time.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/* eslint-disable camelcase */

/** @param { import("node-pg-migrate").MigrationBuilder } pgm */
exports.up = pgm => {
pgm.addColumn('txs', {
block_time: {
type: 'integer',
notNull: true,
default: '0'
}
});

pgm.addColumn('blocks', {
block_time: {
type: 'integer',
notNull: true,
default: '0'
}
});

};
11 changes: 11 additions & 0 deletions src/api/controllers/db-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,11 @@ function parseDbBlock(
canonical: dbBlock.canonical,
height: dbBlock.block_height,
hash: dbBlock.block_hash,
block_time: dbBlock.block_time > 0 ? dbBlock.block_time : dbBlock.burn_block_time,
block_time_iso:
dbBlock.block_time > 0
? unixEpochToIso(dbBlock.block_time)
: unixEpochToIso(dbBlock.burn_block_time),
index_block_hash: dbBlock.index_block_hash,
parent_block_hash: dbBlock.parent_block_hash,
burn_block_time: dbBlock.burn_block_time,
Expand Down Expand Up @@ -1039,6 +1044,12 @@ function parseDbAbstractTx(dbTx: DbTx, baseTx: BaseTransaction): AbstractTransac
block_hash: dbTx.block_hash,
parent_block_hash: dbTx.parent_block_hash,
block_height: dbTx.block_height,
block_time: dbTx.block_time || dbTx.burn_block_time,
block_time_iso: dbTx.block_time
? unixEpochToIso(dbTx.block_time)
: dbTx.burn_block_time > 0
? unixEpochToIso(dbTx.burn_block_time)
: '',
burn_block_time: dbTx.burn_block_time,
burn_block_time_iso: dbTx.burn_block_time > 0 ? unixEpochToIso(dbTx.burn_block_time) : '',
parent_burn_block_time: dbTx.parent_burn_block_time,
Expand Down
5 changes: 5 additions & 0 deletions src/datastore/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface DbBlock {
execution_cost_write_count: number;
execution_cost_write_length: number;
tx_count: number;
block_time: number;
}

/** An interface representing the microblock data that can be constructed _only_ from the /new_microblocks payload */
Expand Down Expand Up @@ -170,6 +171,7 @@ export interface DbTx extends BaseTx {
block_height: number;
burn_block_time: number;
parent_burn_block_time: number;
block_time: number;

tx_index: number;

Expand Down Expand Up @@ -795,6 +797,7 @@ export interface BlockQueryResult {
parent_microblock_hash: string;
parent_microblock_sequence: number;
block_height: number;
block_time: number;
burn_block_time: number;
burn_block_hash: string;
burn_block_height: number;
Expand Down Expand Up @@ -894,6 +897,7 @@ export interface TxQueryResult {
index_block_hash: string;
parent_index_block_hash: string;
block_hash: string;
block_time: number;
parent_block_hash: string;
block_height: number;
burn_block_time: number;
Expand Down Expand Up @@ -1044,6 +1048,7 @@ export interface TxInsertValues {
block_hash: PgBytea;
parent_block_hash: PgBytea;
block_height: number;
block_time: number;
burn_block_time: number;
parent_burn_block_time: number;
type_id: number;
Expand Down
6 changes: 6 additions & 0 deletions src/datastore/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export const TX_COLUMNS = [
'block_hash',
'parent_block_hash',
'block_height',
'block_time',
'burn_block_time',
'parent_burn_block_time',
'type_id',
Expand Down Expand Up @@ -170,6 +171,7 @@ export const BLOCK_COLUMNS = [
'parent_microblock_hash',
'parent_microblock_sequence',
'block_height',
'block_time',
'burn_block_time',
'burn_block_hash',
'burn_block_height',
Expand Down Expand Up @@ -214,6 +216,7 @@ export const TX_METADATA_TABLES = [
'names',
'namespaces',
'subdomains',
// TODO: add pox_set table here
] as const;

export const POX_SYNTHETIC_EVENT_COLUMNS = [
Expand Down Expand Up @@ -333,6 +336,7 @@ export function parseTxQueryResult(result: ContractTxQueryResult): DbTx {
index_block_hash: result.index_block_hash,
parent_index_block_hash: result.parent_index_block_hash,
block_hash: result.block_hash,
block_time: result.block_time || result.burn_block_time,
parent_block_hash: result.parent_block_hash,
block_height: result.block_height,
burn_block_time: result.burn_block_time,
Expand Down Expand Up @@ -450,6 +454,7 @@ export function parseBlockQueryResult(row: BlockQueryResult): DbBlock {
parent_microblock_hash: row.parent_microblock_hash,
parent_microblock_sequence: row.parent_microblock_sequence,
block_height: row.block_height,
block_time: row.block_time || row.burn_block_time,
burn_block_time: row.burn_block_time,
burn_block_hash: row.burn_block_hash,
burn_block_height: row.burn_block_height,
Expand Down Expand Up @@ -1128,6 +1133,7 @@ export function createDbTxFromCoreMsg(msg: CoreNodeParsedTxMessage): DbTxRaw {
block_height: msg.block_height,
burn_block_time: msg.burn_block_time,
parent_burn_block_time: msg.parent_burn_block_time,
block_time: msg.block_time,
type_id: parseEnum(DbTxTypeId, parsedTx.payload.type_id as number),
anchor_mode: parseEnum(DbTxAnchorMode, parsedTx.anchor_mode as number),
status: getTxDbStatus(coreTx.status),
Expand Down
27 changes: 24 additions & 3 deletions src/datastore/pg-write-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1593,6 +1593,7 @@ export class PgWriteStore extends PgStore {
block_hash: tx.block_hash,
parent_block_hash: tx.parent_block_hash,
block_height: tx.block_height,
block_time: tx.block_time,
burn_block_time: tx.burn_block_time,
parent_burn_block_time: tx.parent_burn_block_time,
type_id: tx.type_id,
Expand Down Expand Up @@ -1790,10 +1791,22 @@ export class PgWriteStore extends PgStore {
this._debounceMempoolStat.running = true;
this._debounceMempoolStat.triggeredAt = null;
try {
const mempoolStats = await this.getMempoolStatsInternal({ sql: this.sql });
const mempoolStats = await this.sqlTransaction(async sql => {
return await this.getMempoolStatsInternal({ sql });
});
this.eventEmitter.emit('mempoolStatsUpdate', mempoolStats);
} catch (e) {
logger.error(e, `failed to run mempool stats update`);
} catch (e: unknown) {
const connectionError = e as Error & { code: string };
if (
connectionError instanceof Error &&
['CONNECTION_ENDED', 'CONNECTION_DESTROYED', 'CONNECTION_CLOSED'].includes(
connectionError.code
)
) {
logger.info(`Skipping mempool stats query because ${connectionError.code}`);
} else {
logger.error(e, `failed to run mempool stats update`);
}
} finally {
this._debounceMempoolStat.running = false;
this._debounceMempoolStat.debounce = null;
Expand Down Expand Up @@ -2969,6 +2982,7 @@ export class PgWriteStore extends PgStore {
block_hash: tx.block_hash,
parent_block_hash: tx.parent_block_hash,
block_height: tx.block_height,
block_time: tx.block_time,
burn_block_time: tx.burn_block_time,
parent_burn_block_time: tx.parent_burn_block_time,
type_id: tx.type_id,
Expand Down Expand Up @@ -3161,4 +3175,11 @@ export class PgWriteStore extends PgStore {
}
}
}

async close(args?: { timeout?: number }): Promise<void> {
if (this._debounceMempoolStat.debounce) {
clearTimeout(this._debounceMempoolStat.debounce);
}
await super.close(args);
}
}
2 changes: 2 additions & 0 deletions src/event-stream/core-node-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ export interface CoreNodeBlockMessage {
pox_v1_unlock_height?: number;
pox_v2_unlock_height?: number;
pox_v3_unlock_height?: number;
block_time: number;
}

export interface CoreNodeParsedTxMessage {
Expand All @@ -274,6 +275,7 @@ export interface CoreNodeParsedTxMessage {
burn_block_time: number;
parent_burn_block_time: number;
parent_burn_block_hash: string;
block_time: number;
}

export interface CoreNodeBurnBlockMessage {
Expand Down
18 changes: 17 additions & 1 deletion src/event-stream/event-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ async function handleMicroblockMessage(
time: msg.burn_block_timestamp,
},
});
const stacksBlockReceiptDate = Math.round(Date.now() / 1000);
const parsedTxs: CoreNodeParsedTxMessage[] = [];
msg.transactions.forEach(tx => {
const blockData: CoreNodeMsgBlockData = {
Expand All @@ -197,6 +198,7 @@ async function handleMicroblockMessage(
burn_block_height: -1,
index_block_hash: '',
block_hash: '',
block_time: stacksBlockReceiptDate,

// These properties can be determined with a db query, they are set while the db is inserting them.
block_height: -1,
Expand All @@ -218,6 +220,7 @@ async function handleMicroblockMessage(
{
block_height: -1, // TODO: fill during initial db insert
index_block_hash: '',
block_time: stacksBlockReceiptDate,
},
chainId
),
Expand All @@ -232,10 +235,20 @@ async function handleBlockMessage(
): Promise<void> {
const ingestionTimer = stopwatch();
const counts = newCoreNoreBlockEventCounts();
// If running in IBD mode, we use the parent burn block timestamp as the receipt date,
// otherwise, use the current timestamp.
const parsedTxs: CoreNodeParsedTxMessage[] = [];
const blockData: CoreNodeMsgBlockData = {
...msg,
};
if (!blockData.block_time) {
// TODO: if the core node can give use the stacks-block count/index for the current tenure/burn_block then we should
// increment this by that value so that timestampts increase monotonically.
const stacksBlockReceiptDate = db.isEventReplay
? msg.parent_burn_block_timestamp
: Math.round(Date.now() / 1000);
blockData.block_time = stacksBlockReceiptDate;
}
msg.transactions.forEach(item => {
const parsedTx = parseMessageTransaction(chainId, item, blockData, msg.events);
if (parsedTx) {
Expand Down Expand Up @@ -296,6 +309,7 @@ async function handleBlockMessage(
execution_cost_write_count: 0,
execution_cost_write_length: 0,
tx_count: msg.transactions.length,
block_time: msg.block_time,
};

logger.debug(`Received block ${msg.block_hash} (${msg.block_height}) from node`, dbBlock);
Expand Down Expand Up @@ -349,7 +363,7 @@ async function handleBlockMessage(
block: dbBlock,
microblocks: dbMicroblocks,
minerRewards: dbMinerRewards,
txs: parseDataStoreTxEventData(parsedTxs, msg.events, msg, chainId),
txs: parseDataStoreTxEventData(parsedTxs, msg.events, dbBlock, chainId),
pox_v1_unlock_height: msg.pox_v1_unlock_height,
pox_v2_unlock_height: msg.pox_v2_unlock_height,
pox_v3_unlock_height: msg.pox_v3_unlock_height,
Expand All @@ -369,6 +383,7 @@ function parseDataStoreTxEventData(
blockData: {
block_height: number;
index_block_hash: string;
block_time: number;
},
chainId: ChainID
): DataStoreTxEventData[] {
Expand Down Expand Up @@ -1091,6 +1106,7 @@ export function parseNewBlockMessage(chainId: ChainID, msg: CoreNodeBlockMessage
burn_block_time: msg.burn_block_time,
burn_block_hash: msg.burn_block_hash,
burn_block_height: msg.burn_block_height,
block_time: msg.block_time,
miner_txid: msg.miner_txid,
execution_cost_read_count: totalCost.execution_cost_read_count,
execution_cost_read_length: totalCost.execution_cost_read_length,
Expand Down
2 changes: 2 additions & 0 deletions src/event-stream/reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@ export interface CoreNodeMsgBlockData {
block_height: number;
burn_block_time: number;
burn_block_height: number;
block_time: number;
}

export function parseMicroblocksFromTxs(args: {
Expand Down Expand Up @@ -744,6 +745,7 @@ export function parseMessageTransaction(
raw_tx: coreTx.raw_tx,
parsed_tx: rawTx,
block_hash: blockData.block_hash,
block_time: blockData.block_time,
index_block_hash: blockData.index_block_hash,
parent_index_block_hash: blockData.parent_index_block_hash,
parent_block_hash: blockData.parent_block_hash,
Expand Down

0 comments on commit f547832

Please sign in to comment.