Skip to content

Commit 1465737

Browse files
authored
Merge pull request #822 from blockstack/develop
Merge develop into master
2 parents d810183 + bae619d commit 1465737

File tree

2 files changed

+116
-79
lines changed

2 files changed

+116
-79
lines changed

.github/workflows/gh-pages.yml

+2-3
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,15 @@ on:
88
- master
99
types:
1010
- completed
11-
push:
12-
branches:
13-
- 'master'
1411
workflow_dispatch:
1512

1613
jobs:
1714
gh-pages:
1815
runs-on: ubuntu-latest
1916
steps:
2017
- uses: actions/checkout@v2
18+
with:
19+
fetch-depth: 0
2120

2221
- name: Use Node.js
2322
uses: actions/setup-node@v2

src/datastore/postgres-store.ts

+114-76
Original file line numberDiff line numberDiff line change
@@ -1034,8 +1034,8 @@ export class PgDataStore
10341034
const candidateTxIds = data.txs.map(d => d.tx.tx_id);
10351035
const removedTxsResult = await this.pruneMempoolTxs(client, candidateTxIds);
10361036
if (removedTxsResult.removedTxs.length > 0) {
1037-
logger.debug(
1038-
`Removed ${removedTxsResult.removedTxs.length} microblock-txs from mempool table`
1037+
logger.verbose(
1038+
`Removed ${removedTxsResult.removedTxs.length} microblock-txs from mempool table during microblock ingestion`
10391039
);
10401040
}
10411041
});
@@ -1068,7 +1068,9 @@ export class PgDataStore
10681068
const candidateTxIds = data.txs.map(d => d.tx.tx_id);
10691069
const removedTxsResult = await this.pruneMempoolTxs(client, candidateTxIds);
10701070
if (removedTxsResult.removedTxs.length > 0) {
1071-
logger.debug(`Removed ${removedTxsResult.removedTxs.length} txs from mempool table`);
1071+
logger.verbose(
1072+
`Removed ${removedTxsResult.removedTxs.length} txs from mempool table during new block ingestion`
1073+
);
10721074
}
10731075
}
10741076

@@ -1110,6 +1112,8 @@ export class PgDataStore
11101112
data.block.execution_cost_write_count = totalCost.execution_cost_write_count;
11111113
data.block.execution_cost_write_length = totalCost.execution_cost_write_length;
11121114

1115+
let batchedTxData: DataStoreTxEventData[] = data.txs;
1116+
11131117
// Find microblocks that weren't already inserted via the unconfirmed microblock event.
11141118
// This happens when a stacks-node is syncing and receives confirmed microblocks with their anchor block at the same time.
11151119
if (data.microblocks.length > 0) {
@@ -1136,44 +1140,50 @@ export class PgDataStore
11361140
const missingTxs = data.txs.filter(entry =>
11371141
missingMicroblockHashes.has(entry.tx.microblock_hash)
11381142
);
1139-
// TODO(mb): the microblock code after this line should take into account this already inserted confirmed microblock data,
1140-
// right now it performs redundant updates, blindly treating all microblock txs as unconfirmed.
11411143
await this.insertMicroblockData(client, missingMicroblocks, missingTxs);
1144+
1145+
// Clear already inserted microblock txs from the anchor-block update data to avoid duplicate inserts.
1146+
batchedTxData = batchedTxData.filter(entry => {
1147+
return !missingMicroblockHashes.has(entry.tx.microblock_hash);
1148+
});
11421149
}
11431150
}
11441151

