@@ -207,7 +207,7 @@ export class PgWriteStore extends PgStore {
207
207
208
208
await this . sqlWriteTransaction ( async sql => {
209
209
const chainTip = await this . getChainTip ( sql ) ;
210
- await this . handleReorg ( sql , data . block , chainTip . block_height ) ;
210
+ const reorg = await this . handleReorg ( sql , data . block , chainTip . block_height ) ;
211
211
const isCanonical = data . block . block_height > chainTip . block_height ;
212
212
if ( ! isCanonical ) {
213
213
markBlockUpdateDataAsNonCanonical ( data ) ;
@@ -292,6 +292,14 @@ export class PgWriteStore extends PgStore {
292
292
// via microblocks.
293
293
q . enqueue ( ( ) => this . updateStxBalances ( sql , data . txs , data . minerRewards ) ) ;
294
294
q . enqueue ( ( ) => this . updateFtBalances ( sql , data . txs ) ) ;
295
+ // If this block re-orgs past microblocks, though, we must discount the balances generated
296
+ // by their txs which are now also reorged. We must do this here because the block re-org
297
+ // logic is decoupled from the microblock re-org logic so previous balance updates will
298
+ // not apply.
299
+ await this . updateFtBalancesFromMicroblockReOrg ( sql , [
300
+ ...reorg . markedNonCanonical . microblockHashes ,
301
+ ...reorg . markedCanonical . microblockHashes ,
302
+ ] ) ;
295
303
}
296
304
if ( data . poxSetSigners && data . poxSetSigners . signers ) {
297
305
const poxSet = data . poxSetSigners ;
@@ -1150,6 +1158,116 @@ export class PgWriteStore extends PgStore {
1150
1158
}
1151
1159
}
1152
1160
1161
+ async updateFtBalancesFromMicroblockReOrg ( sql : PgSqlClient , microblockHashes : string [ ] ) {
1162
+ if ( microblockHashes . length === 0 ) return ;
1163
+ await sql `
1164
+ WITH updated_txs AS (
1165
+ SELECT tx_id, sender_address, nonce, sponsor_address, fee_rate, sponsored, canonical, microblock_canonical
1166
+ FROM txs
1167
+ WHERE microblock_hash IN ${ sql ( microblockHashes ) }
1168
+ ),
1169
+ affected_addresses AS (
1170
+ SELECT
1171
+ sender_address AS address,
1172
+ fee_rate AS fee_change,
1173
+ canonical,
1174
+ microblock_canonical,
1175
+ sponsored
1176
+ FROM updated_txs
1177
+ WHERE sponsored = false
1178
+ UNION ALL
1179
+ SELECT
1180
+ sponsor_address AS address,
1181
+ fee_rate AS fee_change,
1182
+ canonical,
1183
+ microblock_canonical,
1184
+ sponsored
1185
+ FROM updated_txs
1186
+ WHERE sponsored = true
1187
+ ),
1188
+ balances_update AS (
1189
+ SELECT
1190
+ a.address,
1191
+ SUM(CASE WHEN a.canonical AND a.microblock_canonical THEN -a.fee_change ELSE a.fee_change END) AS balance_change
1192
+ FROM affected_addresses a
1193
+ GROUP BY a.address
1194
+ )
1195
+ INSERT INTO ft_balances (address, token, balance)
1196
+ SELECT b.address, 'stx', b.balance_change
1197
+ FROM balances_update b
1198
+ ON CONFLICT (address, token)
1199
+ DO UPDATE
1200
+ SET balance = ft_balances.balance + EXCLUDED.balance
1201
+ RETURNING ft_balances.address
1202
+ ` ;
1203
+ await sql `
1204
+ WITH updated_events AS (
1205
+ SELECT sender, recipient, amount, asset_event_type_id, asset_identifier, canonical, microblock_canonical
1206
+ FROM ft_events
1207
+ WHERE microblock_hash IN ${ sql ( microblockHashes ) }
1208
+ ),
1209
+ event_changes AS (
1210
+ SELECT address, asset_identifier, SUM(balance_change) AS balance_change
1211
+ FROM (
1212
+ SELECT sender AS address, asset_identifier,
1213
+ SUM(CASE WHEN canonical AND microblock_canonical THEN -amount ELSE amount END) AS balance_change
1214
+ FROM updated_events
1215
+ WHERE asset_event_type_id IN (1, 3) -- Transfers and Burns affect the sender's balance
1216
+ GROUP BY sender, asset_identifier
1217
+ UNION ALL
1218
+ SELECT recipient AS address, asset_identifier,
1219
+ SUM(CASE WHEN canonical AND microblock_canonical THEN amount ELSE -amount END) AS balance_change
1220
+ FROM updated_events
1221
+ WHERE asset_event_type_id IN (1, 2) -- Transfers and Mints affect the recipient's balance
1222
+ GROUP BY recipient, asset_identifier
1223
+ ) AS subquery
1224
+ GROUP BY address, asset_identifier
1225
+ )
1226
+ INSERT INTO ft_balances (address, token, balance)
1227
+ SELECT ec.address, ec.asset_identifier, ec.balance_change
1228
+ FROM event_changes ec
1229
+ ON CONFLICT (address, token)
1230
+ DO UPDATE
1231
+ SET balance = ft_balances.balance + EXCLUDED.balance
1232
+ RETURNING ft_balances.address
1233
+ ` ;
1234
+ await sql `
1235
+ WITH updated_events AS (
1236
+ SELECT sender, recipient, amount, asset_event_type_id, canonical, microblock_canonical
1237
+ FROM stx_events
1238
+ WHERE microblock_hash IN ${ sql ( microblockHashes ) }
1239
+ ),
1240
+ event_changes AS (
1241
+ SELECT
1242
+ address,
1243
+ SUM(balance_change) AS balance_change
1244
+ FROM (
1245
+ SELECT
1246
+ sender AS address,
1247
+ SUM(CASE WHEN canonical AND microblock_canonical THEN -amount ELSE amount END) AS balance_change
1248
+ FROM updated_events
1249
+ WHERE asset_event_type_id IN (1, 3) -- Transfers and Burns affect the sender's balance
1250
+ GROUP BY sender
1251
+ UNION ALL
1252
+ SELECT
1253
+ recipient AS address,
1254
+ SUM(CASE WHEN canonical AND microblock_canonical THEN amount ELSE -amount END) AS balance_change
1255
+ FROM updated_events
1256
+ WHERE asset_event_type_id IN (1, 2) -- Transfers and Mints affect the recipient's balance
1257
+ GROUP BY recipient
1258
+ ) AS subquery
1259
+ GROUP BY address
1260
+ )
1261
+ INSERT INTO ft_balances (address, token, balance)
1262
+ SELECT ec.address, 'stx', ec.balance_change
1263
+ FROM event_changes ec
1264
+ ON CONFLICT (address, token)
1265
+ DO UPDATE
1266
+ SET balance = ft_balances.balance + EXCLUDED.balance
1267
+ RETURNING ft_balances.address
1268
+ ` ;
1269
+ }
1270
+
1153
1271
async updateStxEvents ( sql : PgSqlClient , entries : { tx : DbTx ; stxEvents : DbStxEvent [ ] } [ ] ) {
1154
1272
const values : StxEventInsertValues [ ] = [ ] ;
1155
1273
for ( const { tx, stxEvents } of entries ) {
@@ -3289,7 +3407,13 @@ export class PgWriteStore extends PgStore {
3289
3407
microblocksAccepted . add ( mb ) ;
3290
3408
} ) ;
3291
3409
updatedEntities . markedCanonical . microblocks += microblocksAccepted . size ;
3410
+ updatedEntities . markedCanonical . microblockHashes . push (
3411
+ ...microCanonicalUpdateResult . acceptedMicroblocks
3412
+ ) ;
3292
3413
updatedEntities . markedNonCanonical . microblocks += microblocksOrphaned . size ;
3414
+ updatedEntities . markedNonCanonical . microblockHashes . push (
3415
+ ...microCanonicalUpdateResult . orphanedMicroblocks
3416
+ ) ;
3293
3417
3294
3418
const markCanonicalResult = await this . markEntitiesCanonical (
3295
3419
sql ,
0 commit comments