1145-
let batchedTxData: DataStoreTxEventData[] = data.txs;
1146-
const { acceptedMicroblockTxs, orphanedMicroblockTxs } = await this.updateMicroCanonical(
1147-
client,
1148-
{
1149-
isCanonical: isCanonical,
1150-
blockHeight: data.block.block_height,
1151-
blockHash: data.block.block_hash,
1152-
indexBlockHash: data.block.index_block_hash,
1153-
parentIndexBlockHash: data.block.parent_index_block_hash,
1154-
parentMicroblockHash: data.block.parent_microblock_hash,
1155-
parentMicroblockSequence: data.block.parent_microblock_sequence,
1156-
burnBlockTime: data.block.burn_block_time,
1157-
}
1158-
);
1152+
// When processing an immediately-non-canonical block, do not orphan and possible existing microblocks
1153+
// which may be still considered canonical by the canonical block at this height.
1154+
if (isCanonical) {
1155+
const { acceptedMicroblockTxs, orphanedMicroblockTxs } = await this.updateMicroCanonical(
1156+
client,
1157+
{
1158+
isCanonical: isCanonical,
1159+
blockHeight: data.block.block_height,
1160+
blockHash: data.block.block_hash,
1161+
indexBlockHash: data.block.index_block_hash,
1162+
parentIndexBlockHash: data.block.parent_index_block_hash,
1163+
parentMicroblockHash: data.block.parent_microblock_hash,
1164+
parentMicroblockSequence: data.block.parent_microblock_sequence,
1165+
burnBlockTime: data.block.burn_block_time,
1166+
}
1167+
);
11591168

1160-
// Identify any micro-orphaned txs that also didn't make it into this anchor block, and restore them into the mempool
1161-
const orphanedAndMissingTxs = orphanedMicroblockTxs.filter(
1162-
tx => !data.txs.find(r => tx.tx_id === r.tx.tx_id)
1163-
);
1164-
const restoredMempoolTxs = await this.restoreMempoolTxs(
1165-
client,
1166-
orphanedAndMissingTxs.map(tx => tx.tx_id)
1167-
);
1168-
restoredMempoolTxs.restoredTxs.forEach(txId => {
1169-
logger.info(`Restored micro-orphaned tx to mempool ${txId}`);
1170-
});
1169+
// Identify any micro-orphaned txs that also didn't make it into this anchor block, and restore them into the mempool
1170+
const orphanedAndMissingTxs = orphanedMicroblockTxs.filter(
1171+
tx => !data.txs.find(r => tx.tx_id === r.tx.tx_id)
1172+
);
1173+
const restoredMempoolTxs = await this.restoreMempoolTxs(
1174+
client,
1175+
orphanedAndMissingTxs.map(tx => tx.tx_id)
1176+
);
1177+
restoredMempoolTxs.restoredTxs.forEach(txId => {
1178+
logger.info(`Restored micro-orphaned tx to mempool ${txId}`);
1179+
});
11711180

1172-
// Clear accepted microblock txs from the anchor-block update data to avoid duplicate inserts.
1173-
batchedTxData = data.txs.filter(entry => {
1174-
const matchingTx = acceptedMicroblockTxs.find(tx => tx.tx_id === entry.tx.tx_id);
1175-
return !matchingTx;
1176-
});
1181+
// Clear accepted microblock txs from the anchor-block update data to avoid duplicate inserts.
1182+
batchedTxData = batchedTxData.filter(entry => {
1183+
const matchingTx = acceptedMicroblockTxs.find(tx => tx.tx_id === entry.tx.tx_id);
1184+
return !matchingTx;
1185+
});
1186+
}
11771187

11781188
// TODO(mb): sanity tests on tx_index on batchedTxData, re-normalize if necessary
11791189

@@ -1267,7 +1277,12 @@ export class PgDataStore
12671277
parentMicroblockSequence: number;
12681278
burnBlockTime: number;
12691279
}
1270-
): Promise<{ acceptedMicroblockTxs: DbTx[]; orphanedMicroblockTxs: DbTx[] }> {
1280+
): Promise<{
1281+
acceptedMicroblockTxs: DbTx[];
1282+
orphanedMicroblockTxs: DbTx[];
1283+
acceptedMicroblocks: string[];
1284+
orphanedMicroblocks: string[];
1285+
}> {
12711286
// Find the parent microblock if this anchor block points to one. If not, perform a sanity check for expected block headers in this case:
12721287
// > Anchored blocks that do not have parent microblock streams will have their parent microblock header hashes set to all 0's, and the parent microblock sequence number set to 0.
12731288
let acceptedMicroblockTip: DbMicroblock | undefined;
@@ -1294,7 +1309,7 @@ export class PgDataStore
12941309
acceptedMicroblockTip = this.parseMicroblockQueryResult(microblockTipQuery.rows[0]);
12951310
}
12961311

1297-
// Identify microblocks that were either excepted or orphaned by this anchor block.
1312+
// Identify microblocks that were either accepted or orphaned by this anchor block.
12981313
const unanchoredMicroblocksAtTip = await this.findUnanchoredMicroblocksAtChainTip(
12991314
client,
13001315
blockData.parentIndexBlockHash,
@@ -1337,6 +1352,8 @@ export class PgDataStore
13371352
return {
13381353
acceptedMicroblockTxs,
13391354
orphanedMicroblockTxs,
1355+
acceptedMicroblocks,
1356+
orphanedMicroblocks,
13401357
};
13411358
}
13421359

@@ -1454,8 +1471,17 @@ export class PgDataStore
14541471
args.microblocks.map(mb => hexToBuffer(mb)),
14551472
]
14561473
);
1457-
1474+
// Any txs restored need to be pruned from the mempool
14581475
const updatedMbTxs = updatedMbTxsQuery.rows.map(r => this.parseTxQueryResult(r));
1476+
const txsToPrune = updatedMbTxs
1477+
.filter(tx => tx.canonical && tx.microblock_canonical)
1478+
.map(tx => tx.tx_id);
1479+
const removedTxsResult = await this.pruneMempoolTxs(client, txsToPrune);
1480+
if (removedTxsResult.removedTxs.length > 0) {
1481+
logger.verbose(
1482+
`Removed ${removedTxsResult.removedTxs.length} txs from mempool table during micro-reorg handling`
1483+
);
1484+
}
14591485

14601486
// Update the `index_block_hash` and `microblock_canonical` properties on all the tables containing other
14611487
// microblock-tx metadata that have been accepted or orphaned in this anchor block.
@@ -1902,29 +1928,6 @@ export class PgDataStore
19021928
canonical: boolean,
19031929
updatedEntities: UpdatedEntities
19041930
): Promise<{ txsMarkedCanonical: string[]; txsMarkedNonCanonical: string[] }> {
1905-
const microblockResult = await client.query<{ microblock_hash: Buffer }>(
1906-
`
1907-
UPDATE microblocks
1908-
SET canonical = $2
1909-
WHERE index_block_hash = $1 AND canonical != $2
1910-
RETURNING microblock_hash
1911-
`,
1912-
[indexBlockHash, canonical]
1913-
);
1914-
const microblockHashes = microblockResult.rows.map(row =>
1915-
bufferToHexPrefixString(row.microblock_hash)
1916-
);
1917-
if (canonical) {
1918-
updatedEntities.markedCanonical.microblocks += microblockResult.rowCount;
1919-
} else {
1920-
updatedEntities.markedNonCanonical.microblocks += microblockResult.rowCount;
1921-
}
1922-
for (const microblockHash of microblockHashes) {
1923-
logger.verbose(
1924-
`Marked microblock as ${canonical ? 'canonical' : 'non-canonical'}: ${microblockHash}`
1925-
);
1926-
}
1927-
19281931
const txResult = await client.query<TxQueryResult>(
19291932
`
19301933
UPDATE txs
@@ -2118,18 +2121,6 @@ export class PgDataStore
21182121
}
21192122
updatedEntities.markedCanonical.blocks++;
21202123

2121-
const restoredBlock = this.parseBlockQueryResult(restoredBlockResult.rows[0]);
2122-
await this.updateMicroCanonical(client, {
2123-
isCanonical: true,
2124-
blockHeight: restoredBlock.block_height,
2125-
blockHash: restoredBlock.block_hash,
2126-
indexBlockHash: restoredBlock.index_block_hash,
2127-
parentIndexBlockHash: restoredBlock.parent_index_block_hash,
2128-
parentMicroblockHash: restoredBlock.parent_microblock_hash,
2129-
parentMicroblockSequence: restoredBlock.parent_microblock_sequence,
2130-
burnBlockTime: restoredBlock.burn_block_time,
2131-
});
2132-
21332124
const orphanedBlockResult = await client.query<BlockQueryResult>(
21342125
`
21352126
-- orphan the now conflicting block at the same height
@@ -2140,10 +2131,14 @@ export class PgDataStore
21402131
`,
21412132
[restoredBlockResult.rows[0].block_height, indexBlockHash]
21422133
);
2134+
2135+
const microblocksOrphaned = new Set<string>();
2136+
const microblocksAccepted = new Set<string>();
2137+
21432138
if (orphanedBlockResult.rowCount > 0) {
21442139
const orphanedBlocks = orphanedBlockResult.rows.map(b => this.parseBlockQueryResult(b));
21452140
for (const orphanedBlock of orphanedBlocks) {
2146-
await this.updateMicroCanonical(client, {
2141+
const microCanonicalUpdateResult = await this.updateMicroCanonical(client, {
21472142
isCanonical: false,
21482143
blockHeight: orphanedBlock.block_height,
21492144
blockHash: orphanedBlock.block_hash,
@@ -2153,6 +2148,14 @@ export class PgDataStore
21532148
parentMicroblockSequence: orphanedBlock.parent_microblock_sequence,
21542149
burnBlockTime: orphanedBlock.burn_block_time,
21552150
});
2151+
microCanonicalUpdateResult.orphanedMicroblocks.forEach(mb => {
2152+
microblocksOrphaned.add(mb);
2153+
microblocksAccepted.delete(mb);
2154+
});
2155+
microCanonicalUpdateResult.acceptedMicroblocks.forEach(mb => {
2156+
microblocksOrphaned.delete(mb);
2157+
microblocksAccepted.add(mb);
2158+
});
21562159
}
21572160

21582161
updatedEntities.markedNonCanonical.blocks++;
@@ -2165,14 +2168,49 @@ export class PgDataStore
21652168
await this.restoreMempoolTxs(client, markNonCanonicalResult.txsMarkedNonCanonical);
21662169
}
21672170

2171+
// The canonical microblock tables _must_ be restored _after_ orphaning all other blocks at a given height,
2172+
// because there is only 1 row per microblock hash, and both the orphaned blocks at this height and the
2173+
// canonical block can be pointed to the same microblocks.
2174+
const restoredBlock = this.parseBlockQueryResult(restoredBlockResult.rows[0]);
2175+
const microCanonicalUpdateResult = await this.updateMicroCanonical(client, {
2176+
isCanonical: true,
2177+
blockHeight: restoredBlock.block_height,
2178+
blockHash: restoredBlock.block_hash,
2179+
indexBlockHash: restoredBlock.index_block_hash,
2180+
parentIndexBlockHash: restoredBlock.parent_index_block_hash,
2181+
parentMicroblockHash: restoredBlock.parent_microblock_hash,
2182+
parentMicroblockSequence: restoredBlock.parent_microblock_sequence,
2183+
burnBlockTime: restoredBlock.burn_block_time,
2184+
});
2185+
microCanonicalUpdateResult.orphanedMicroblocks.forEach(mb => {
2186+
microblocksOrphaned.add(mb);
2187+
microblocksAccepted.delete(mb);
2188+
});
2189+
microCanonicalUpdateResult.acceptedMicroblocks.forEach(mb => {
2190+
microblocksOrphaned.delete(mb);
2191+
microblocksAccepted.add(mb);
2192+
});
2193+
updatedEntities.markedCanonical.microblocks += microblocksAccepted.size;
2194+
updatedEntities.markedNonCanonical.microblocks += microblocksOrphaned.size;
2195+
2196+
microblocksOrphaned.forEach(mb => logger.verbose(`Marked microblock as non-canonical: ${mb}`));
2197+
microblocksAccepted.forEach(mb => logger.verbose(`Marked microblock as canonical: ${mb}`));
2198+
21682199
const markCanonicalResult = await this.markEntitiesCanonical(
21692200
client,
21702201
indexBlockHash,
21712202
true,
21722203
updatedEntities
21732204
);
2174-
await this.pruneMempoolTxs(client, markCanonicalResult.txsMarkedCanonical);
2175-
2205+
const removedTxsResult = await this.pruneMempoolTxs(
2206+
client,
2207+
markCanonicalResult.txsMarkedCanonical
2208+
);
2209+
if (removedTxsResult.removedTxs.length > 0) {
2210+
logger.verbose(
2211+
`Removed ${removedTxsResult.removedTxs.length} txs from mempool table during reorg handling`
2212+
);
2213+
}
21762214
const parentResult = await client.query<{ index_block_hash: Buffer }>(
21772215
`
21782216
-- check if the parent block is also orphaned

0 commit comments

Comments
 (0)