From 37bdb83badf9b716f38496fcc8301b4853e4d122 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Fri, 1 Dec 2023 11:28:40 +0100 Subject: [PATCH 01/23] feat: handle pox-3 force unlocks --- docs/api/info/get-status.schema.json | 4 ++++ docs/generated.d.ts | 1 + migrations/1701368149776_nakamoto-txs.js | 8 ++++++++ src/api/routes/status.ts | 1 + src/datastore/common.ts | 1 + src/datastore/pg-store.ts | 19 +++++++++++++------ src/datastore/pg-write-store.ts | 8 ++++++++ src/event-stream/core-node-message.ts | 1 + src/event-stream/event-server.ts | 1 + 9 files changed, 38 insertions(+), 6 deletions(-) diff --git a/docs/api/info/get-status.schema.json b/docs/api/info/get-status.schema.json index f629d0afd0..24dec15e1e 100644 --- a/docs/api/info/get-status.schema.json +++ b/docs/api/info/get-status.schema.json @@ -23,6 +23,10 @@ "type": "integer", "nullable": true }, + "pox_v3_unlock_height": { + "type": "integer", + "nullable": true + }, "chain_tip": { "$ref": "../../entities/info/chain-tip.schema.json" } diff --git a/docs/generated.d.ts b/docs/generated.d.ts index d1c42c1e17..11cdd6e248 100644 --- a/docs/generated.d.ts +++ b/docs/generated.d.ts @@ -1669,6 +1669,7 @@ export interface ServerStatusResponse { status: string; pox_v1_unlock_height?: number; pox_v2_unlock_height?: number; + pox_v3_unlock_height?: number; chain_tip?: ChainTip; } /** diff --git a/migrations/1701368149776_nakamoto-txs.js b/migrations/1701368149776_nakamoto-txs.js index 626757c032..27ec61ddbd 100644 --- a/migrations/1701368149776_nakamoto-txs.js +++ b/migrations/1701368149776_nakamoto-txs.js @@ -1,5 +1,13 @@ /** @param { import("node-pg-migrate").MigrationBuilder } pgm */ exports.up = pgm => { + pgm.addColumn('pox_state', { + pox_v3_unlock_height: { + type: 'bigint', + notNull: true, + default: 0, + }, + }); + pgm.addColumns('txs', { // `nakamoto-coinbase` tx types coinbase_vrf_proof: 'bytea', diff --git a/src/api/routes/status.ts b/src/api/routes/status.ts index 328cb327ed..a56dd3e788 100644 --- a/src/api/routes/status.ts +++ b/src/api/routes/status.ts @@ -17,6 +17,7 @@ export function createStatusRouter(db: PgStore): express.Router { if (poxForceUnlockHeights.found) { response.pox_v1_unlock_height = poxForceUnlockHeights.result.pox1UnlockHeight as number; response.pox_v2_unlock_height = poxForceUnlockHeights.result.pox2UnlockHeight as number; + response.pox_v3_unlock_height = poxForceUnlockHeights.result.pox3UnlockHeight as number; } const chainTip = await db.getChainTip(); if (chainTip.block_height > 0) { diff --git a/src/datastore/common.ts b/src/datastore/common.ts index f21a6219cd..88083cccd5 100644 --- a/src/datastore/common.ts +++ b/src/datastore/common.ts @@ -569,6 +569,7 @@ export interface DataStoreBlockUpdateData { txs: DataStoreTxEventData[]; pox_v1_unlock_height?: number; pox_v2_unlock_height?: number; + pox_v3_unlock_height?: number; } export interface DataStoreMicroblockUpdateData { diff --git a/src/datastore/pg-store.ts b/src/datastore/pg-store.ts index f5f888c640..1bc2e5127e 100644 --- a/src/datastore/pg-store.ts +++ b/src/datastore/pg-store.ts @@ -287,11 +287,17 @@ export class PgStore extends BasePgStore { }); } - async getPoxForcedUnlockHeightsInternal( - sql: PgSqlClient - ): Promise> { - const query = await sql<{ pox_v1_unlock_height: string; pox_v2_unlock_height: string }[]>` - SELECT pox_v1_unlock_height, pox_v2_unlock_height + async getPoxForcedUnlockHeightsInternal(sql: PgSqlClient): Promise< + FoundOrNot<{ + pox1UnlockHeight: number | null; + pox2UnlockHeight: number | null; + pox3UnlockHeight: number | null; + }> + > { + const query = await sql< + { pox_v1_unlock_height: string; pox_v2_unlock_height: string; pox_v3_unlock_height: string }[] + >` + SELECT pox_v1_unlock_height, pox_v2_unlock_height, pox_v3_unlock_height FROM pox_state LIMIt 1 `; @@ -300,10 +306,11 @@ export class PgStore extends BasePgStore { } const pox1UnlockHeight = parseInt(query[0].pox_v1_unlock_height) || null; const pox2UnlockHeight = parseInt(query[0].pox_v2_unlock_height) || null; + const pox3UnlockHeight = parseInt(query[0].pox_v3_unlock_height) || null; if (pox2UnlockHeight === 0) { return { found: false }; } - return { found: true, result: { pox1UnlockHeight, pox2UnlockHeight } }; + return { found: true, result: { pox1UnlockHeight, pox2UnlockHeight, pox3UnlockHeight } }; } async getPoxForceUnlockHeights() { diff --git a/src/datastore/pg-write-store.ts b/src/datastore/pg-write-store.ts index 6afd6d7f99..ee60fbba76 100644 --- a/src/datastore/pg-write-store.ts +++ b/src/datastore/pg-write-store.ts @@ -325,6 +325,14 @@ export class PgWriteStore extends PgStore { WHERE pox_v2_unlock_height != ${data.pox_v2_unlock_height} `; } + if (isCanonical && data.pox_v3_unlock_height !== undefined) { + // update the pox_state.pox_v3_unlock_height singleton + await sql` + UPDATE pox_state + SET pox_v3_unlock_height = ${data.pox_v3_unlock_height} + WHERE pox_v3_unlock_height != ${data.pox_v3_unlock_height} + `; + } // When receiving first block, check if "block 0" boot data was received, // if so, update their properties to correspond to "block 1", since we treat diff --git a/src/event-stream/core-node-message.ts b/src/event-stream/core-node-message.ts index e23e569968..dd5927a0bf 100644 --- a/src/event-stream/core-node-message.ts +++ b/src/event-stream/core-node-message.ts @@ -254,6 +254,7 @@ export interface CoreNodeBlockMessage { }[]; pox_v1_unlock_height?: number; pox_v2_unlock_height?: number; + pox_v3_unlock_height?: number; } export interface CoreNodeParsedTxMessage { diff --git a/src/event-stream/event-server.ts b/src/event-stream/event-server.ts index c0ec3c86c0..8fae008295 100644 --- a/src/event-stream/event-server.ts +++ b/src/event-stream/event-server.ts @@ -321,6 +321,7 @@ async function handleBlockMessage( txs: parseDataStoreTxEventData(parsedTxs, msg.events, msg, 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, }; await db.update(dbData); From 0359fe754bb8f4de7deefe947333b9afb03ea4e3 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Fri, 8 Dec 2023 11:01:02 +0100 Subject: [PATCH 02/23] chore: todo note --- src/datastore/pg-store.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/datastore/pg-store.ts b/src/datastore/pg-store.ts index 1bc2e5127e..9167e7e0a6 100644 --- a/src/datastore/pg-store.ts +++ b/src/datastore/pg-store.ts @@ -2276,6 +2276,8 @@ export class PgStore extends BasePgStore { } } + // TODO: implement includePox3State + // == PoX-3 ================================================================ // Assuming includePox3State = true; since there is no unlock height for pox3 (yet) From aad6cca3aa34fd1d951fbabe332022259b82cf4d Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sat, 9 Dec 2023 13:57:39 +0100 Subject: [PATCH 03/23] feat: handle pox-3 force unlocks, pox-4 events, and revoke-delegate-stx event --- src/api/controllers/db-controller.ts | 40 +- src/api/rosetta-constants.ts | 15 - src/api/routes/pox2.ts | 24 +- src/api/routes/pox3.ts | 24 +- src/datastore/common.ts | 100 ++--- src/datastore/helpers.ts | 122 +++--- src/datastore/pg-store.ts | 366 +++++++++++------- src/datastore/pg-write-store.ts | 164 ++------ .../importers/new-block-importer.ts | 14 +- src/event-stream/event-server.ts | 33 +- src/event-stream/pox2-event-parsing.ts | 183 +++++---- src/event-stream/reader.ts | 25 +- src/pox-helpers.ts | 45 ++- src/rosetta/rosetta-helpers.ts | 17 +- src/test-utils/test-builders.ts | 1 + 15 files changed, 624 insertions(+), 549 deletions(-) diff --git a/src/api/controllers/db-controller.ts b/src/api/controllers/db-controller.ts index ac4ac4711f..53d99f0b83 100644 --- a/src/api/controllers/db-controller.ts +++ b/src/api/controllers/db-controller.ts @@ -59,14 +59,13 @@ import { BaseTx, DbMinerReward, StxUnlockEvent, - DbPox2Event, - DbPox3Event, + DbPoxSyntheticEvent, } from '../../datastore/common'; import { unwrapOptional, FoundOrNot, unixEpochToIso, EMPTY_HASH_256, ChainID } from '../../helpers'; import { serializePostCondition, serializePostConditionMode } from '../serializers/post-conditions'; import { getOperations, parseTransactionMemo } from '../../rosetta/rosetta-helpers'; import { PgStore } from '../../datastore/pg-store'; -import { Pox2EventName } from '../../pox-helpers'; +import { SyntheticPoxEventName } from '../../pox-helpers'; import { logger } from '../../logger'; export function parseTxTypeStrings(values: string[]): TransactionType[] { @@ -193,7 +192,7 @@ export function getAssetEventTypeString( } } -export function parsePox2Event(poxEvent: DbPox2Event | DbPox3Event) { +export function parsePoxSyntheticEvent(poxEvent: DbPoxSyntheticEvent) { const baseInfo = { block_height: poxEvent.block_height, tx_id: poxEvent.tx_id, @@ -208,7 +207,7 @@ export function parsePox2Event(poxEvent: DbPox2Event | DbPox3Event) { name: poxEvent.name, }; switch (poxEvent.name) { - case Pox2EventName.HandleUnlock: { + case SyntheticPoxEventName.HandleUnlock: { return { ...baseInfo, data: { @@ -217,7 +216,7 @@ export function parsePox2Event(poxEvent: DbPox2Event | DbPox3Event) { }, }; } - case Pox2EventName.StackStx: { + case SyntheticPoxEventName.StackStx: { return { ...baseInfo, data: { @@ -228,7 +227,7 @@ export function parsePox2Event(poxEvent: DbPox2Event | DbPox3Event) { }, }; } - case Pox2EventName.StackIncrease: { + case SyntheticPoxEventName.StackIncrease: { return { ...baseInfo, data: { @@ -237,7 +236,7 @@ export function parsePox2Event(poxEvent: DbPox2Event | DbPox3Event) { }, }; } - case Pox2EventName.StackExtend: { + case SyntheticPoxEventName.StackExtend: { return { ...baseInfo, data: { @@ -246,7 +245,7 @@ export function parsePox2Event(poxEvent: DbPox2Event | DbPox3Event) { }, }; } - case Pox2EventName.DelegateStx: { + case SyntheticPoxEventName.DelegateStx: { return { ...baseInfo, data: { @@ -256,7 +255,7 @@ export function parsePox2Event(poxEvent: DbPox2Event | DbPox3Event) { }, }; } - case Pox2EventName.DelegateStackStx: { + case SyntheticPoxEventName.DelegateStackStx: { return { ...baseInfo, data: { @@ -268,7 +267,7 @@ export function parsePox2Event(poxEvent: DbPox2Event | DbPox3Event) { }, }; } - case Pox2EventName.DelegateStackIncrease: { + case SyntheticPoxEventName.DelegateStackIncrease: { return { ...baseInfo, data: { @@ -278,7 +277,7 @@ export function parsePox2Event(poxEvent: DbPox2Event | DbPox3Event) { }, }; } - case Pox2EventName.DelegateStackExtend: { + case SyntheticPoxEventName.DelegateStackExtend: { return { ...baseInfo, data: { @@ -288,7 +287,7 @@ export function parsePox2Event(poxEvent: DbPox2Event | DbPox3Event) { }, }; } - case Pox2EventName.StackAggregationCommit: { + case SyntheticPoxEventName.StackAggregationCommit: { return { ...baseInfo, data: { @@ -297,7 +296,7 @@ export function parsePox2Event(poxEvent: DbPox2Event | DbPox3Event) { }, }; } - case Pox2EventName.StackAggregationCommitIndexed: { + case SyntheticPoxEventName.StackAggregationCommitIndexed: { return { ...baseInfo, data: { @@ -306,7 +305,7 @@ export function parsePox2Event(poxEvent: DbPox2Event | DbPox3Event) { }, }; } - case Pox2EventName.StackAggregationIncrease: { + case SyntheticPoxEventName.StackAggregationIncrease: { return { ...baseInfo, data: { @@ -315,8 +314,17 @@ export function parsePox2Event(poxEvent: DbPox2Event | DbPox3Event) { }, }; } + case SyntheticPoxEventName.RevokeDelegateStx: { + return { + ...baseInfo, + data: { + amount_ustx: poxEvent.data.amount_ustx.toString(), + delegate_to: poxEvent.data.delegate_to, + }, + }; + } default: - throw new Error(`Unexpected Pox2 event name ${(poxEvent as DbPox2Event).name}`); + throw new Error(`Unexpected Pox2 event name ${(poxEvent as DbPoxSyntheticEvent).name}`); } } diff --git a/src/api/rosetta-constants.ts b/src/api/rosetta-constants.ts index 8024c16c0e..1e563f654b 100644 --- a/src/api/rosetta-constants.ts +++ b/src/api/rosetta-constants.ts @@ -476,18 +476,3 @@ export const RosettaSchemas: Record = { '@stacks/stacks-blockchain-api-types/api/rosetta/rosetta-construction-combine-response.schema.json', }, }; - -export const PoxContractIdentifier = { - pox1: { - mainnet: 'SP000000000000000000002Q6VF78.pox', - testnet: 'ST000000000000000000002AMW42H.pox', - }, - pox2: { - mainnet: 'SP000000000000000000002Q6VF78.pox-2', - testnet: 'ST000000000000000000002AMW42H.pox-2', - }, - pox3: { - mainnet: 'SP000000000000000000002Q6VF78.pox-3', - testnet: 'ST000000000000000000002AMW42H.pox-3', - }, -} as const; diff --git a/src/api/routes/pox2.ts b/src/api/routes/pox2.ts index 565fa6fe8b..a0786a09da 100644 --- a/src/api/routes/pox2.ts +++ b/src/api/routes/pox2.ts @@ -12,7 +12,7 @@ import { isValidBitcoinAddress, tryConvertC32ToBtc } from '../../helpers'; import { InvalidRequestError, InvalidRequestErrorType } from '../../errors'; import { getPagingQueryLimit, parsePagingQueryInput, ResourceType } from '../pagination'; import { PgStore } from '../../datastore/pg-store'; -import { parsePox2Event } from '../controllers/db-controller'; +import { parsePoxSyntheticEvent } from '../controllers/db-controller'; import { validatePrincipal, validateRequestHexInput } from '../query-helpers'; export function createPox2EventsRouter(db: PgStore): express.Router { @@ -24,8 +24,12 @@ export function createPox2EventsRouter(db: PgStore): express.Router { const limit = getPagingQueryLimit(ResourceType.Pox2Event, req.query.limit); const offset = parsePagingQueryInput(req.query.offset ?? 0); - const queryResults = await db.getPox2Events({ offset, limit }); - const parsedResult = queryResults.map(r => parsePox2Event(r)); + const queryResults = await db.getPoxSyntheticEvents({ + offset, + limit, + poxTable: 'pox2_events', + }); + const parsedResult = queryResults.map(r => parsePoxSyntheticEvent(r)); const response = { limit, offset, @@ -41,12 +45,15 @@ export function createPox2EventsRouter(db: PgStore): express.Router { asyncHandler(async (req, res) => { const { tx_id } = req.params; validateRequestHexInput(tx_id); - const queryResults = await db.getPox2EventsForTx({ txId: tx_id }); + const queryResults = await db.getPoxSyntheticEventsForTx({ + txId: tx_id, + poxTable: 'pox2_events', + }); if (!queryResults.found) { res.status(404).json({ error: `could not find transaction by ID ${tx_id}` }); return; } - const parsedResult = queryResults.result.map(r => parsePox2Event(r)); + const parsedResult = queryResults.result.map(r => parsePoxSyntheticEvent(r)); const response = { results: parsedResult, }; @@ -60,12 +67,15 @@ export function createPox2EventsRouter(db: PgStore): express.Router { asyncHandler(async (req, res) => { const { principal } = req.params; validatePrincipal(principal); - const queryResults = await db.getPox2EventsForStacker({ principal }); + const queryResults = await db.getPoxSyntheticEventsForStacker({ + principal, + poxTable: 'pox2_events', + }); if (!queryResults.found) { res.status(404).json({ error: `could not find principal ${principal}` }); return; } - const parsedResult = queryResults.result.map(r => parsePox2Event(r)); + const parsedResult = queryResults.result.map(r => parsePoxSyntheticEvent(r)); const response = { results: parsedResult, }; diff --git a/src/api/routes/pox3.ts b/src/api/routes/pox3.ts index c01d305665..4a1c45c52d 100644 --- a/src/api/routes/pox3.ts +++ b/src/api/routes/pox3.ts @@ -2,7 +2,7 @@ import * as express from 'express'; import { asyncHandler } from '../async-handler'; import { PgStore } from '../../datastore/pg-store'; -import { parsePox2Event } from '../controllers/db-controller'; +import { parsePoxSyntheticEvent } from '../controllers/db-controller'; import { ResourceType, getPagingQueryLimit, parsePagingQueryInput } from '../pagination'; import { validatePrincipal, validateRequestHexInput } from '../query-helpers'; @@ -15,8 +15,12 @@ export function createPox3EventsRouter(db: PgStore): express.Router { const limit = getPagingQueryLimit(ResourceType.Pox2Event, req.query.limit); const offset = parsePagingQueryInput(req.query.offset ?? 0); - const queryResults = await db.getPox3Events({ offset, limit }); - const parsedResult = queryResults.map(r => parsePox2Event(r)); // parsePox2Event is pox-3 compatible + const queryResults = await db.getPoxSyntheticEvents({ + offset, + limit, + poxTable: 'pox3_events', + }); + const parsedResult = queryResults.map(r => parsePoxSyntheticEvent(r)); const response = { limit, offset, @@ -31,12 +35,15 @@ export function createPox3EventsRouter(db: PgStore): express.Router { asyncHandler(async (req, res) => { const { tx_id } = req.params; validateRequestHexInput(tx_id); - const queryResults = await db.getPox3EventsForTx({ txId: tx_id }); + const queryResults = await db.getPoxSyntheticEventsForTx({ + txId: tx_id, + poxTable: 'pox3_events', + }); if (!queryResults.found) { res.status(404).json({ error: `could not find transaction by ID ${tx_id}` }); return; } - const parsedResult = queryResults.result.map(r => parsePox2Event(r)); + const parsedResult = queryResults.result.map(r => parsePoxSyntheticEvent(r)); const response = { results: parsedResult, }; @@ -49,12 +56,15 @@ export function createPox3EventsRouter(db: PgStore): express.Router { asyncHandler(async (req, res) => { const { principal } = req.params; validatePrincipal(principal); - const queryResults = await db.getPox3EventsForStacker({ principal }); + const queryResults = await db.getPoxSyntheticEventsForStacker({ + principal, + poxTable: 'pox3_events', + }); if (!queryResults.found) { res.status(404).json({ error: `could not find principal ${principal}` }); return; } - const parsedResult = queryResults.result.map(r => parsePox2Event(r)); + const parsedResult = queryResults.result.map(r => parsePoxSyntheticEvent(r)); const response = { results: parsedResult, }; diff --git a/src/datastore/common.ts b/src/datastore/common.ts index 88083cccd5..d57c65c4c5 100644 --- a/src/datastore/common.ts +++ b/src/datastore/common.ts @@ -1,6 +1,6 @@ import { ClarityAbi } from '@stacks/transactions'; import { Block } from '@stacks/stacks-blockchain-api-types'; -import { Pox2EventName } from '../pox-helpers'; +import { SyntheticPoxEventName } from '../pox-helpers'; import { PgBytea, PgJsonb, PgNumeric } from '@hirosystems/api-toolkit'; export interface DbBlock { @@ -321,7 +321,9 @@ export interface DbEventBase { canonical: boolean; } -export interface DbPox2BaseEventData { +export type PoxSyntheticEventTable = 'pox2_events' | 'pox3_events' | 'pox4_events'; + +export interface DbPoxSyntheticBaseEventData { stacker: string; locked: bigint; balance: bigint; @@ -330,16 +332,16 @@ export interface DbPox2BaseEventData { pox_addr_raw: string | null; } -export interface DbPox2HandleUnlockEvent extends DbPox2BaseEventData { - name: Pox2EventName.HandleUnlock; +export interface DbPoxSyntheticHandleUnlockEvent extends DbPoxSyntheticBaseEventData { + name: SyntheticPoxEventName.HandleUnlock; data: { first_cycle_locked: bigint; first_unlocked_cycle: bigint; }; } -export interface DbPox2StackStxEvent extends DbPox2BaseEventData { - name: Pox2EventName.StackStx; +export interface DbPoxSyntheticStackStxEvent extends DbPoxSyntheticBaseEventData { + name: SyntheticPoxEventName.StackStx; data: { lock_amount: bigint; lock_period: bigint; @@ -348,24 +350,24 @@ export interface DbPox2StackStxEvent extends DbPox2BaseEventData { }; } -export interface DbPox2StackIncreaseEvent extends DbPox2BaseEventData { - name: Pox2EventName.StackIncrease; +export interface DbPoxSyntheticStackIncreaseEvent extends DbPoxSyntheticBaseEventData { + name: SyntheticPoxEventName.StackIncrease; data: { increase_by: bigint; total_locked: bigint; }; } -export interface DbPox2StackExtendEvent extends DbPox2BaseEventData { - name: Pox2EventName.StackExtend; +export interface DbPoxSyntheticStackExtendEvent extends DbPoxSyntheticBaseEventData { + name: SyntheticPoxEventName.StackExtend; data: { extend_count: bigint; unlock_burn_height: bigint; }; } -export interface DbPox2DelegateStxEvent extends DbPox2BaseEventData { - name: Pox2EventName.DelegateStx; +export interface DbPoxSyntheticDelegateStxEvent extends DbPoxSyntheticBaseEventData { + name: SyntheticPoxEventName.DelegateStx; data: { amount_ustx: bigint; delegate_to: string; @@ -373,8 +375,8 @@ export interface DbPox2DelegateStxEvent extends DbPox2BaseEventData { }; } -export interface DbPox2DelegateStackStxEvent extends DbPox2BaseEventData { - name: Pox2EventName.DelegateStackStx; +export interface DbPoxSyntheticDelegateStackStxEvent extends DbPoxSyntheticBaseEventData { + name: SyntheticPoxEventName.DelegateStackStx; data: { lock_amount: bigint; unlock_burn_height: bigint; @@ -384,8 +386,8 @@ export interface DbPox2DelegateStackStxEvent extends DbPox2BaseEventData { }; } -export interface DbPox2DelegateStackIncreaseEvent extends DbPox2BaseEventData { - name: Pox2EventName.DelegateStackIncrease; +export interface DbPoxSyntheticDelegateStackIncreaseEvent extends DbPoxSyntheticBaseEventData { + name: SyntheticPoxEventName.DelegateStackIncrease; data: { increase_by: bigint; total_locked: bigint; @@ -393,8 +395,8 @@ export interface DbPox2DelegateStackIncreaseEvent extends DbPox2BaseEventData { }; } -export interface DbPox2DelegateStackExtendEvent extends DbPox2BaseEventData { - name: Pox2EventName.DelegateStackExtend; +export interface DbPoxSyntheticDelegateStackExtendEvent extends DbPoxSyntheticBaseEventData { + name: SyntheticPoxEventName.DelegateStackExtend; data: { unlock_burn_height: bigint; extend_count: bigint; @@ -402,47 +404,55 @@ export interface DbPox2DelegateStackExtendEvent extends DbPox2BaseEventData { }; } -export interface DbPox2StackAggregationCommitEvent extends DbPox2BaseEventData { - name: Pox2EventName.StackAggregationCommit; +export interface DbPoxSyntheticStackAggregationCommitEvent extends DbPoxSyntheticBaseEventData { + name: SyntheticPoxEventName.StackAggregationCommit; data: { reward_cycle: bigint; amount_ustx: bigint; }; } -export interface DbPox2StackAggregationCommitIndexedEvent extends DbPox2BaseEventData { - name: Pox2EventName.StackAggregationCommitIndexed; +export interface DbPoxSyntheticStackAggregationCommitIndexedEvent + extends DbPoxSyntheticBaseEventData { + name: SyntheticPoxEventName.StackAggregationCommitIndexed; data: { reward_cycle: bigint; amount_ustx: bigint; }; } -export interface DbPox2StackAggregationIncreaseEvent extends DbPox2BaseEventData { - name: Pox2EventName.StackAggregationIncrease; +export interface DbPoxSyntheticStackAggregationIncreaseEvent extends DbPoxSyntheticBaseEventData { + name: SyntheticPoxEventName.StackAggregationIncrease; data: { reward_cycle: bigint; amount_ustx: bigint; }; } -export type DbPox2EventData = - | DbPox2HandleUnlockEvent - | DbPox2StackStxEvent - | DbPox2StackIncreaseEvent - | DbPox2StackExtendEvent - | DbPox2DelegateStxEvent - | DbPox2DelegateStackStxEvent - | DbPox2DelegateStackIncreaseEvent - | DbPox2DelegateStackExtendEvent - | DbPox2StackAggregationCommitEvent - | DbPox2StackAggregationCommitIndexedEvent - | DbPox2StackAggregationIncreaseEvent; +export interface DbPoxSyntheticRevokeDelegateStxEvent extends DbPoxSyntheticBaseEventData { + name: SyntheticPoxEventName.RevokeDelegateStx; + data: { + // TODO: determine what data is available for this event type + amount_ustx: bigint; + delegate_to: string; + }; +} -export type DbPox2Event = DbEventBase & DbPox2EventData; +export type DbPoxSyntheticEventData = + | DbPoxSyntheticHandleUnlockEvent + | DbPoxSyntheticStackStxEvent + | DbPoxSyntheticStackIncreaseEvent + | DbPoxSyntheticStackExtendEvent + | DbPoxSyntheticDelegateStxEvent + | DbPoxSyntheticDelegateStackStxEvent + | DbPoxSyntheticDelegateStackIncreaseEvent + | DbPoxSyntheticDelegateStackExtendEvent + | DbPoxSyntheticStackAggregationCommitEvent + | DbPoxSyntheticStackAggregationCommitIndexedEvent + | DbPoxSyntheticStackAggregationIncreaseEvent + | DbPoxSyntheticRevokeDelegateStxEvent; -// todo: should we copy DbPox2EventData for pox3? -export type DbPox3Event = DbEventBase & DbPox2EventData; +export type DbPoxSyntheticEvent = DbEventBase & DbPoxSyntheticEventData; export interface DbPox3Stacker { stacker: string; @@ -587,8 +597,9 @@ export interface DataStoreTxEventData { smartContracts: DbSmartContract[]; names: DbBnsName[]; namespaces: DbBnsNamespace[]; - pox2Events: DbPox2Event[]; - pox3Events: DbPox3Event[]; + pox2Events: DbPoxSyntheticEvent[]; + pox3Events: DbPoxSyntheticEvent[]; + pox4Events: DbPoxSyntheticEvent[]; } export interface DataStoreAttachmentData { @@ -1195,7 +1206,7 @@ export interface RawEventRequestInsertValues { payload: string; } -export interface Pox2EventQueryResult { +export interface PoxSyntheticEventQueryResult { event_index: number; tx_id: string; tx_index: number; @@ -1254,10 +1265,7 @@ export interface Pox2EventQueryResult { amount_ustx: string | null; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface Pox3EventQueryResult extends Pox2EventQueryResult {} - -export interface Pox2EventInsertValues { +export interface PoxSyntheticEventInsertValues { event_index: number; tx_id: PgBytea; tx_index: number; diff --git a/src/datastore/helpers.ts b/src/datastore/helpers.ts index ccf0889964..ea585610a0 100644 --- a/src/datastore/helpers.ts +++ b/src/datastore/helpers.ts @@ -14,19 +14,19 @@ import { DbMempoolTxRaw, DbMicroblock, DbNftEvent, - DbPox2BaseEventData, - DbPox2DelegateStackExtendEvent, - DbPox2DelegateStackIncreaseEvent, - DbPox2DelegateStackStxEvent, - DbPox2DelegateStxEvent, - DbPox2Event, - DbPox2HandleUnlockEvent, - DbPox2StackAggregationCommitEvent, - DbPox2StackAggregationCommitIndexedEvent, - DbPox2StackAggregationIncreaseEvent, - DbPox2StackExtendEvent, - DbPox2StackIncreaseEvent, - DbPox2StackStxEvent, + DbPoxSyntheticBaseEventData, + DbPoxSyntheticDelegateStackExtendEvent, + DbPoxSyntheticDelegateStackIncreaseEvent, + DbPoxSyntheticDelegateStackStxEvent, + DbPoxSyntheticDelegateStxEvent, + DbPoxSyntheticEvent, + DbPoxSyntheticHandleUnlockEvent, + DbPoxSyntheticStackAggregationCommitEvent, + DbPoxSyntheticStackAggregationCommitIndexedEvent, + DbPoxSyntheticStackAggregationIncreaseEvent, + DbPoxSyntheticStackExtendEvent, + DbPoxSyntheticStackIncreaseEvent, + DbPoxSyntheticStackStxEvent, DbSmartContract, DbSmartContractEvent, DbStxEvent, @@ -39,8 +39,9 @@ import { FaucetRequestQueryResult, MempoolTxQueryResult, MicroblockQueryResult, - Pox2EventQueryResult, + PoxSyntheticEventQueryResult, TxQueryResult, + DbPoxSyntheticRevokeDelegateStxEvent, } from './common'; import { CoreNodeDropMempoolTxReasonType, @@ -60,7 +61,7 @@ import * as prom from 'prom-client'; import { NftEvent } from 'docs/generated'; import { getAssetEventTypeString } from '../api/controllers/db-controller'; import { PgStoreEventEmitter } from './pg-store-event-emitter'; -import { Pox2EventName } from '../pox-helpers'; +import { SyntheticPoxEventName } from '../pox-helpers'; import { logger } from '../logger'; import { PgSqlClient } from '@hirosystems/api-toolkit'; @@ -208,7 +209,7 @@ export const TX_METADATA_TABLES = [ 'subdomains', ] as const; -export const POX2_EVENT_COLUMNS = [ +export const POX_SYNTHETIC_EVENT_COLUMNS = [ 'event_index', 'tx_id', 'tx_index', @@ -241,8 +242,6 @@ export const POX2_EVENT_COLUMNS = [ 'amount_ustx', ]; -export const POX3_EVENT_COLUMNS = POX2_EVENT_COLUMNS; - /** * Adds a table name prefix to an array of column names. * @param columns - array of column names @@ -606,7 +605,7 @@ export function parseDbEvents( return events; } -export function parseDbPox2Event(row: Pox2EventQueryResult): DbPox2Event { +export function parseDbPoxSyntheticEvent(row: PoxSyntheticEventQueryResult): DbPoxSyntheticEvent { const baseEvent: DbEventBase = { event_index: row.event_index, tx_id: row.tx_id, @@ -614,7 +613,7 @@ export function parseDbPox2Event(row: Pox2EventQueryResult): DbPox2Event { block_height: row.block_height, canonical: row.canonical, }; - const basePox2Event: DbPox2BaseEventData = { + const basePoxEvent: DbPoxSyntheticBaseEventData = { stacker: row.stacker, locked: BigInt(row.locked ?? 0), balance: BigInt(row.balance), @@ -622,11 +621,11 @@ export function parseDbPox2Event(row: Pox2EventQueryResult): DbPox2Event { pox_addr: row.pox_addr ?? null, pox_addr_raw: row.pox_addr_raw ?? null, }; - const rowName = row.name as Pox2EventName; + const rowName = row.name as SyntheticPoxEventName; switch (rowName) { - case Pox2EventName.HandleUnlock: { - const eventData: DbPox2HandleUnlockEvent = { - ...basePox2Event, + case SyntheticPoxEventName.HandleUnlock: { + const eventData: DbPoxSyntheticHandleUnlockEvent = { + ...basePoxEvent, name: rowName, data: { first_cycle_locked: BigInt(unwrapOptionalProp(row, 'first_unlocked_cycle')), @@ -638,9 +637,9 @@ export function parseDbPox2Event(row: Pox2EventQueryResult): DbPox2Event { ...eventData, }; } - case Pox2EventName.StackStx: { - const eventData: DbPox2StackStxEvent = { - ...basePox2Event, + case SyntheticPoxEventName.StackStx: { + const eventData: DbPoxSyntheticStackStxEvent = { + ...basePoxEvent, name: rowName, data: { lock_amount: BigInt(unwrapOptionalProp(row, 'lock_amount')), @@ -654,9 +653,9 @@ export function parseDbPox2Event(row: Pox2EventQueryResult): DbPox2Event { ...eventData, }; } - case Pox2EventName.StackIncrease: { - const eventData: DbPox2StackIncreaseEvent = { - ...basePox2Event, + case SyntheticPoxEventName.StackIncrease: { + const eventData: DbPoxSyntheticStackIncreaseEvent = { + ...basePoxEvent, name: rowName, data: { increase_by: BigInt(unwrapOptionalProp(row, 'increase_by')), @@ -668,9 +667,9 @@ export function parseDbPox2Event(row: Pox2EventQueryResult): DbPox2Event { ...eventData, }; } - case Pox2EventName.StackExtend: { - const eventData: DbPox2StackExtendEvent = { - ...basePox2Event, + case SyntheticPoxEventName.StackExtend: { + const eventData: DbPoxSyntheticStackExtendEvent = { + ...basePoxEvent, name: rowName, data: { extend_count: BigInt(unwrapOptionalProp(row, 'extend_count')), @@ -682,9 +681,9 @@ export function parseDbPox2Event(row: Pox2EventQueryResult): DbPox2Event { ...eventData, }; } - case Pox2EventName.DelegateStx: { - const eventData: DbPox2DelegateStxEvent = { - ...basePox2Event, + case SyntheticPoxEventName.DelegateStx: { + const eventData: DbPoxSyntheticDelegateStxEvent = { + ...basePoxEvent, name: rowName, data: { amount_ustx: BigInt(unwrapOptionalProp(row, 'amount_ustx')), @@ -699,9 +698,9 @@ export function parseDbPox2Event(row: Pox2EventQueryResult): DbPox2Event { ...eventData, }; } - case Pox2EventName.DelegateStackStx: { - const eventData: DbPox2DelegateStackStxEvent = { - ...basePox2Event, + case SyntheticPoxEventName.DelegateStackStx: { + const eventData: DbPoxSyntheticDelegateStackStxEvent = { + ...basePoxEvent, name: rowName, data: { lock_amount: BigInt(unwrapOptionalProp(row, 'lock_amount')), @@ -716,9 +715,9 @@ export function parseDbPox2Event(row: Pox2EventQueryResult): DbPox2Event { ...eventData, }; } - case Pox2EventName.DelegateStackIncrease: { - const eventData: DbPox2DelegateStackIncreaseEvent = { - ...basePox2Event, + case SyntheticPoxEventName.DelegateStackIncrease: { + const eventData: DbPoxSyntheticDelegateStackIncreaseEvent = { + ...basePoxEvent, name: rowName, data: { increase_by: BigInt(unwrapOptionalProp(row, 'increase_by')), @@ -731,9 +730,9 @@ export function parseDbPox2Event(row: Pox2EventQueryResult): DbPox2Event { ...eventData, }; } - case Pox2EventName.DelegateStackExtend: { - const eventData: DbPox2DelegateStackExtendEvent = { - ...basePox2Event, + case SyntheticPoxEventName.DelegateStackExtend: { + const eventData: DbPoxSyntheticDelegateStackExtendEvent = { + ...basePoxEvent, name: rowName, data: { unlock_burn_height: BigInt(unwrapOptionalProp(row, 'unlock_burn_height')), @@ -746,9 +745,9 @@ export function parseDbPox2Event(row: Pox2EventQueryResult): DbPox2Event { ...eventData, }; } - case Pox2EventName.StackAggregationCommit: { - const eventData: DbPox2StackAggregationCommitEvent = { - ...basePox2Event, + case SyntheticPoxEventName.StackAggregationCommit: { + const eventData: DbPoxSyntheticStackAggregationCommitEvent = { + ...basePoxEvent, name: rowName, data: { reward_cycle: BigInt(unwrapOptionalProp(row, 'reward_cycle')), @@ -760,9 +759,9 @@ export function parseDbPox2Event(row: Pox2EventQueryResult): DbPox2Event { ...eventData, }; } - case Pox2EventName.StackAggregationCommitIndexed: { - const eventData: DbPox2StackAggregationCommitIndexedEvent = { - ...basePox2Event, + case SyntheticPoxEventName.StackAggregationCommitIndexed: { + const eventData: DbPoxSyntheticStackAggregationCommitIndexedEvent = { + ...basePoxEvent, name: rowName, data: { reward_cycle: BigInt(unwrapOptionalProp(row, 'reward_cycle')), @@ -774,9 +773,9 @@ export function parseDbPox2Event(row: Pox2EventQueryResult): DbPox2Event { ...eventData, }; } - case Pox2EventName.StackAggregationIncrease: { - const eventData: DbPox2StackAggregationIncreaseEvent = { - ...basePox2Event, + case SyntheticPoxEventName.StackAggregationIncrease: { + const eventData: DbPoxSyntheticStackAggregationIncreaseEvent = { + ...basePoxEvent, name: rowName, data: { reward_cycle: BigInt(unwrapOptionalProp(row, 'reward_cycle')), @@ -788,6 +787,21 @@ export function parseDbPox2Event(row: Pox2EventQueryResult): DbPox2Event { ...eventData, }; } + case SyntheticPoxEventName.RevokeDelegateStx: { + const eventData: DbPoxSyntheticRevokeDelegateStxEvent = { + ...basePoxEvent, + name: rowName, + data: { + // TODO: figure out what data is available for this event + amount_ustx: BigInt(unwrapOptionalProp(row, 'amount_ustx')), + delegate_to: unwrapOptionalProp(row, 'delegate_to'), + }, + }; + return { + ...baseEvent, + ...eventData, + }; + } default: { throw new Error(`Unexpected event name ${rowName}`); } diff --git a/src/datastore/pg-store.ts b/src/datastore/pg-store.ts index 9167e7e0a6..0ebfda9942 100644 --- a/src/datastore/pg-store.ts +++ b/src/datastore/pg-store.ts @@ -43,8 +43,7 @@ import { DbMicroblock, DbMinerReward, DbNftEvent, - DbPox2Event, - DbPox3Event, + DbPoxSyntheticEvent, DbPox3Stacker, DbRewardSlotHolder, DbSearchResult, @@ -65,11 +64,11 @@ import { NftEventWithTxMetadata, NftHoldingInfo, NftHoldingInfoWithTxMetadata, - Pox2EventQueryResult, - Pox3EventQueryResult, + PoxSyntheticEventQueryResult, RawTxQueryResult, StxUnlockEvent, TransferQueryResult, + PoxSyntheticEventTable, } from './common'; import { abiColumn, @@ -78,22 +77,21 @@ import { MICROBLOCK_COLUMNS, parseBlockQueryResult, parseDbEvents, - parseDbPox2Event, + parseDbPoxSyntheticEvent, parseFaucetRequestQueryResult, parseMempoolTxQueryResult, parseMicroblockQueryResult, parseQueryResultToSmartContract, parseTxQueryResult, parseTxsWithAssetTransfers, - POX2_EVENT_COLUMNS, - POX3_EVENT_COLUMNS, + POX_SYNTHETIC_EVENT_COLUMNS, prefixedCols, TX_COLUMNS, unsafeCols, validateZonefileHash, } from './helpers'; import { PgNotifier } from './pg-notifier'; -import { Pox2EventName } from '../pox-helpers'; +import { SyntheticPoxEventName } from '../pox-helpers'; import { BasePgStore, PgSqlClient, connectPostgres } from '@hirosystems/api-toolkit'; import { PgServer, @@ -1842,119 +1840,72 @@ export class PgStore extends BasePgStore { return parseQueryResultToSmartContract(row); } - async getPox2Events({ + async getPoxSyntheticEvents({ limit, offset, + poxTable, }: { limit: number; offset: number; - }): Promise { + poxTable: PoxSyntheticEventTable; + }): Promise { return await this.sqlTransaction(async sql => { - const queryResults = await sql` - SELECT ${sql(POX2_EVENT_COLUMNS)} - FROM pox2_events + const queryResults = await sql` + SELECT ${sql(POX_SYNTHETIC_EVENT_COLUMNS)} + FROM ${sql(poxTable)} WHERE canonical = true AND microblock_canonical = true ORDER BY block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC LIMIT ${limit} OFFSET ${offset} `; - const result = queryResults.map(result => parseDbPox2Event(result)); + const result = queryResults.map(result => parseDbPoxSyntheticEvent(result)); return result; }); } - /** modified copy of `getPox2Events` */ - async getPox3Events({ - limit, - offset, + async getPoxSyntheticEventsForTx({ + txId, + poxTable, }: { - limit: number; - offset: number; - }): Promise { - return await this.sqlTransaction(async sql => { - const queryResults = await sql` - SELECT ${sql(POX2_EVENT_COLUMNS)} - FROM pox3_events - WHERE canonical = true AND microblock_canonical = true - ORDER BY block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC - LIMIT ${limit} - OFFSET ${offset} - `; - const result = queryResults.map(result => parseDbPox2Event(result)); - return result; - }); - } - - async getPox2EventsForTx({ txId }: { txId: string }): Promise> { - return await this.sqlTransaction(async sql => { - const dbTx = await this.getTx({ txId, includeUnanchored: true }); - if (!dbTx.found) { - return { found: false }; - } - const queryResults = await sql` - SELECT ${sql(POX2_EVENT_COLUMNS)} - FROM pox2_events - WHERE canonical = true AND microblock_canonical = true AND tx_id = ${txId} - ORDER BY block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC - `; - const result = queryResults.map(result => parseDbPox2Event(result)); - return { found: true, result: result }; - }); - } - - /** modified copy of `getPox2EventsForTx` */ - async getPox3EventsForTx({ txId }: { txId: string }): Promise> { + txId: string; + poxTable: PoxSyntheticEventTable; + }): Promise> { return await this.sqlTransaction(async sql => { const dbTx = await this.getTx({ txId, includeUnanchored: true }); if (!dbTx.found) { return { found: false }; } - const queryResults = await sql` - SELECT ${sql(POX2_EVENT_COLUMNS)} - FROM pox3_events + const queryResults = await sql` + SELECT ${sql(POX_SYNTHETIC_EVENT_COLUMNS)} + FROM ${sql(poxTable)} WHERE canonical = true AND microblock_canonical = true AND tx_id = ${txId} ORDER BY block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC `; - const result = queryResults.map(result => parseDbPox2Event(result)); + const result = queryResults.map(result => parseDbPoxSyntheticEvent(result)); return { found: true, result: result }; }); } - async getPox2EventsForStacker({ + async getPoxSyntheticEventsForStacker({ principal, + poxTable, }: { principal: string; - }): Promise> { + poxTable: PoxSyntheticEventTable; + }): Promise> { return await this.sqlTransaction(async sql => { - const queryResults = await sql` - SELECT ${sql(POX2_EVENT_COLUMNS)} - FROM pox2_events + const queryResults = await sql` + SELECT ${sql(POX_SYNTHETIC_EVENT_COLUMNS)} + FROM ${sql(poxTable)} WHERE canonical = true AND microblock_canonical = true AND stacker = ${principal} ORDER BY block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC `; - const result = queryResults.map(result => parseDbPox2Event(result)); - return { found: true, result: result }; - }); - } - - /** modified copy of `getPox2EventsForStacker` */ - async getPox3EventsForStacker({ - principal, - }: { - principal: string; - }): Promise> { - return await this.sqlTransaction(async sql => { - const queryResults = await sql` - SELECT ${sql(POX2_EVENT_COLUMNS)} - FROM pox3_events - WHERE canonical = true AND microblock_canonical = true AND stacker = ${principal} - ORDER BY block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC - `; - const result = queryResults.map(result => parseDbPox2Event(result)); + const result = queryResults.map(result => parseDbPoxSyntheticEvent(result)); return { found: true, result: result }; }); } + // TODO: modify this function for pox4 async getPox3PoolDelegations(args: { delegator: string; blockHeight: number; @@ -1982,7 +1933,7 @@ export class PgStore extends BasePgStore { FROM pox3_events WHERE canonical = true AND microblock_canonical = true AND - name = ${Pox2EventName.DelegateStx} AND delegate_to = ${args.delegator} AND + name = ${SyntheticPoxEventName.DelegateStx} AND delegate_to = ${args.delegator} AND block_height <= ${args.blockHeight} AND block_height > ${args.afterBlockHeight} AND (unlock_burn_height > ${args.burnBlockHeight} OR unlock_burn_height IS NULL) ORDER BY stacker, block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC @@ -2191,6 +2142,7 @@ export class PgStore extends BasePgStore { let includePox1State = true; let includePox2State = true; + let includePox3State = true; const poxForceUnlockHeights = await this.getPoxForcedUnlockHeightsInternal(sql); if (poxForceUnlockHeights.found) { if ( @@ -2205,6 +2157,12 @@ export class PgStore extends BasePgStore { ) { includePox2State = false; } + if ( + poxForceUnlockHeights.result.pox3UnlockHeight && + burnBlockHeight > poxForceUnlockHeights.result.pox3UnlockHeight + ) { + includePox3State = false; + } } // Once the pox_v1_unlock_height is reached, stop using `stx_lock_events` to determinel locked state, @@ -2243,22 +2201,26 @@ export class PgStore extends BasePgStore { if (includePox2State) { // Query for the latest lock event that still applies to the current burn block height. // Special case for `handle-unlock` which should be returned if it is the last received event. - const pox2EventQuery = await sql` - SELECT ${sql(POX2_EVENT_COLUMNS)} + const pox2EventQuery = await sql` + SELECT ${sql(POX_SYNTHETIC_EVENT_COLUMNS)} FROM pox2_events WHERE canonical = true AND microblock_canonical = true AND stacker = ${stxAddress} AND block_height <= ${blockHeight} AND ( - (name != ${Pox2EventName.HandleUnlock} AND burnchain_unlock_height >= ${burnBlockHeight}) + (name != ${ + SyntheticPoxEventName.HandleUnlock + } AND burnchain_unlock_height >= ${burnBlockHeight}) OR - (name = ${Pox2EventName.HandleUnlock} AND burnchain_unlock_height < ${burnBlockHeight}) + (name = ${ + SyntheticPoxEventName.HandleUnlock + } AND burnchain_unlock_height < ${burnBlockHeight}) ) ORDER BY block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC LIMIT 1 `; if (pox2EventQuery.length > 0) { - const pox2Event = parseDbPox2Event(pox2EventQuery[0]); - if (pox2Event.name === Pox2EventName.HandleUnlock) { + const pox2Event = parseDbPoxSyntheticEvent(pox2EventQuery[0]); + if (pox2Event.name === SyntheticPoxEventName.HandleUnlock) { // on a handle-unlock, set all of the locked stx related property to empty/default lockTxId = ''; locked = 0n; @@ -2276,33 +2238,73 @@ export class PgStore extends BasePgStore { } } - // TODO: implement includePox3State + // Once the pox_v3_unlock_height is reached, stop using `pox3_events` to determine locked state. + if (includePox3State) { + // Query for the latest lock event that still applies to the current burn block height. + // Special case for `handle-unlock` which should be returned if it is the last received event. + const pox3EventQuery = await sql` + SELECT ${sql(POX_SYNTHETIC_EVENT_COLUMNS)} + FROM pox3_events + WHERE canonical = true AND microblock_canonical = true AND stacker = ${stxAddress} + AND block_height <= ${blockHeight} + AND ( + (name != ${ + SyntheticPoxEventName.HandleUnlock + } AND burnchain_unlock_height >= ${burnBlockHeight}) + OR + (name = ${ + SyntheticPoxEventName.HandleUnlock + } AND burnchain_unlock_height < ${burnBlockHeight}) + ) + ORDER BY block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC + LIMIT 1 + `; + if (pox3EventQuery.length > 0) { + const pox3Event = parseDbPoxSyntheticEvent(pox3EventQuery[0]); + if (pox3Event.name === SyntheticPoxEventName.HandleUnlock) { + // on a handle-unlock, set all of the locked stx related property to empty/default + lockTxId = ''; + locked = 0n; + burnchainUnlockHeight = 0; + lockHeight = 0; + burnchainLockHeight = 0; + } else { + lockTxId = pox3Event.tx_id; + locked = pox3Event.locked; + burnchainUnlockHeight = Number(pox3Event.burnchain_unlock_height); + lockHeight = pox3Event.block_height; + const blockQuery = await this.getBlockByHeightInternal(sql, lockHeight); + burnchainLockHeight = blockQuery.found ? blockQuery.result.burn_block_height : 0; + } + } + } - // == PoX-3 ================================================================ - // Assuming includePox3State = true; since there is no unlock height for pox3 (yet) + // == PoX-4 ================================================================ + // Assuming includePox3State = true; since there is no unlock height for pox4 (yet) // Query for the latest lock event that still applies to the current burn block height. // Special case for `handle-unlock` which should be returned if it is the last received event. - // todo: do we need handle-unlock for pox3? - - // Modified copy of the pox-2 query - const pox3EventQuery = await sql` - SELECT ${sql(POX3_EVENT_COLUMNS)} - FROM pox3_events + const pox4EventQuery = await sql` + SELECT ${sql(POX_SYNTHETIC_EVENT_COLUMNS)} + FROM pox4_events WHERE canonical = true AND microblock_canonical = true AND stacker = ${stxAddress} AND block_height <= ${blockHeight} AND ( - (name != ${Pox2EventName.HandleUnlock} AND burnchain_unlock_height >= ${burnBlockHeight}) + (name != ${ + SyntheticPoxEventName.HandleUnlock + } AND burnchain_unlock_height >= ${burnBlockHeight}) OR - (name = ${Pox2EventName.HandleUnlock} AND burnchain_unlock_height < ${burnBlockHeight}) + (name = ${ + SyntheticPoxEventName.HandleUnlock + } AND burnchain_unlock_height < ${burnBlockHeight}) ) ORDER BY block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC LIMIT 1 `; - if (pox3EventQuery.length > 0) { - const pox3Event = parseDbPox2Event(pox3EventQuery[0]); - if (pox3Event.name === Pox2EventName.HandleUnlock) { + if (pox4EventQuery.length > 0) { + const pox4Event = parseDbPoxSyntheticEvent(pox4EventQuery[0]); + if (pox4Event.name === SyntheticPoxEventName.HandleUnlock) { // on a handle-unlock, set all of the locked stx related property to empty/default lockTxId = ''; locked = 0n; @@ -2310,10 +2312,10 @@ export class PgStore extends BasePgStore { lockHeight = 0; burnchainLockHeight = 0; } else { - lockTxId = pox3Event.tx_id; - locked = pox3Event.locked; - burnchainUnlockHeight = Number(pox3Event.burnchain_unlock_height); - lockHeight = pox3Event.block_height; + lockTxId = pox4Event.tx_id; + locked = pox4Event.locked; + burnchainUnlockHeight = Number(pox4Event.burnchain_unlock_height); + lockHeight = pox4Event.block_height; const blockQuery = await this.getBlockByHeightInternal(sql, lockHeight); burnchainLockHeight = blockQuery.found ? blockQuery.result.burn_block_height : 0; } @@ -4002,10 +4004,12 @@ export class PgStore extends BasePgStore { } let v1UnlockHeight: number | null = null; let v2UnlockHeight: number | null = null; + let v3UnlockHeight: number | null = null; const poxUnlockHeights = await this.getPoxForcedUnlockHeightsInternal(sql); if (poxUnlockHeights.found) { v1UnlockHeight = poxUnlockHeights.result.pox1UnlockHeight; v2UnlockHeight = poxUnlockHeights.result.pox2UnlockHeight; + v3UnlockHeight = poxUnlockHeights.result.pox3UnlockHeight; } type StxLockEventResult = { @@ -4064,8 +4068,8 @@ export class PgStore extends BasePgStore { let poxV2Unlocks: StxLockEventResult[] = []; const checkPox2Unlocks = v2UnlockHeight === null || current_burn_height < v2UnlockHeight; if (checkPox2Unlocks) { - const pox2EventQuery = await sql` - SELECT DISTINCT ON (stacker) stacker, ${sql(POX2_EVENT_COLUMNS)} + const pox2EventQuery = await sql` + SELECT DISTINCT ON (stacker) stacker, ${sql(POX_SYNTHETIC_EVENT_COLUMNS)} FROM pox2_events WHERE canonical = true AND microblock_canonical = true AND block_height <= ${block.block_height} @@ -4074,15 +4078,15 @@ export class PgStore extends BasePgStore { burnchain_unlock_height <= ${current_burn_height} AND burnchain_unlock_height > ${previous_burn_height} AND name IN ${sql([ - Pox2EventName.StackStx, - Pox2EventName.StackIncrease, - Pox2EventName.StackExtend, - Pox2EventName.DelegateStackStx, - Pox2EventName.DelegateStackIncrease, - Pox2EventName.DelegateStackExtend, + SyntheticPoxEventName.StackStx, + SyntheticPoxEventName.StackIncrease, + SyntheticPoxEventName.StackExtend, + SyntheticPoxEventName.DelegateStackStx, + SyntheticPoxEventName.DelegateStackIncrease, + SyntheticPoxEventName.DelegateStackExtend, ])} ) OR ( - name = ${Pox2EventName.HandleUnlock} + name = ${SyntheticPoxEventName.HandleUnlock} AND burnchain_unlock_height < ${current_burn_height} AND burnchain_unlock_height >= ${previous_burn_height} ) @@ -4090,7 +4094,7 @@ export class PgStore extends BasePgStore { ORDER BY stacker, block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC `; poxV2Unlocks = pox2EventQuery.map(row => { - const pox2Event = parseDbPox2Event(row); + const pox2Event = parseDbPoxSyntheticEvent(row); const unlockEvent: StxLockEventResult = { locked_amount: pox2Event.locked.toString(), unlock_height: Number(pox2Event.burnchain_unlock_height), @@ -4109,23 +4113,23 @@ export class PgStore extends BasePgStore { current_burn_height > v2UnlockHeight && previous_burn_height <= v2UnlockHeight; if (generatePoxV2ForceUnlocks) { - const pox2EventQuery = await sql` - SELECT DISTINCT ON (stacker) stacker, ${sql(POX2_EVENT_COLUMNS)} + const pox2EventQuery = await sql` + SELECT DISTINCT ON (stacker) stacker, ${sql(POX_SYNTHETIC_EVENT_COLUMNS)} FROM pox2_events WHERE canonical = true AND microblock_canonical = true AND block_height <= ${block.block_height} AND ( - ( name != ${Pox2EventName.HandleUnlock} AND + ( name != ${SyntheticPoxEventName.HandleUnlock} AND burnchain_unlock_height >= ${current_burn_height}) OR - ( name = ${Pox2EventName.HandleUnlock} AND + ( name = ${SyntheticPoxEventName.HandleUnlock} AND burnchain_unlock_height < ${current_burn_height}) ) ORDER BY stacker, block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC `; for (const row of pox2EventQuery) { - const pox2Event = parseDbPox2Event(row); - if (pox2Event.name !== Pox2EventName.HandleUnlock) { + const pox2Event = parseDbPoxSyntheticEvent(row); + if (pox2Event.name !== SyntheticPoxEventName.HandleUnlock) { const unlockEvent: StxLockEventResult = { locked_amount: pox2Event.locked.toString(), unlock_height: Number(pox2Event.burnchain_unlock_height), @@ -4139,10 +4143,11 @@ export class PgStore extends BasePgStore { } } - // modified copy of pox2 unlocks query let poxV3Unlocks: StxLockEventResult[] = []; - const pox3EventQuery = await sql` - SELECT DISTINCT ON (stacker) stacker, ${sql(POX3_EVENT_COLUMNS)} + const checkPox3Unlocks = v3UnlockHeight === null || current_burn_height < v3UnlockHeight; + if (checkPox3Unlocks) { + const pox3EventQuery = await sql` + SELECT DISTINCT ON (stacker) stacker, ${sql(POX_SYNTHETIC_EVENT_COLUMNS)} FROM pox3_events WHERE canonical = true AND microblock_canonical = true AND block_height <= ${block.block_height} @@ -4151,30 +4156,107 @@ export class PgStore extends BasePgStore { burnchain_unlock_height <= ${current_burn_height} AND burnchain_unlock_height > ${previous_burn_height} AND name IN ${sql([ - Pox2EventName.StackStx, - Pox2EventName.StackIncrease, - Pox2EventName.StackExtend, - Pox2EventName.DelegateStackStx, - Pox2EventName.DelegateStackIncrease, - Pox2EventName.DelegateStackExtend, + SyntheticPoxEventName.StackStx, + SyntheticPoxEventName.StackIncrease, + SyntheticPoxEventName.StackExtend, + SyntheticPoxEventName.DelegateStackStx, + SyntheticPoxEventName.DelegateStackIncrease, + SyntheticPoxEventName.DelegateStackExtend, + ])} + ) OR ( + name = ${SyntheticPoxEventName.HandleUnlock} + AND burnchain_unlock_height < ${current_burn_height} + AND burnchain_unlock_height >= ${previous_burn_height} + ) + ) + ORDER BY stacker, block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC + `; + poxV3Unlocks = pox3EventQuery.map(row => { + const pox3Event = parseDbPoxSyntheticEvent(row); + const unlockEvent: StxLockEventResult = { + locked_amount: pox3Event.locked.toString(), + unlock_height: Number(pox3Event.burnchain_unlock_height), + locked_address: pox3Event.stacker, + block_height: pox3Event.block_height, + tx_index: pox3Event.tx_index, + event_index: pox3Event.event_index, + }; + return unlockEvent; + }); + } + + // modified copy of pox2 and pox3 unlocks query + const poxV3ForceUnlocks: StxLockEventResult[] = []; + const generatePoxV3ForceUnlocks = + v3UnlockHeight !== null && + current_burn_height > v3UnlockHeight && + previous_burn_height <= v3UnlockHeight; + if (generatePoxV3ForceUnlocks) { + const pox3EventQuery = await sql` + SELECT DISTINCT ON (stacker) stacker, ${sql(POX_SYNTHETIC_EVENT_COLUMNS)} + FROM pox3_events + WHERE canonical = true AND microblock_canonical = true + AND block_height <= ${block.block_height} + AND ( + ( name != ${SyntheticPoxEventName.HandleUnlock} AND + burnchain_unlock_height >= ${current_burn_height}) + OR + ( name = ${SyntheticPoxEventName.HandleUnlock} AND + burnchain_unlock_height < ${current_burn_height}) + ) + ORDER BY stacker, block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC + `; + for (const row of pox3EventQuery) { + const pox3Event = parseDbPoxSyntheticEvent(row); + if (pox3Event.name !== SyntheticPoxEventName.HandleUnlock) { + const unlockEvent: StxLockEventResult = { + locked_amount: pox3Event.locked.toString(), + unlock_height: Number(pox3Event.burnchain_unlock_height), + locked_address: pox3Event.stacker, + block_height: pox3Event.block_height, + tx_index: pox3Event.tx_index, + event_index: pox3Event.event_index, + }; + poxV3ForceUnlocks.push(unlockEvent); + } + } + } + + let poxV4Unlocks: StxLockEventResult[] = []; + const pox4EventQuery = await sql` + SELECT DISTINCT ON (stacker) stacker, ${sql(POX_SYNTHETIC_EVENT_COLUMNS)} + FROM pox4_events + WHERE canonical = true AND microblock_canonical = true + AND block_height <= ${block.block_height} + AND ( + ( + burnchain_unlock_height <= ${current_burn_height} + AND burnchain_unlock_height > ${previous_burn_height} + AND name IN ${sql([ + SyntheticPoxEventName.StackStx, + SyntheticPoxEventName.StackIncrease, + SyntheticPoxEventName.StackExtend, + SyntheticPoxEventName.DelegateStackStx, + SyntheticPoxEventName.DelegateStackIncrease, + SyntheticPoxEventName.DelegateStackExtend, ])} ) OR ( - name = ${Pox2EventName.HandleUnlock} + name = ${SyntheticPoxEventName.HandleUnlock} AND burnchain_unlock_height < ${current_burn_height} AND burnchain_unlock_height >= ${previous_burn_height} ) ) ORDER BY stacker, block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC `; - poxV3Unlocks = pox3EventQuery.map(row => { - const pox3Event = parseDbPox2Event(row) as DbPox3Event; + poxV4Unlocks = pox4EventQuery.map(row => { + const pox4Event = parseDbPoxSyntheticEvent(row); const unlockEvent: StxLockEventResult = { - locked_amount: pox3Event.locked.toString(), - unlock_height: Number(pox3Event.burnchain_unlock_height), - locked_address: pox3Event.stacker, - block_height: pox3Event.block_height, - tx_index: pox3Event.tx_index, - event_index: pox3Event.event_index, + locked_amount: pox4Event.locked.toString(), + unlock_height: Number(pox4Event.burnchain_unlock_height), + locked_address: pox4Event.stacker, + block_height: pox4Event.block_height, + tx_index: pox4Event.tx_index, + event_index: pox4Event.event_index, }; return unlockEvent; }); @@ -4194,6 +4276,8 @@ export class PgStore extends BasePgStore { poxV2Unlocks, poxV2ForceUnlocks, poxV3Unlocks, + poxV3ForceUnlocks, + poxV4Unlocks, ]) { unlocks.forEach(row => { const unlockEvent: StxUnlockEvent = { diff --git a/src/datastore/pg-write-store.ts b/src/datastore/pg-write-store.ts index ee60fbba76..8246eafc47 100644 --- a/src/datastore/pg-write-store.ts +++ b/src/datastore/pg-write-store.ts @@ -50,15 +50,15 @@ import { DataStoreAttachmentData, DataStoreAttachmentSubdomainData, DataStoreBnsBlockData, - DbPox2Event, - Pox2EventInsertValues, + PoxSyntheticEventInsertValues, DbTxRaw, DbMempoolTxRaw, DbChainTip, - DbPox3Event, RawEventRequestInsertValues, IndexesState, NftCustodyInsertValues, + DbPoxSyntheticEvent, + PoxSyntheticEventTable, } from './common'; import { BLOCK_COLUMNS, @@ -77,7 +77,7 @@ import { PgNotifier } from './pg-notifier'; import { MIGRATIONS_DIR, PgStore } from './pg-store'; import * as zoneFileParser from 'zone-file'; import { parseResolver, parseZoneFileTxt } from '../event-stream/bns/bns-helpers'; -import { Pox2EventName } from '../pox-helpers'; +import { SyntheticPoxEventName } from '../pox-helpers'; import { logger } from '../logger'; import { PgJsonb, @@ -190,6 +190,7 @@ export class PgWriteStore extends PgStore { namespaces: tx.namespaces.map(e => ({ ...e, canonical: false })), pox2Events: tx.pox2Events.map(e => ({ ...e, canonical: false })), pox3Events: tx.pox3Events.map(e => ({ ...e, canonical: false })), + pox4Events: tx.pox4Events.map(e => ({ ...e, canonical: false })), })); data.minerRewards = data.minerRewards.map(mr => ({ ...mr, canonical: false })); } else { @@ -360,10 +361,13 @@ export class PgWriteStore extends PgStore { contractLogEvents.push(...entry.contractLogEvents); await this.updateBatchSmartContractEvent(sql, entry.tx, entry.contractLogEvents); for (const pox2Event of entry.pox2Events) { - await this.updatePox2Event(sql, entry.tx, pox2Event); + await this.updatePoxSyntheticEvent(sql, entry.tx, 'pox2_events', pox2Event); } for (const pox3Event of entry.pox3Events) { - await this.updatePox3Event(sql, entry.tx, pox3Event); + await this.updatePoxSyntheticEvent(sql, entry.tx, 'pox3_events', pox3Event); + } + for (const pox4Event of entry.pox4Events) { + await this.updatePoxSyntheticEvent(sql, entry.tx, 'pox4_events', pox4Event); } for (const stxLockEvent of entry.stxLockEvents) { await this.updateStxLockEvent(sql, entry.tx, stxLockEvent); @@ -659,6 +663,7 @@ export class PgWriteStore extends PgStore { namespaces: entry.namespaces.map(e => ({ ...e, ready_block: blockHeight })), pox2Events: entry.pox2Events.map(e => ({ ...e, block_height: blockHeight })), pox3Events: entry.pox3Events.map(e => ({ ...e, block_height: blockHeight })), + pox4Events: entry.pox4Events.map(e => ({ ...e, block_height: blockHeight })), }); deployedSmartContracts.push(...entry.smartContracts); contractLogEvents.push(...entry.contractLogEvents); @@ -823,8 +828,13 @@ export class PgWriteStore extends PgStore { logger.info('Updated block zero boot data', tablesUpdates); } - async updatePox2Event(sql: PgSqlClient, tx: DbTx, event: DbPox2Event) { - const values: Pox2EventInsertValues = { + async updatePoxSyntheticEvent( + sql: PgSqlClient, + tx: DbTx, + poxTable: PoxSyntheticEventTable, + event: DbPoxSyntheticEvent + ) { + const values: PoxSyntheticEventInsertValues = { event_index: event.event_index, tx_id: event.tx_id, tx_index: event.tx_index, @@ -858,35 +868,35 @@ export class PgWriteStore extends PgStore { }; // Set event-specific columns switch (event.name) { - case Pox2EventName.HandleUnlock: { + case SyntheticPoxEventName.HandleUnlock: { values.first_cycle_locked = event.data.first_cycle_locked.toString(); values.first_unlocked_cycle = event.data.first_unlocked_cycle.toString(); break; } - case Pox2EventName.StackStx: { + case SyntheticPoxEventName.StackStx: { values.lock_period = event.data.lock_period.toString(); values.lock_amount = event.data.lock_amount.toString(); values.start_burn_height = event.data.start_burn_height.toString(); values.unlock_burn_height = event.data.unlock_burn_height.toString(); break; } - case Pox2EventName.StackIncrease: { + case SyntheticPoxEventName.StackIncrease: { values.increase_by = event.data.increase_by.toString(); values.total_locked = event.data.total_locked.toString(); break; } - case Pox2EventName.StackExtend: { + case SyntheticPoxEventName.StackExtend: { values.extend_count = event.data.extend_count.toString(); values.unlock_burn_height = event.data.unlock_burn_height.toString(); break; } - case Pox2EventName.DelegateStx: { + case SyntheticPoxEventName.DelegateStx: { values.amount_ustx = event.data.amount_ustx.toString(); values.delegate_to = event.data.delegate_to; values.unlock_burn_height = event.data.unlock_burn_height?.toString() ?? null; break; } - case Pox2EventName.DelegateStackStx: { + case SyntheticPoxEventName.DelegateStackStx: { values.lock_period = event.data.lock_period.toString(); values.lock_amount = event.data.lock_amount.toString(); values.start_burn_height = event.data.start_burn_height.toString(); @@ -894,147 +904,44 @@ export class PgWriteStore extends PgStore { values.delegator = event.data.delegator; break; } - case Pox2EventName.DelegateStackIncrease: { + case SyntheticPoxEventName.DelegateStackIncrease: { values.increase_by = event.data.increase_by.toString(); values.total_locked = event.data.total_locked.toString(); values.delegator = event.data.delegator; break; } - case Pox2EventName.DelegateStackExtend: { + case SyntheticPoxEventName.DelegateStackExtend: { values.extend_count = event.data.extend_count.toString(); values.unlock_burn_height = event.data.unlock_burn_height.toString(); values.delegator = event.data.delegator; break; } - case Pox2EventName.StackAggregationCommit: { + case SyntheticPoxEventName.StackAggregationCommit: { values.reward_cycle = event.data.reward_cycle.toString(); values.amount_ustx = event.data.amount_ustx.toString(); break; } - case Pox2EventName.StackAggregationCommitIndexed: { + case SyntheticPoxEventName.StackAggregationCommitIndexed: { values.reward_cycle = event.data.reward_cycle.toString(); values.amount_ustx = event.data.amount_ustx.toString(); break; } - case Pox2EventName.StackAggregationIncrease: { + case SyntheticPoxEventName.StackAggregationIncrease: { values.reward_cycle = event.data.reward_cycle.toString(); values.amount_ustx = event.data.amount_ustx.toString(); break; } - default: { - throw new Error(`Unexpected Pox2 event name: ${(event as DbPox2Event).name}`); - } - } - await sql` - INSERT INTO pox2_events ${sql(values)} - `; - } - - // todo: abstract or copy all types - async updatePox3Event(sql: PgSqlClient, tx: DbTx, event: DbPox3Event) { - const values: Pox2EventInsertValues = { - event_index: event.event_index, - tx_id: event.tx_id, - tx_index: event.tx_index, - block_height: event.block_height, - index_block_hash: tx.index_block_hash, - parent_index_block_hash: tx.parent_index_block_hash, - microblock_hash: tx.microblock_hash, - microblock_sequence: tx.microblock_sequence, - microblock_canonical: tx.microblock_canonical, - canonical: event.canonical, - stacker: event.stacker, - locked: event.locked.toString(), - balance: event.balance.toString(), - burnchain_unlock_height: event.burnchain_unlock_height.toString(), - name: event.name, - pox_addr: event.pox_addr, - pox_addr_raw: event.pox_addr_raw, - first_cycle_locked: null, - first_unlocked_cycle: null, - delegate_to: null, - lock_period: null, - lock_amount: null, - start_burn_height: null, - unlock_burn_height: null, - delegator: null, - increase_by: null, - total_locked: null, - extend_count: null, - reward_cycle: null, - amount_ustx: null, - }; - // Set event-specific columns - switch (event.name) { - case Pox2EventName.HandleUnlock: { - values.first_cycle_locked = event.data.first_cycle_locked.toString(); - values.first_unlocked_cycle = event.data.first_unlocked_cycle.toString(); - break; - } - case Pox2EventName.StackStx: { - values.lock_period = event.data.lock_period.toString(); - values.lock_amount = event.data.lock_amount.toString(); - values.start_burn_height = event.data.start_burn_height.toString(); - values.unlock_burn_height = event.data.unlock_burn_height.toString(); - break; - } - case Pox2EventName.StackIncrease: { - values.increase_by = event.data.increase_by.toString(); - values.total_locked = event.data.total_locked.toString(); - break; - } - case Pox2EventName.StackExtend: { - values.extend_count = event.data.extend_count.toString(); - values.unlock_burn_height = event.data.unlock_burn_height.toString(); - break; - } - case Pox2EventName.DelegateStx: { + case SyntheticPoxEventName.RevokeDelegateStx: { values.amount_ustx = event.data.amount_ustx.toString(); values.delegate_to = event.data.delegate_to; - values.unlock_burn_height = event.data.unlock_burn_height?.toString() ?? null; - break; - } - case Pox2EventName.DelegateStackStx: { - values.lock_period = event.data.lock_period.toString(); - values.lock_amount = event.data.lock_amount.toString(); - values.start_burn_height = event.data.start_burn_height.toString(); - values.unlock_burn_height = event.data.unlock_burn_height.toString(); - values.delegator = event.data.delegator; - break; - } - case Pox2EventName.DelegateStackIncrease: { - values.increase_by = event.data.increase_by.toString(); - values.total_locked = event.data.total_locked.toString(); - values.delegator = event.data.delegator; - break; - } - case Pox2EventName.DelegateStackExtend: { - values.extend_count = event.data.extend_count.toString(); - values.unlock_burn_height = event.data.unlock_burn_height.toString(); - values.delegator = event.data.delegator; - break; - } - case Pox2EventName.StackAggregationCommit: { - values.reward_cycle = event.data.reward_cycle.toString(); - values.amount_ustx = event.data.amount_ustx.toString(); - break; - } - case Pox2EventName.StackAggregationCommitIndexed: { - values.reward_cycle = event.data.reward_cycle.toString(); - values.amount_ustx = event.data.amount_ustx.toString(); - break; - } - case Pox2EventName.StackAggregationIncrease: { - values.reward_cycle = event.data.reward_cycle.toString(); - values.amount_ustx = event.data.amount_ustx.toString(); break; } default: { - throw new Error(`Unexpected Pox3 event name: ${(event as DbPox2Event).name}`); + throw new Error(`Unexpected Pox3 event name: ${(event as DbPoxSyntheticEvent).name}`); } } await sql` - INSERT INTO pox3_events ${sql(values)} + INSERT INTO ${sql(poxTable)} ${sql(values)} `; } @@ -2166,10 +2073,13 @@ export class PgWriteStore extends PgStore { await this.updatePrincipalStxTxs(sql, entry.tx, entry.stxEvents); await this.updateBatchSmartContractEvent(sql, entry.tx, entry.contractLogEvents); for (const pox2Event of entry.pox2Events) { - await this.updatePox2Event(sql, entry.tx, pox2Event); + await this.updatePoxSyntheticEvent(sql, entry.tx, 'pox2_events', pox2Event); } for (const pox3Event of entry.pox3Events) { - await this.updatePox3Event(sql, entry.tx, pox3Event); + await this.updatePoxSyntheticEvent(sql, entry.tx, 'pox3_events', pox3Event); + } + for (const pox4Event of entry.pox4Events) { + await this.updatePoxSyntheticEvent(sql, entry.tx, 'pox4_events', pox4Event); } for (const stxLockEvent of entry.stxLockEvents) { await this.updateStxLockEvent(sql, entry.tx, stxLockEvent); diff --git a/src/event-replay/parquet-based/importers/new-block-importer.ts b/src/event-replay/parquet-based/importers/new-block-importer.ts index 7d00a036de..869d6f3d3d 100644 --- a/src/event-replay/parquet-based/importers/new-block-importer.ts +++ b/src/event-replay/parquet-based/importers/new-block-importer.ts @@ -392,7 +392,7 @@ const populateBatchInserters = (db: PgWriteStore) => { const insertPox2Events = async (dbData: DataStoreBlockUpdateData) => { for (const entry of dbData.txs) { for (const pox2Event of entry.pox2Events) { - await db.updatePox2Event(db.sql, entry.tx, pox2Event); + await db.updatePoxSyntheticEvent(db.sql, entry.tx, 'pox2_events', pox2Event); } } }; @@ -400,7 +400,15 @@ const populateBatchInserters = (db: PgWriteStore) => { const insertPox3Events = async (dbData: DataStoreBlockUpdateData) => { for (const entry of dbData.txs) { for (const pox3Event of entry.pox3Events) { - await db.updatePox3Event(db.sql, entry.tx, pox3Event); + await db.updatePoxSyntheticEvent(db.sql, entry.tx, 'pox3_events', pox3Event); + } + } + }; + + const insertPox4Events = async (dbData: DataStoreBlockUpdateData) => { + for (const entry of dbData.txs) { + for (const pox4Event of entry.pox4Events) { + await db.updatePoxSyntheticEvent(db.sql, entry.tx, 'pox4_events', pox4Event); } } }; @@ -438,6 +446,8 @@ const populateBatchInserters = (db: PgWriteStore) => { insertPox2Events(dbData), // Insert pox3_events insertPox3Events(dbData), + // Insert pox4_events + insertPox4Events(dbData), ]); next(); diff --git a/src/event-stream/event-server.ts b/src/event-stream/event-server.ts index 8fae008295..f33f7b8f2d 100644 --- a/src/event-stream/event-server.ts +++ b/src/event-stream/event-server.ts @@ -35,7 +35,7 @@ import { DataStoreTxEventData, DbMicroblock, DataStoreAttachmentData, - DbPox2Event, + DbPoxSyntheticEvent, DbTxStatus, DbBnsSubdomain, } from '../datastore/common'; @@ -70,10 +70,11 @@ import { getTxDbStatus, } from '../datastore/helpers'; import { handleBnsImport } from '../import-v1'; -import { decodePox2PrintEvent } from './pox2-event-parsing'; +import { decodePoxSyntheticPrintEvent } from './pox2-event-parsing'; import { logger, loggerMiddleware } from '../logger'; import * as zoneFileParser from 'zone-file'; import { hexToBuffer, isProdEnv, stopwatch } from '@hirosystems/api-toolkit'; +import { POX_2_CONTRACT_NAME, POX_3_CONTRACT_NAME, POX_4_CONTRACT_NAME } from '../pox-helpers'; const IBD_PRUNABLE_ROUTES = ['/new_mempool_tx', '/drop_mempool_tx', '/new_microblocks']; @@ -351,6 +352,7 @@ function parseDataStoreTxEventData( namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }; switch (tx.parsed_tx.payload.type_id) { case TxPayloadTypeID.VersionedSmartContract: @@ -436,32 +438,24 @@ function parseDataStoreTxEventData( if (isPoxPrintEvent(event)) { const network = getChainIDNetwork(chainId) === 'mainnet' ? 'mainnet' : 'testnet'; const [, contractName] = event.contract_event.contract_identifier.split('.'); - // todo: switch could be abstracted more switch (contractName) { // pox-1 is handled in custom node events - case 'pox-2': { - const poxEventData = decodePox2PrintEvent(event.contract_event.raw_value, network); + case POX_2_CONTRACT_NAME: + case POX_3_CONTRACT_NAME: + case POX_4_CONTRACT_NAME: { + const poxEventData = decodePoxSyntheticPrintEvent( + event.contract_event.raw_value, + network + ); if (poxEventData === null) break; - logger.debug(`Pox2 event data:`, poxEventData); - const dbPoxEvent: DbPox2Event = { + logger.debug(`Synthetic pox event data for ${contractName}:`, poxEventData); + const dbPoxEvent: DbPoxSyntheticEvent = { ...dbEvent, ...poxEventData, }; dbTx.pox2Events.push(dbPoxEvent); break; } - case 'pox-3': { - const decodePox3PrintEvent = decodePox2PrintEvent; // todo: do we want to copy all pox2 methods for pox3? - const poxEventData = decodePox3PrintEvent(event.contract_event.raw_value, network); - if (poxEventData === null) break; - logger.debug(`Pox3 event data:`, poxEventData); - const dbPoxEvent: DbPox2Event = { - ...dbEvent, - ...poxEventData, - }; - dbTx.pox3Events.push(dbPoxEvent); - break; - } } } @@ -624,6 +618,7 @@ function parseDataStoreTxEventData( tx.stxLockEvents, tx.pox2Events, tx.pox3Events, + tx.pox4Events, ] .flat() .sort((a, b) => a.event_index - b.event_index); diff --git a/src/event-stream/pox2-event-parsing.ts b/src/event-stream/pox2-event-parsing.ts index 66e4e27525..0b0dac559a 100644 --- a/src/event-stream/pox2-event-parsing.ts +++ b/src/event-stream/pox2-event-parsing.ts @@ -1,17 +1,18 @@ import { - DbPox2BaseEventData, - DbPox2DelegateStackExtendEvent, - DbPox2DelegateStackIncreaseEvent, - DbPox2DelegateStackStxEvent, - DbPox2DelegateStxEvent, - DbPox2EventData, - DbPox2HandleUnlockEvent, - DbPox2StackAggregationCommitEvent, - DbPox2StackAggregationCommitIndexedEvent, - DbPox2StackAggregationIncreaseEvent, - DbPox2StackExtendEvent, - DbPox2StackIncreaseEvent, - DbPox2StackStxEvent, + DbPoxSyntheticBaseEventData, + DbPoxSyntheticDelegateStackExtendEvent, + DbPoxSyntheticDelegateStackIncreaseEvent, + DbPoxSyntheticDelegateStackStxEvent, + DbPoxSyntheticDelegateStxEvent, + DbPoxSyntheticEventData, + DbPoxSyntheticHandleUnlockEvent, + DbPoxSyntheticRevokeDelegateStxEvent, + DbPoxSyntheticStackAggregationCommitEvent, + DbPoxSyntheticStackAggregationCommitIndexedEvent, + DbPoxSyntheticStackAggregationIncreaseEvent, + DbPoxSyntheticStackExtendEvent, + DbPoxSyntheticStackIncreaseEvent, + DbPoxSyntheticStackStxEvent, } from '../datastore/common'; import { ClarityTypeID, @@ -29,12 +30,15 @@ import { decodeClarityValue, } from 'stacks-encoding-native-js'; import { poxAddressToBtcAddress } from '@stacks/stacking'; -import { Pox2EventName } from '../pox-helpers'; +import { SyntheticPoxEventName } from '../pox-helpers'; import { logger } from '../logger'; import { bufferToHex, coerceToBuffer } from '@hirosystems/api-toolkit'; function tryClarityPoxAddressToBtcAddress( - poxAddr: Pox2Addr | ClarityValueOptionalSome | ClarityValueOptionalNone, + poxAddr: + | PoxSyntheticEventAddr + | ClarityValueOptionalSome + | ClarityValueOptionalNone, network: 'mainnet' | 'testnet' | 'devnet' | 'mocknet' ): { btcAddr: string | null; raw: Buffer } { let btcAddr: string | null = null; @@ -68,12 +72,12 @@ function tryClarityPoxAddressToBtcAddress( }; } -type Pox2Addr = ClarityValueTuple<{ +type PoxSyntheticEventAddr = ClarityValueTuple<{ hashbytes: ClarityValueBuffer; version: ClarityValueBuffer; }>; -type PoX2EventData = ClarityValueTuple<{ +type PoXSyntheticEventData = ClarityValueTuple<{ name: ClarityValueStringAscii; balance: ClarityValueUInt; stacker: ClarityValuePrincipalStandard | ClarityValuePrincipalContract; @@ -82,68 +86,73 @@ type PoX2EventData = ClarityValueTuple<{ data: ClarityValueTuple; }>; -interface Pox2PrintEventTypes { - [Pox2EventName.HandleUnlock]: { +interface PoxSyntheticPrintEventTypes { + [SyntheticPoxEventName.HandleUnlock]: { 'first-cycle-locked': ClarityValueUInt; 'first-unlocked-cycle': ClarityValueUInt; }; - [Pox2EventName.StackStx]: { + [SyntheticPoxEventName.StackStx]: { 'lock-amount': ClarityValueUInt; 'lock-period': ClarityValueUInt; - 'pox-addr': Pox2Addr; + 'pox-addr': PoxSyntheticEventAddr; 'start-burn-height': ClarityValueUInt; 'unlock-burn-height': ClarityValueUInt; }; - [Pox2EventName.StackIncrease]: { + [SyntheticPoxEventName.StackIncrease]: { 'increase-by': ClarityValueUInt; 'total-locked': ClarityValueUInt; }; - [Pox2EventName.StackExtend]: { + [SyntheticPoxEventName.StackExtend]: { 'extend-count': ClarityValueUInt; 'unlock-burn-height': ClarityValueUInt; - 'pox-addr': Pox2Addr; + 'pox-addr': PoxSyntheticEventAddr; }; - [Pox2EventName.DelegateStx]: { + [SyntheticPoxEventName.DelegateStx]: { 'amount-ustx': ClarityValueUInt; 'delegate-to': ClarityValuePrincipalStandard | ClarityValuePrincipalContract; 'unlock-burn-height': ClarityValueOptionalSome | ClarityValueOptionalNone; - 'pox-addr': Pox2Addr | ClarityValueOptionalNone; + 'pox-addr': PoxSyntheticEventAddr | ClarityValueOptionalNone; }; - [Pox2EventName.DelegateStackStx]: { + [SyntheticPoxEventName.DelegateStackStx]: { 'lock-amount': ClarityValueUInt; 'unlock-burn-height': ClarityValueUInt; - 'pox-addr': Pox2Addr; + 'pox-addr': PoxSyntheticEventAddr; 'start-burn-height': ClarityValueUInt; 'lock-period': ClarityValueUInt; delegator: ClarityValuePrincipalStandard | ClarityValuePrincipalContract; }; - [Pox2EventName.DelegateStackIncrease]: { - 'pox-addr': Pox2Addr; + [SyntheticPoxEventName.DelegateStackIncrease]: { + 'pox-addr': PoxSyntheticEventAddr; 'increase-by': ClarityValueUInt; 'total-locked': ClarityValueUInt; delegator: ClarityValuePrincipalStandard | ClarityValuePrincipalContract; }; - [Pox2EventName.DelegateStackExtend]: { - 'pox-addr': Pox2Addr; + [SyntheticPoxEventName.DelegateStackExtend]: { + 'pox-addr': PoxSyntheticEventAddr; 'unlock-burn-height': ClarityValueUInt; 'extend-count': ClarityValueUInt; delegator: ClarityValuePrincipalStandard | ClarityValuePrincipalContract; }; - [Pox2EventName.StackAggregationCommit]: { - 'pox-addr': Pox2Addr; + [SyntheticPoxEventName.StackAggregationCommit]: { + 'pox-addr': PoxSyntheticEventAddr; 'reward-cycle': ClarityValueUInt; 'amount-ustx': ClarityValueUInt; }; - [Pox2EventName.StackAggregationCommitIndexed]: { - 'pox-addr': Pox2Addr; + [SyntheticPoxEventName.StackAggregationCommitIndexed]: { + 'pox-addr': PoxSyntheticEventAddr; 'reward-cycle': ClarityValueUInt; 'amount-ustx': ClarityValueUInt; }; - [Pox2EventName.StackAggregationIncrease]: { - 'pox-addr': Pox2Addr; + [SyntheticPoxEventName.StackAggregationIncrease]: { + 'pox-addr': PoxSyntheticEventAddr; 'reward-cycle': ClarityValueUInt; 'amount-ustx': ClarityValueUInt; }; + [SyntheticPoxEventName.RevokeDelegateStx]: { + 'amount-ustx': ClarityValueUInt; + 'delegate-to': ClarityValuePrincipalStandard | ClarityValuePrincipalContract; + 'pox-addr': PoxSyntheticEventAddr | ClarityValueOptionalNone; + }; } function clarityPrincipalToFullAddress( @@ -163,10 +172,10 @@ function clarityPrincipalToFullAddress( // https://github.com/stacks-network/stacks-blockchain/pull/3318 const PATCH_EVENT_BALANCES = true; -export function decodePox2PrintEvent( +export function decodePoxSyntheticPrintEvent( rawClarityData: string, network: 'mainnet' | 'testnet' | 'devnet' | 'mocknet' -): DbPox2EventData | null { +): DbPoxSyntheticEventData | null { const decoded = decodeClarityValue(rawClarityData); if (decoded.type_id === ClarityTypeID.ResponseError) { logger.info(`Received ResponseError when decoding Pox2 print event: ${decoded.repr}`); @@ -175,17 +184,17 @@ export function decodePox2PrintEvent( if (decoded.type_id !== ClarityTypeID.ResponseOk) { const valCommon: ClarityValueAbstract = decoded; throw new Error( - `Unexpected PoX2 event Clarity type ID, expected ResponseOk, got ${valCommon.type_id}: ${valCommon.repr}` + `Unexpected PoX synthetic event Clarity type ID, expected ResponseOk, got ${valCommon.type_id}: ${valCommon.repr}` ); } if (decoded.value.type_id !== ClarityTypeID.Tuple) { throw new Error( - `Unexpected PoX2 event Clarity type ID, expected Tuple, got ${decoded.value.type_id}` + `Unexpected PoX synthetic event Clarity type ID, expected Tuple, got ${decoded.value.type_id}` ); } - const opData = (decoded.value as PoX2EventData).data; + const opData = (decoded.value as PoXSyntheticEventData).data; - const baseEventData: DbPox2BaseEventData = { + const baseEventData: DbPoxSyntheticBaseEventData = { stacker: clarityPrincipalToFullAddress(opData.stacker), locked: BigInt(opData.locked.value), balance: BigInt(opData.balance.value), @@ -194,7 +203,7 @@ export function decodePox2PrintEvent( pox_addr_raw: null, }; - const eventName = opData.name.data as keyof Pox2PrintEventTypes; + const eventName = opData.name.data as keyof PoxSyntheticPrintEventTypes; if (opData.name.type_id !== ClarityTypeID.StringAscii) { throw new Error( `Unexpected PoX2 event name type, expected StringAscii, got ${opData.name.type_id}` @@ -210,8 +219,8 @@ export function decodePox2PrintEvent( if ('pox-addr' in eventData) { const eventPoxAddr = eventData['pox-addr'] as - | Pox2Addr - | ClarityValueOptionalSome + | PoxSyntheticEventAddr + | ClarityValueOptionalSome | ClarityValueOptionalNone; const encodedArr = tryClarityPoxAddressToBtcAddress(eventPoxAddr, network); baseEventData.pox_addr = encodedArr.btcAddr; @@ -219,9 +228,9 @@ export function decodePox2PrintEvent( } switch (eventName) { - case Pox2EventName.HandleUnlock: { - const d = eventData as Pox2PrintEventTypes[typeof eventName]; - const parsedData: DbPox2HandleUnlockEvent = { + case SyntheticPoxEventName.HandleUnlock: { + const d = eventData as PoxSyntheticPrintEventTypes[typeof eventName]; + const parsedData: DbPoxSyntheticHandleUnlockEvent = { ...baseEventData, name: eventName, data: { @@ -235,9 +244,9 @@ export function decodePox2PrintEvent( } return parsedData; } - case Pox2EventName.StackStx: { - const d = eventData as Pox2PrintEventTypes[typeof eventName]; - const parsedData: DbPox2StackStxEvent = { + case SyntheticPoxEventName.StackStx: { + const d = eventData as PoxSyntheticPrintEventTypes[typeof eventName]; + const parsedData: DbPoxSyntheticStackStxEvent = { ...baseEventData, name: eventName, data: { @@ -254,9 +263,9 @@ export function decodePox2PrintEvent( } return parsedData; } - case Pox2EventName.StackIncrease: { - const d = eventData as Pox2PrintEventTypes[typeof eventName]; - const parsedData: DbPox2StackIncreaseEvent = { + case SyntheticPoxEventName.StackIncrease: { + const d = eventData as PoxSyntheticPrintEventTypes[typeof eventName]; + const parsedData: DbPoxSyntheticStackIncreaseEvent = { ...baseEventData, name: eventName, data: { @@ -270,9 +279,9 @@ export function decodePox2PrintEvent( } return parsedData; } - case Pox2EventName.StackExtend: { - const d = eventData as Pox2PrintEventTypes[typeof eventName]; - const parsedData: DbPox2StackExtendEvent = { + case SyntheticPoxEventName.StackExtend: { + const d = eventData as PoxSyntheticPrintEventTypes[typeof eventName]; + const parsedData: DbPoxSyntheticStackExtendEvent = { ...baseEventData, name: eventName, data: { @@ -285,9 +294,9 @@ export function decodePox2PrintEvent( } return parsedData; } - case Pox2EventName.DelegateStx: { - const d = eventData as Pox2PrintEventTypes[typeof eventName]; - const parsedData: DbPox2DelegateStxEvent = { + case SyntheticPoxEventName.DelegateStx: { + const d = eventData as PoxSyntheticPrintEventTypes[typeof eventName]; + const parsedData: DbPoxSyntheticDelegateStxEvent = { ...baseEventData, name: eventName, data: { @@ -306,9 +315,9 @@ export function decodePox2PrintEvent( } return parsedData; } - case Pox2EventName.DelegateStackStx: { - const d = eventData as Pox2PrintEventTypes[typeof eventName]; - const parsedData: DbPox2DelegateStackStxEvent = { + case SyntheticPoxEventName.DelegateStackStx: { + const d = eventData as PoxSyntheticPrintEventTypes[typeof eventName]; + const parsedData: DbPoxSyntheticDelegateStackStxEvent = { ...baseEventData, name: eventName, data: { @@ -326,9 +335,9 @@ export function decodePox2PrintEvent( } return parsedData; } - case Pox2EventName.DelegateStackIncrease: { - const d = eventData as Pox2PrintEventTypes[typeof eventName]; - const parsedData: DbPox2DelegateStackIncreaseEvent = { + case SyntheticPoxEventName.DelegateStackIncrease: { + const d = eventData as PoxSyntheticPrintEventTypes[typeof eventName]; + const parsedData: DbPoxSyntheticDelegateStackIncreaseEvent = { ...baseEventData, name: eventName, data: { @@ -343,9 +352,9 @@ export function decodePox2PrintEvent( } return parsedData; } - case Pox2EventName.DelegateStackExtend: { - const d = eventData as Pox2PrintEventTypes[typeof eventName]; - const parsedData: DbPox2DelegateStackExtendEvent = { + case SyntheticPoxEventName.DelegateStackExtend: { + const d = eventData as PoxSyntheticPrintEventTypes[typeof eventName]; + const parsedData: DbPoxSyntheticDelegateStackExtendEvent = { ...baseEventData, name: eventName, data: { @@ -359,9 +368,9 @@ export function decodePox2PrintEvent( } return parsedData; } - case Pox2EventName.StackAggregationCommit: { - const d = eventData as Pox2PrintEventTypes[typeof eventName]; - const parsedData: DbPox2StackAggregationCommitEvent = { + case SyntheticPoxEventName.StackAggregationCommit: { + const d = eventData as PoxSyntheticPrintEventTypes[typeof eventName]; + const parsedData: DbPoxSyntheticStackAggregationCommitEvent = { ...baseEventData, name: eventName, data: { @@ -371,9 +380,9 @@ export function decodePox2PrintEvent( }; return parsedData; } - case Pox2EventName.StackAggregationCommitIndexed: { - const d = eventData as Pox2PrintEventTypes[typeof eventName]; - const parsedData: DbPox2StackAggregationCommitIndexedEvent = { + case SyntheticPoxEventName.StackAggregationCommitIndexed: { + const d = eventData as PoxSyntheticPrintEventTypes[typeof eventName]; + const parsedData: DbPoxSyntheticStackAggregationCommitIndexedEvent = { ...baseEventData, name: eventName, data: { @@ -383,9 +392,9 @@ export function decodePox2PrintEvent( }; return parsedData; } - case Pox2EventName.StackAggregationIncrease: { - const d = eventData as Pox2PrintEventTypes[typeof eventName]; - const parsedData: DbPox2StackAggregationIncreaseEvent = { + case SyntheticPoxEventName.StackAggregationIncrease: { + const d = eventData as PoxSyntheticPrintEventTypes[typeof eventName]; + const parsedData: DbPoxSyntheticStackAggregationIncreaseEvent = { ...baseEventData, name: eventName, data: { @@ -395,7 +404,19 @@ export function decodePox2PrintEvent( }; return parsedData; } + case SyntheticPoxEventName.RevokeDelegateStx: { + const d = eventData as PoxSyntheticPrintEventTypes[typeof eventName]; + const parsedData: DbPoxSyntheticRevokeDelegateStxEvent = { + ...baseEventData, + name: eventName, + data: { + amount_ustx: BigInt(d['amount-ustx'].value), + delegate_to: clarityPrincipalToFullAddress(d['delegate-to']), + }, + }; + return parsedData; + } default: - throw new Error(`Unexpected PoX-2 event data name: ${opData.name.data}`); + throw new Error(`Unexpected PoX synthetic event data name: ${opData.name.data}`); } } diff --git a/src/event-stream/reader.ts b/src/event-stream/reader.ts index 40b7f1eeea..93c3a65ec1 100644 --- a/src/event-stream/reader.ts +++ b/src/event-stream/reader.ts @@ -34,8 +34,8 @@ import { } from 'stacks-encoding-native-js'; import { DbMicroblockPartial, - DbPox2DelegateStxEvent, - DbPox2StackStxEvent, + DbPoxSyntheticDelegateStxEvent, + DbPoxSyntheticStackStxEvent, } from '../datastore/common'; import { NotImplementedError } from '../errors'; import { @@ -62,8 +62,8 @@ import { } from '@stacks/transactions'; import { poxAddressToTuple } from '@stacks/stacking'; import { c32ToB58 } from 'c32check'; -import { decodePox2PrintEvent } from './pox2-event-parsing'; -import { Pox2EventName } from '../pox-helpers'; +import { decodePoxSyntheticPrintEvent } from './pox2-event-parsing'; +import { PoxContractIdentifiers, SyntheticPoxEventName } from '../pox-helpers'; import { principalCV } from '@stacks/transactions/dist/clarity/types/principalCV'; import { logger } from '../logger'; import { bufferToHex, hexToBuffer } from '@hirosystems/api-toolkit'; @@ -345,7 +345,7 @@ function createTransactionFromCoreBtcStxLockEvent( txResult: string, txId: string, /** also pox-3 compatible */ - stxStacksPox2Event: DbPox2StackStxEvent | undefined + stxStacksPox2Event: DbPoxSyntheticStackStxEvent | undefined ): DecodedTxResult { const resultCv = decodeClarityValue< ClarityValueResponse< @@ -450,7 +450,7 @@ function createTransactionFromCoreBtcStxLockEvent( function createTransactionFromCoreBtcDelegateStxEvent( chainId: ChainID, contractEvent: SmartContractEvent, - decodedEvent: DbPox2DelegateStxEvent, + decodedEvent: DbPoxSyntheticDelegateStxEvent, txResult: string, txId: string ): DecodedTxResult { @@ -660,7 +660,7 @@ export function parseMessageTransaction( ) .map(e => { const network = getChainIDNetwork(chainId); - const decodedEvent = decodePox2PrintEvent(e.contract_event.raw_value, network); + const decodedEvent = decodePoxSyntheticPrintEvent(e.contract_event.raw_value, network); if (decodedEvent) { return { contractEvent: e, @@ -683,7 +683,7 @@ export function parseMessageTransaction( txSender = stxTransferEvent.stx_transfer_event.sender; } else if (stxLockEvent) { const stxStacksPoxEvent = - poxEvent?.decodedEvent.name === Pox2EventName.StackStx + poxEvent?.decodedEvent.name === SyntheticPoxEventName.StackStx ? poxEvent.decodedEvent : undefined; rawTx = createTransactionFromCoreBtcStxLockEvent( @@ -695,7 +695,7 @@ export function parseMessageTransaction( stxStacksPoxEvent ); txSender = stxLockEvent.stx_lock_event.locked_address; - } else if (poxEvent && poxEvent.decodedEvent.name === Pox2EventName.DelegateStx) { + } else if (poxEvent && poxEvent.decodedEvent.name === SyntheticPoxEventName.DelegateStx) { rawTx = createTransactionFromCoreBtcDelegateStxEvent( chainId, poxEvent.contractEvent, @@ -845,10 +845,5 @@ export function parseMessageTransaction( export function isPoxPrintEvent(event: SmartContractEvent): boolean { if (event.contract_event.topic !== 'print') return false; - - const [address, name] = event.contract_event.contract_identifier.split('.'); - return ( - (address == BootContractAddress.mainnet || address == BootContractAddress.testnet) && - name.startsWith('pox') - ); + return PoxContractIdentifiers.includes(event.contract_event.contract_identifier); } diff --git a/src/pox-helpers.ts b/src/pox-helpers.ts index 1fa196f2a4..7cb76f088e 100644 --- a/src/pox-helpers.ts +++ b/src/pox-helpers.ts @@ -1,4 +1,5 @@ -export const enum Pox2EventName { +/** Names for synthetic events generated for the pox-2, pox-3, and pox-4 contracts */ +export enum SyntheticPoxEventName { HandleUnlock = 'handle-unlock', StackStx = 'stack-stx', StackIncrease = 'stack-increase', @@ -10,9 +11,43 @@ export const enum Pox2EventName { StackAggregationCommit = 'stack-aggregation-commit', StackAggregationCommitIndexed = 'stack-aggregation-commit-indexed', StackAggregationIncrease = 'stack-aggregation-increase', + RevokeDelegateStx = 'revoke-delegate-stx', // Only guaranteed to be present in pox-4 } -export const enum Pox2ContractIdentifer { - mainnet = 'SP000000000000000000002Q6VF78.pox-2', - testnet = 'ST000000000000000000002AMW42H.pox-2', -} +const BOOT_ADDR_MAINNET = 'SP000000000000000000002Q6VF78'; +const BOOT_ADDR_TESTNET = 'ST000000000000000000002AMW42H'; + +export const POX_1_CONTRACT_NAME = 'pox'; +export const POX_2_CONTRACT_NAME = 'pox-2'; +export const POX_3_CONTRACT_NAME = 'pox-3'; +export const POX_4_CONTRACT_NAME = 'pox-4'; + +export const PoxContractNames = [ + POX_1_CONTRACT_NAME, + POX_2_CONTRACT_NAME, + POX_3_CONTRACT_NAME, + POX_4_CONTRACT_NAME, +] as const; + +export const PoxContractIdentifier = { + pox1: { + mainnet: `${BOOT_ADDR_MAINNET}.${POX_1_CONTRACT_NAME}`, + testnet: `${BOOT_ADDR_TESTNET}.${POX_1_CONTRACT_NAME}`, + }, + pox2: { + mainnet: `${BOOT_ADDR_MAINNET}.${POX_2_CONTRACT_NAME}`, + testnet: `${BOOT_ADDR_TESTNET}.${POX_2_CONTRACT_NAME}`, + }, + pox3: { + mainnet: `${BOOT_ADDR_MAINNET}.${POX_3_CONTRACT_NAME}`, + testnet: `${BOOT_ADDR_TESTNET}.${POX_3_CONTRACT_NAME}`, + }, + pox4: { + mainnet: `${BOOT_ADDR_MAINNET}.${POX_4_CONTRACT_NAME}`, + testnet: `${BOOT_ADDR_TESTNET}.${POX_4_CONTRACT_NAME}`, + }, +} as const; + +export const PoxContractIdentifiers = Object.values(PoxContractIdentifier).flatMap( + Object.values +) as string[]; diff --git a/src/rosetta/rosetta-helpers.ts b/src/rosetta/rosetta-helpers.ts index 5427109c1f..c00c3ff908 100644 --- a/src/rosetta/rosetta-helpers.ts +++ b/src/rosetta/rosetta-helpers.ts @@ -30,12 +30,7 @@ import { getTxTypeString, parseContractCallMetadata, } from '../api/controllers/db-controller'; -import { - PoxContractIdentifier, - RosettaConstants, - RosettaNetworks, - RosettaOperationType, -} from '../api/rosetta-constants'; +import { RosettaConstants, RosettaNetworks, RosettaOperationType } from '../api/rosetta-constants'; import { BaseTx, DbAssetEventTypeId, @@ -78,6 +73,7 @@ import { parseRecoverableSignatureVrs } from '@stacks/common'; import { logger } from '../logger'; import { hexToBuffer } from '@hirosystems/api-toolkit'; import { RosettaFtMetadata, RosettaFtMetadataClient } from './rosetta-ft-metadata-client'; +import { PoxContractIdentifiers } from '../pox-helpers'; enum CoinAction { CoinSpent = 'coin_spent', @@ -692,14 +688,7 @@ async function makeCallContractOperation( case 'stack-stx': case 'delegate-stx': case 'revoke-delegate-stx': - if ( - stackContractCall.contract_call.contract_id === PoxContractIdentifier.pox1.testnet || - stackContractCall.contract_call.contract_id === PoxContractIdentifier.pox1.mainnet || - stackContractCall.contract_call.contract_id === PoxContractIdentifier.pox2.testnet || - stackContractCall.contract_call.contract_id === PoxContractIdentifier.pox2.mainnet || - stackContractCall.contract_call.contract_id === PoxContractIdentifier.pox3.testnet || - stackContractCall.contract_call.contract_id === PoxContractIdentifier.pox3.mainnet - ) { + if (PoxContractIdentifiers.includes(stackContractCall.contract_call.contract_id)) { parseStackingContractCall(contractCallOp, stackContractCall); } else { parseGenericContractCall(contractCallOp, tx); diff --git a/src/test-utils/test-builders.ts b/src/test-utils/test-builders.ts index 261b37c8f4..98f2db2a63 100644 --- a/src/test-utils/test-builders.ts +++ b/src/test-utils/test-builders.ts @@ -247,6 +247,7 @@ function testTx(args?: TestTxArgs): DataStoreTxEventData { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }; return data; } From f1e3ff07caf0a4ac90f234617b50d31ce6e279c8 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sat, 9 Dec 2023 14:02:55 +0100 Subject: [PATCH 04/23] chore: rename pox-2 file --- src/event-stream/event-server.ts | 2 +- .../{pox2-event-parsing.ts => pox-event-parsing.ts} | 0 src/event-stream/reader.ts | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/event-stream/{pox2-event-parsing.ts => pox-event-parsing.ts} (100%) diff --git a/src/event-stream/event-server.ts b/src/event-stream/event-server.ts index f33f7b8f2d..99095b9661 100644 --- a/src/event-stream/event-server.ts +++ b/src/event-stream/event-server.ts @@ -70,7 +70,7 @@ import { getTxDbStatus, } from '../datastore/helpers'; import { handleBnsImport } from '../import-v1'; -import { decodePoxSyntheticPrintEvent } from './pox2-event-parsing'; +import { decodePoxSyntheticPrintEvent } from './pox-event-parsing'; import { logger, loggerMiddleware } from '../logger'; import * as zoneFileParser from 'zone-file'; import { hexToBuffer, isProdEnv, stopwatch } from '@hirosystems/api-toolkit'; diff --git a/src/event-stream/pox2-event-parsing.ts b/src/event-stream/pox-event-parsing.ts similarity index 100% rename from src/event-stream/pox2-event-parsing.ts rename to src/event-stream/pox-event-parsing.ts diff --git a/src/event-stream/reader.ts b/src/event-stream/reader.ts index 93c3a65ec1..bd8eff3c09 100644 --- a/src/event-stream/reader.ts +++ b/src/event-stream/reader.ts @@ -62,7 +62,7 @@ import { } from '@stacks/transactions'; import { poxAddressToTuple } from '@stacks/stacking'; import { c32ToB58 } from 'c32check'; -import { decodePoxSyntheticPrintEvent } from './pox2-event-parsing'; +import { decodePoxSyntheticPrintEvent } from './pox-event-parsing'; import { PoxContractIdentifiers, SyntheticPoxEventName } from '../pox-helpers'; import { principalCV } from '@stacks/transactions/dist/clarity/types/principalCV'; import { logger } from '../logger'; From cd7b3ff59865235742683e07840a08457b61617c Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sat, 9 Dec 2023 15:59:12 +0100 Subject: [PATCH 05/23] chore: simplified pox route paths --- src/api/init.ts | 22 ++++++-- src/api/routes/{pox3.ts => pox.ts} | 24 ++++++--- src/api/routes/pox2.ts | 87 ------------------------------ 3 files changed, 35 insertions(+), 98 deletions(-) rename src/api/routes/{pox3.ts => pox.ts} (83%) delete mode 100644 src/api/routes/pox2.ts diff --git a/src/api/init.ts b/src/api/init.ts index d2494e1334..b6da26e27b 100644 --- a/src/api/init.ts +++ b/src/api/init.ts @@ -1,5 +1,6 @@ import { Server, createServer } from 'http'; import { Socket } from 'net'; +import * as querystring from 'querystring'; import * as express from 'express'; import { v4 as uuid } from 'uuid'; import * as cors from 'cors'; @@ -40,8 +41,7 @@ import * as fs from 'fs'; import { PgStore } from '../datastore/pg-store'; import { PgWriteStore } from '../datastore/pg-write-store'; import { WebSocketTransmitter } from './routes/ws/web-socket-transmitter'; -import { createPox2EventsRouter } from './routes/pox2'; -import { createPox3EventsRouter } from './routes/pox3'; +import { createPoxEventsRouter } from './routes/pox'; import { createStackingRouter } from './routes/stacking'; import { logger, loggerMiddleware } from '../logger'; import { SERVER_VERSION, isPgConnectionError, isProdEnv, waiter } from '@hirosystems/api-toolkit'; @@ -195,8 +195,22 @@ export async function startApiServer(opts: { router.use('/status', createStatusRouter(datastore)); router.use('/fee_rate', createFeeRateRouter(datastore)); router.use('/tokens', createTokenRouter(datastore)); - router.use('/pox2_events', createPox2EventsRouter(datastore)); - router.use('/pox3_events', createPox3EventsRouter(datastore)); + + // These could be defined in one route but a url reporting library breaks with regex in middleware paths + router.use('/pox2', createPoxEventsRouter(datastore, 'pox2')); + router.use('/pox3', createPoxEventsRouter(datastore, 'pox3')); + router.use('/pox4', createPoxEventsRouter(datastore, 'pox4')); + const legacyPoxPathRouter: express.RequestHandler = (req, res) => { + // Redirect old pox routes paths to new one above + const newPath = req.path === '/' ? '/events' : req.path; + const query = querystring.stringify(req.query as Record); + const baseUrl = req.baseUrl.replace(/(pox[23])_events/, '$1'); + const redirectPath = `${baseUrl}${newPath}${query ? `?${query}` : ''}`; + return res.redirect(redirectPath); + }; + router.use('/pox2_events', legacyPoxPathRouter); + router.use('/pox3_events', legacyPoxPathRouter); + if (getChainIDNetwork(chainId) === 'testnet' && writeDatastore) { router.use('/faucets', createFaucetRouter(writeDatastore)); } diff --git a/src/api/routes/pox3.ts b/src/api/routes/pox.ts similarity index 83% rename from src/api/routes/pox3.ts rename to src/api/routes/pox.ts index 4a1c45c52d..d657942a0f 100644 --- a/src/api/routes/pox3.ts +++ b/src/api/routes/pox.ts @@ -1,16 +1,26 @@ import * as express from 'express'; import { asyncHandler } from '../async-handler'; - +import { getPagingQueryLimit, parsePagingQueryInput, ResourceType } from '../pagination'; import { PgStore } from '../../datastore/pg-store'; import { parsePoxSyntheticEvent } from '../controllers/db-controller'; -import { ResourceType, getPagingQueryLimit, parsePagingQueryInput } from '../pagination'; import { validatePrincipal, validateRequestHexInput } from '../query-helpers'; -export function createPox3EventsRouter(db: PgStore): express.Router { +export function createPoxEventsRouter( + db: PgStore, + poxVersion: 'pox2' | 'pox3' | 'pox4' +): express.Router { const router = express.Router(); + const poxTable = ( + { + pox2: 'pox2_events', + pox3: 'pox3_events', + pox4: 'pox4_events', + } as const + )[poxVersion]; + router.get( - '/', + '/events', asyncHandler(async (req, res) => { const limit = getPagingQueryLimit(ResourceType.Pox2Event, req.query.limit); const offset = parsePagingQueryInput(req.query.offset ?? 0); @@ -18,7 +28,7 @@ export function createPox3EventsRouter(db: PgStore): express.Router { const queryResults = await db.getPoxSyntheticEvents({ offset, limit, - poxTable: 'pox3_events', + poxTable, }); const parsedResult = queryResults.map(r => parsePoxSyntheticEvent(r)); const response = { @@ -37,7 +47,7 @@ export function createPox3EventsRouter(db: PgStore): express.Router { validateRequestHexInput(tx_id); const queryResults = await db.getPoxSyntheticEventsForTx({ txId: tx_id, - poxTable: 'pox3_events', + poxTable, }); if (!queryResults.found) { res.status(404).json({ error: `could not find transaction by ID ${tx_id}` }); @@ -58,7 +68,7 @@ export function createPox3EventsRouter(db: PgStore): express.Router { validatePrincipal(principal); const queryResults = await db.getPoxSyntheticEventsForStacker({ principal, - poxTable: 'pox3_events', + poxTable, }); if (!queryResults.found) { res.status(404).json({ error: `could not find principal ${principal}` }); diff --git a/src/api/routes/pox2.ts b/src/api/routes/pox2.ts deleted file mode 100644 index a0786a09da..0000000000 --- a/src/api/routes/pox2.ts +++ /dev/null @@ -1,87 +0,0 @@ -import * as express from 'express'; -import { asyncHandler } from '../async-handler'; -import { - BurnchainReward, - BurnchainRewardListResponse, - BurnchainRewardSlotHolder, - BurnchainRewardSlotHolderListResponse, - BurnchainRewardsTotal, -} from '@stacks/stacks-blockchain-api-types'; - -import { isValidBitcoinAddress, tryConvertC32ToBtc } from '../../helpers'; -import { InvalidRequestError, InvalidRequestErrorType } from '../../errors'; -import { getPagingQueryLimit, parsePagingQueryInput, ResourceType } from '../pagination'; -import { PgStore } from '../../datastore/pg-store'; -import { parsePoxSyntheticEvent } from '../controllers/db-controller'; -import { validatePrincipal, validateRequestHexInput } from '../query-helpers'; - -export function createPox2EventsRouter(db: PgStore): express.Router { - const router = express.Router(); - - router.get( - '/', - asyncHandler(async (req, res) => { - const limit = getPagingQueryLimit(ResourceType.Pox2Event, req.query.limit); - const offset = parsePagingQueryInput(req.query.offset ?? 0); - - const queryResults = await db.getPoxSyntheticEvents({ - offset, - limit, - poxTable: 'pox2_events', - }); - const parsedResult = queryResults.map(r => parsePoxSyntheticEvent(r)); - const response = { - limit, - offset, - results: parsedResult, - }; - res.json(response); - }) - ); - - // TODO: this should probably be a tx route e.g. /extended/v1/tx/:tx_id/pox2_events - router.get( - '/tx/:tx_id', - asyncHandler(async (req, res) => { - const { tx_id } = req.params; - validateRequestHexInput(tx_id); - const queryResults = await db.getPoxSyntheticEventsForTx({ - txId: tx_id, - poxTable: 'pox2_events', - }); - if (!queryResults.found) { - res.status(404).json({ error: `could not find transaction by ID ${tx_id}` }); - return; - } - const parsedResult = queryResults.result.map(r => parsePoxSyntheticEvent(r)); - const response = { - results: parsedResult, - }; - res.json(response); - }) - ); - - // TODO: this should probably be an account route e.g. /extended/v1/address/:stx_address/pox2_events - router.get( - '/stacker/:principal', - asyncHandler(async (req, res) => { - const { principal } = req.params; - validatePrincipal(principal); - const queryResults = await db.getPoxSyntheticEventsForStacker({ - principal, - poxTable: 'pox2_events', - }); - if (!queryResults.found) { - res.status(404).json({ error: `could not find principal ${principal}` }); - return; - } - const parsedResult = queryResults.result.map(r => parsePoxSyntheticEvent(r)); - const response = { - results: parsedResult, - }; - res.json(response); - }) - ); - - return router; -} From cddfaa2604f855e0ba78a235abf6b5e044f367dd Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sat, 9 Dec 2023 16:13:35 +0100 Subject: [PATCH 06/23] feat: add migration to create pox4_events table --- migrations/1702134678728_pox_4_events.js | 203 ++++++++++++++++++ src/tests-2.4/pox-3-burnchain-delegate-stx.ts | 4 +- 2 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 migrations/1702134678728_pox_4_events.js diff --git a/migrations/1702134678728_pox_4_events.js b/migrations/1702134678728_pox_4_events.js new file mode 100644 index 0000000000..788edd483c --- /dev/null +++ b/migrations/1702134678728_pox_4_events.js @@ -0,0 +1,203 @@ +/** @param { import("node-pg-migrate").MigrationBuilder } pgm */ +exports.up = pgm => { + // Adds pox4_events table which matches previous pox2_events table + pgm.createTable('pox4_events', { + id: { + type: 'bigserial', + primaryKey: true, + }, + event_index: { + type: 'integer', + notNull: true, + }, + tx_id: { + notNull: true, + type: 'bytea', + }, + tx_index: { + type: 'smallint', + notNull: true, + }, + block_height: { + type: 'integer', + notNull: true, + }, + index_block_hash: { + type: 'bytea', + notNull: true, + }, + parent_index_block_hash: { + type: 'bytea', + notNull: true, + }, + microblock_hash: { + type: 'bytea', + notNull: true, + }, + microblock_sequence: { + type: 'integer', + notNull: true, + }, + microblock_canonical: { + type: 'boolean', + notNull: true, + }, + canonical: { + type: 'boolean', + notNull: true, + }, + stacker: { + type: 'string', + notNull: true, + }, + locked: { + type: 'numeric', + notNull: true, + }, + balance: { + type: 'numeric', + notNull: true, + }, + burnchain_unlock_height: { + type: 'bigint', + notNull: true, + }, + name: { + type: 'string', + notNull: true, + }, + pox_addr: { + type: 'string', + }, + pox_addr_raw: { + type: 'bytea', + }, + first_cycle_locked: { + // unique to handle-unlock + type: 'numeric', + }, + first_unlocked_cycle: { + // unique to handle-unlock + type: 'numeric', + }, + delegate_to: { + // unique to delegate-stx + type: 'string', + }, + lock_period: { + // unique to stack-stx, delegate-stack-stx + type: 'numeric', + }, + lock_amount: { + // unique to stack-stx, delegate-stack-stx + type: 'numeric', + }, + start_burn_height: { + // unique to stack-stx, delegate-stack-stx + type: 'numeric', + }, + unlock_burn_height: { + // unique to stack-stx, stack-extend, delegate-stack-stx, delegate-stack-extend, delegate-stx + type: 'numeric', + }, + delegator: { + // unique to delegate-stack-stx, delegate-stack-increase, delegate-stack-extend + type: 'string', + }, + increase_by: { + // unique to stack-increase, delegate-stack-increase + type: 'numeric', + }, + total_locked: { + // unique to stack-increase, delegate-stack-increase + type: 'numeric', + }, + extend_count: { + // unique to stack-extend, delegate-stack-extend + type: 'numeric', + }, + reward_cycle: { + // unique to stack-aggregation-* + type: 'numeric', + }, + amount_ustx: { + // unique to stack-aggregation-*, delegate-stx + type: 'numeric', + }, + }); + + pgm.addConstraint( + 'pox4_events', + 'valid_event_specific_columns', + `CHECK ( + CASE name + WHEN 'handle-unlock' THEN + first_cycle_locked IS NOT NULL AND + first_unlocked_cycle IS NOT NULL + WHEN 'stack-stx' THEN + lock_period IS NOT NULL AND + lock_amount IS NOT NULL AND + start_burn_height IS NOT NULL AND + unlock_burn_height IS NOT NULL + WHEN 'stack-increase' THEN + increase_by IS NOT NULL AND + total_locked IS NOT NULL + WHEN 'stack-extend' THEN + extend_count IS NOT NULL AND + unlock_burn_height IS NOT NULL + WHEN 'delegate-stx' THEN + amount_ustx IS NOT NULL AND + delegate_to IS NOT NULL + WHEN 'delegate-stack-stx' THEN + lock_period IS NOT NULL AND + lock_amount IS NOT NULL AND + start_burn_height IS NOT NULL AND + unlock_burn_height IS NOT NULL AND + delegator IS NOT NULL + WHEN 'delegate-stack-increase' THEN + increase_by IS NOT NULL AND + total_locked IS NOT NULL AND + delegator IS NOT NULL + WHEN 'delegate-stack-extend' THEN + extend_count IS NOT NULL AND + unlock_burn_height IS NOT NULL AND + delegator IS NOT NULL + WHEN 'stack-aggregation-commit' THEN + reward_cycle IS NOT NULL AND + amount_ustx IS NOT NULL + WHEN 'stack-aggregation-commit-indexed' THEN + reward_cycle IS NOT NULL AND + amount_ustx IS NOT NULL + WHEN 'stack-aggregation-increase' THEN + reward_cycle IS NOT NULL AND + amount_ustx IS NOT NULL + ELSE false + END + )` + ); + + pgm.createIndex('pox4_events', [ + { name: 'block_height', sort: 'DESC' }, + { name: 'microblock_sequence', sort: 'DESC' }, + { name: 'tx_index', sort: 'DESC' }, + { name: 'event_index', sort: 'DESC' }, + ]); + + pgm.createIndex('pox4_events', 'tx_id'); + pgm.createIndex('pox4_events', 'index_block_hash'); + pgm.createIndex('pox4_events', 'microblock_hash'); + + pgm.createIndex('pox4_events', 'stacker'); + pgm.createIndex('pox4_events', 'burnchain_unlock_height'); + pgm.createIndex('pox4_events', 'pox_addr'); + pgm.createIndex('pox4_events', 'delegator'); + pgm.createIndex('pox4_events', 'name'); + + pgm.createIndex('pox4_events', 'delegate_to'); + pgm.createIndex('pox4_events', 'unlock_burn_height'); +}; + +/** @param { import("node-pg-migrate").MigrationBuilder } pgm */ +exports.down = pgm => { + pgm.dropTable('pox4_events'); +}; diff --git a/src/tests-2.4/pox-3-burnchain-delegate-stx.ts b/src/tests-2.4/pox-3-burnchain-delegate-stx.ts index 4d1fa5e813..11d27b945b 100644 --- a/src/tests-2.4/pox-3-burnchain-delegate-stx.ts +++ b/src/tests-2.4/pox-3-burnchain-delegate-stx.ts @@ -35,7 +35,7 @@ import { ApiServer } from '../api/init'; import { StacksNetwork } from '@stacks/network'; import { RPCClient } from 'rpc-bitcoin'; import * as supertest from 'supertest'; -import { Pox2ContractIdentifer } from '../pox-helpers'; +import { PoxContractIdentifier } from '../pox-helpers'; import { ClarityValueUInt, decodeClarityValue } from 'stacks-encoding-native-js'; import { decodeBtcAddress } from '@stacks/stacking'; import { timeout } from '@hirosystems/api-toolkit'; @@ -463,7 +463,7 @@ describe('PoX-3 - Stack using Bitcoin-chain ops', () => { expect(txObj.tx_type).toBe('contract_call'); expect(txObj.tx_status).toBe('success'); expect(txObj.sender_address).toBe(account.stxAddr); - expect(txObj.contract_call.contract_id).toBe(Pox2ContractIdentifer.testnet); + expect(txObj.contract_call.contract_id).toBe(PoxContractIdentifier.pox2.testnet); expect(txObj.contract_call.function_name).toBe('stack-stx'); const callArg1 = txObj.contract_call.function_args![0]; From 9f1541b1939bdf24a4506c17accb8ac3e0609efc Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sat, 9 Dec 2023 16:24:47 +0100 Subject: [PATCH 07/23] test: follow redirects in test fetch helper --- src/test-utils/test-helpers.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test-utils/test-helpers.ts b/src/test-utils/test-helpers.ts index 4e70d5696a..7f35011ff0 100644 --- a/src/test-utils/test-helpers.ts +++ b/src/test-utils/test-helpers.ts @@ -416,8 +416,12 @@ export async function standByForAccountUnlock(address: string): Promise { } } -export async function fetchGet(endpoint: string) { +export async function fetchGet(endpoint: string): Promise { const result = await supertest(testEnv.api.server).get(endpoint); + // Follow redirects + if (result.status >= 300 && result.status < 400) { + return await fetchGet(result.header.location as string); + } expect(result.status).toBe(200); expect(result.type).toBe('application/json'); return result.body as TRes; From bed4c6ea0889a56bf3fe7f1f3e32bf41f97cdca3 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sat, 9 Dec 2023 16:46:50 +0100 Subject: [PATCH 08/23] chore: fix storing different pox version events --- src/event-stream/event-server.ts | 38 ++++++++++++++++++--------- src/event-stream/pox-event-parsing.ts | 6 ++--- src/event-stream/reader.ts | 2 +- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/event-stream/event-server.ts b/src/event-stream/event-server.ts index 99095b9661..7f123f7a29 100644 --- a/src/event-stream/event-server.ts +++ b/src/event-stream/event-server.ts @@ -438,23 +438,37 @@ function parseDataStoreTxEventData( if (isPoxPrintEvent(event)) { const network = getChainIDNetwork(chainId) === 'mainnet' ? 'mainnet' : 'testnet'; const [, contractName] = event.contract_event.contract_identifier.split('.'); - switch (contractName) { - // pox-1 is handled in custom node events - case POX_2_CONTRACT_NAME: - case POX_3_CONTRACT_NAME: - case POX_4_CONTRACT_NAME: { - const poxEventData = decodePoxSyntheticPrintEvent( - event.contract_event.raw_value, - network - ); - if (poxEventData === null) break; + // pox-1 is handled in custom node events + const processSyntheticEvent = [ + POX_2_CONTRACT_NAME, + POX_3_CONTRACT_NAME, + POX_4_CONTRACT_NAME, + ].includes(contractName); + if (processSyntheticEvent) { + const poxEventData = decodePoxSyntheticPrintEvent( + event.contract_event.raw_value, + network + ); + if (poxEventData !== null) { logger.debug(`Synthetic pox event data for ${contractName}:`, poxEventData); const dbPoxEvent: DbPoxSyntheticEvent = { ...dbEvent, ...poxEventData, }; - dbTx.pox2Events.push(dbPoxEvent); - break; + switch (contractName) { + case POX_2_CONTRACT_NAME: { + dbTx.pox2Events.push(dbPoxEvent); + break; + } + case POX_3_CONTRACT_NAME: { + dbTx.pox3Events.push(dbPoxEvent); + break; + } + case POX_4_CONTRACT_NAME: { + dbTx.pox4Events.push(dbPoxEvent); + break; + } + } } } } diff --git a/src/event-stream/pox-event-parsing.ts b/src/event-stream/pox-event-parsing.ts index 0b0dac559a..4581988f0a 100644 --- a/src/event-stream/pox-event-parsing.ts +++ b/src/event-stream/pox-event-parsing.ts @@ -178,7 +178,7 @@ export function decodePoxSyntheticPrintEvent( ): DbPoxSyntheticEventData | null { const decoded = decodeClarityValue(rawClarityData); if (decoded.type_id === ClarityTypeID.ResponseError) { - logger.info(`Received ResponseError when decoding Pox2 print event: ${decoded.repr}`); + logger.info(`Received ResponseError when decoding Pox synthetic print event: ${decoded.repr}`); return null; } if (decoded.type_id !== ClarityTypeID.ResponseOk) { @@ -206,14 +206,14 @@ export function decodePoxSyntheticPrintEvent( const eventName = opData.name.data as keyof PoxSyntheticPrintEventTypes; if (opData.name.type_id !== ClarityTypeID.StringAscii) { throw new Error( - `Unexpected PoX2 event name type, expected StringAscii, got ${opData.name.type_id}` + `Unexpected PoX synthetic event name type, expected StringAscii, got ${opData.name.type_id}` ); } const eventData = opData.data.data; if (opData.data.type_id !== ClarityTypeID.Tuple) { throw new Error( - `Unexpected PoX2 event data payload type, expected Tuple, got ${opData.data.type_id}` + `Unexpected PoX synthetic event data payload type, expected Tuple, got ${opData.data.type_id}` ); } diff --git a/src/event-stream/reader.ts b/src/event-stream/reader.ts index bd8eff3c09..1a2eb06374 100644 --- a/src/event-stream/reader.ts +++ b/src/event-stream/reader.ts @@ -652,7 +652,7 @@ export function parseMessageTransaction( (e): e is StxMintEvent => e.type === CoreNodeEventType.StxMintEvent ); - // pox-2 and pox-3 compatible events + // pox-2, pox-3, and pox-4 compatible events const poxEvent = events .filter( (e): e is SmartContractEvent => From 85ec9744081f9b48fc5b64cce48f5abcfcffb8cf Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sat, 9 Dec 2023 17:00:47 +0100 Subject: [PATCH 09/23] test: fix tests with missing pox4_events --- src/tests/address-tests.ts | 8 ++++++++ src/tests/block-tests.ts | 2 ++ src/tests/cache-control-tests.ts | 2 ++ src/tests/datastore-tests.ts | 25 +++++++++++++++++++++++++ src/tests/mempool-tests.ts | 1 + src/tests/microblock-tests.ts | 3 +++ src/tests/other-tests.ts | 1 + src/tests/search-tests.ts | 4 ++++ src/tests/smart-contract-tests.ts | 6 ++++++ src/tests/tx-tests.ts | 17 +++++++++++++++++ 10 files changed, 69 insertions(+) diff --git a/src/tests/address-tests.ts b/src/tests/address-tests.ts index 4e34f9d3fd..c52ffef9ed 100644 --- a/src/tests/address-tests.ts +++ b/src/tests/address-tests.ts @@ -212,6 +212,7 @@ describe('address tests', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], })), }); @@ -1169,6 +1170,7 @@ describe('address tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], } as DataStoreTxEventData; }); dataStoreTxs.push({ @@ -1183,6 +1185,7 @@ describe('address tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }); dataStoreTxs.push({ tx: { ...contractCall, raw_tx: '0x' }, @@ -1209,6 +1212,7 @@ describe('address tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }); await db.update({ block: block, @@ -2129,6 +2133,7 @@ describe('address tests', () => { smartContracts: [smartContract], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -2378,6 +2383,7 @@ describe('address tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -2484,6 +2490,7 @@ describe('address tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -2613,6 +2620,7 @@ describe('address tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }); } await db.updateMicroblocks(mbData); diff --git a/src/tests/block-tests.ts b/src/tests/block-tests.ts index 980093e012..1ac317a188 100644 --- a/src/tests/block-tests.ts +++ b/src/tests/block-tests.ts @@ -480,6 +480,7 @@ describe('block tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, { tx: dbTx2, @@ -493,6 +494,7 @@ describe('block tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }; diff --git a/src/tests/cache-control-tests.ts b/src/tests/cache-control-tests.ts index da9e0e826b..e27211ac87 100644 --- a/src/tests/cache-control-tests.ts +++ b/src/tests/cache-control-tests.ts @@ -147,6 +147,7 @@ describe('cache-control tests', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -314,6 +315,7 @@ describe('cache-control tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); diff --git a/src/tests/datastore-tests.ts b/src/tests/datastore-tests.ts index 6889b0a25c..45f104c870 100644 --- a/src/tests/datastore-tests.ts +++ b/src/tests/datastore-tests.ts @@ -380,6 +380,7 @@ describe('postgres datastore', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -547,6 +548,7 @@ describe('postgres datastore', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -761,6 +763,7 @@ describe('postgres datastore', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], })), }); @@ -958,6 +961,7 @@ describe('postgres datastore', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], })), }); @@ -1235,6 +1239,7 @@ describe('postgres datastore', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, { tx: tx2, @@ -1248,6 +1253,7 @@ describe('postgres datastore', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, { tx: tx3, @@ -1261,6 +1267,7 @@ describe('postgres datastore', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -2013,6 +2020,7 @@ describe('postgres datastore', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -2096,6 +2104,7 @@ describe('postgres datastore', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -2187,6 +2196,7 @@ describe('postgres datastore', () => { smartContracts: [contract], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -2282,6 +2292,7 @@ describe('postgres datastore', () => { smartContracts: [contract], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -2403,6 +2414,7 @@ describe('postgres datastore', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -2485,6 +2497,7 @@ describe('postgres datastore', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -2566,6 +2579,7 @@ describe('postgres datastore', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -2809,6 +2823,7 @@ describe('postgres datastore', () => { namespaces: [namespace1], pox2Events: [], pox3Events: [], + pox4Events: [], }, { tx: { ...tx2, raw_tx: '0x' }, @@ -2822,6 +2837,7 @@ describe('postgres datastore', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -3236,6 +3252,7 @@ describe('postgres datastore', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -3299,6 +3316,7 @@ describe('postgres datastore', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -3751,6 +3769,7 @@ describe('postgres datastore', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -3817,6 +3836,7 @@ describe('postgres datastore', () => { ], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -4018,6 +4038,7 @@ describe('postgres datastore', () => { ], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -4299,6 +4320,7 @@ describe('postgres datastore', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -4380,6 +4402,7 @@ describe('postgres datastore', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -4515,6 +4538,7 @@ describe('postgres datastore', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, { tx: tx2, @@ -4528,6 +4552,7 @@ describe('postgres datastore', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); diff --git a/src/tests/mempool-tests.ts b/src/tests/mempool-tests.ts index 961d4d0756..af8805cbfb 100644 --- a/src/tests/mempool-tests.ts +++ b/src/tests/mempool-tests.ts @@ -537,6 +537,7 @@ describe('mempool tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }; diff --git a/src/tests/microblock-tests.ts b/src/tests/microblock-tests.ts index 06b27304aa..0ed9e74264 100644 --- a/src/tests/microblock-tests.ts +++ b/src/tests/microblock-tests.ts @@ -380,6 +380,7 @@ describe('microblock tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -528,6 +529,7 @@ describe('microblock tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, { tx: mbTx2, @@ -541,6 +543,7 @@ describe('microblock tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); diff --git a/src/tests/other-tests.ts b/src/tests/other-tests.ts index bbd6ad784c..217b59bc0d 100644 --- a/src/tests/other-tests.ts +++ b/src/tests/other-tests.ts @@ -127,6 +127,7 @@ describe('other tests', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); diff --git a/src/tests/search-tests.ts b/src/tests/search-tests.ts index 94c8621339..69b8b62b15 100644 --- a/src/tests/search-tests.ts +++ b/src/tests/search-tests.ts @@ -340,6 +340,7 @@ describe('search tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }; @@ -1253,6 +1254,7 @@ describe('search tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, { tx: stxTx2, @@ -1266,6 +1268,7 @@ describe('search tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, { tx: smartContractTx, @@ -1279,6 +1282,7 @@ describe('search tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }; diff --git a/src/tests/smart-contract-tests.ts b/src/tests/smart-contract-tests.ts index 195b5fe2b3..4329c827a7 100644 --- a/src/tests/smart-contract-tests.ts +++ b/src/tests/smart-contract-tests.ts @@ -137,6 +137,7 @@ describe('smart contract tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, { tx: tx2, @@ -150,6 +151,7 @@ describe('smart contract tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -270,6 +272,7 @@ describe('smart contract tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -377,6 +380,7 @@ describe('smart contract tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -1560,6 +1564,7 @@ describe('smart contract tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, { tx: tx2, @@ -1573,6 +1578,7 @@ describe('smart contract tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); diff --git a/src/tests/tx-tests.ts b/src/tests/tx-tests.ts index d56b7a35cf..8a7ed7b0a3 100644 --- a/src/tests/tx-tests.ts +++ b/src/tests/tx-tests.ts @@ -249,6 +249,7 @@ describe('tx tests', () => { smartContracts: [smartContract1], pox2Events: [], pox3Events: [], + pox4Events: [], }, { tx: dbTx2, @@ -262,6 +263,7 @@ describe('tx tests', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, { tx: dbTx3, @@ -275,6 +277,7 @@ describe('tx tests', () => { smartContracts: [versionedSmartContract1], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -408,6 +411,7 @@ describe('tx tests', () => { smartContracts: [smartContract], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -546,6 +550,7 @@ describe('tx tests', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -683,6 +688,7 @@ describe('tx tests', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -854,6 +860,7 @@ describe('tx tests', () => { smartContracts: [smartContract], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -1199,6 +1206,7 @@ describe('tx tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -1439,6 +1447,7 @@ describe('tx tests', () => { smartContracts: [smartContract], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -1637,6 +1646,7 @@ describe('tx tests', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -1781,6 +1791,7 @@ describe('tx tests', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -1914,6 +1925,7 @@ describe('tx tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -2029,6 +2041,7 @@ describe('tx tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -2690,6 +2703,7 @@ describe('tx tests', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); @@ -2944,6 +2958,7 @@ describe('tx tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, { tx: tx2, @@ -2957,6 +2972,7 @@ describe('tx tests', () => { namespaces: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }; @@ -3248,6 +3264,7 @@ describe('tx tests', () => { smartContracts: [], pox2Events: [], pox3Events: [], + pox4Events: [], }, ], }); From dd9813a23ac1f83602cb2bd26d93bf537452fb6e Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sat, 9 Dec 2023 17:26:16 +0100 Subject: [PATCH 10/23] chore: move delegations lookup endpoint into pox router --- src/api/init.ts | 26 +++++-------- src/api/query-helpers.ts | 10 +++++ src/api/routes/pox.ts | 75 +++++++++++++++++++++++++++++++++++++- src/api/routes/stacking.ts | 72 ------------------------------------ src/datastore/common.ts | 2 +- src/datastore/pg-store.ts | 16 ++++---- 6 files changed, 102 insertions(+), 99 deletions(-) delete mode 100644 src/api/routes/stacking.ts diff --git a/src/api/init.ts b/src/api/init.ts index b6da26e27b..774284803a 100644 --- a/src/api/init.ts +++ b/src/api/init.ts @@ -42,9 +42,9 @@ import { PgStore } from '../datastore/pg-store'; import { PgWriteStore } from '../datastore/pg-write-store'; import { WebSocketTransmitter } from './routes/ws/web-socket-transmitter'; import { createPoxEventsRouter } from './routes/pox'; -import { createStackingRouter } from './routes/stacking'; import { logger, loggerMiddleware } from '../logger'; import { SERVER_VERSION, isPgConnectionError, isProdEnv, waiter } from '@hirosystems/api-toolkit'; +import { getReqQuery } from './query-helpers'; export interface ApiServer { expressApp: express.Express; @@ -203,9 +203,8 @@ export async function startApiServer(opts: { const legacyPoxPathRouter: express.RequestHandler = (req, res) => { // Redirect old pox routes paths to new one above const newPath = req.path === '/' ? '/events' : req.path; - const query = querystring.stringify(req.query as Record); const baseUrl = req.baseUrl.replace(/(pox[23])_events/, '$1'); - const redirectPath = `${baseUrl}${newPath}${query ? `?${query}` : ''}`; + const redirectPath = `${baseUrl}${newPath}${getReqQuery(req)}`; return res.redirect(redirectPath); }; router.use('/pox2_events', legacyPoxPathRouter); @@ -218,20 +217,13 @@ export async function startApiServer(opts: { })() ); - app.use( - '/extended/beta', - (() => { - const router = express.Router(); - router.use(cors()); - router.use((req, res, next) => { - // Set caching on all routes to be disabled by default, individual routes can override - res.set('Cache-Control', 'no-store'); - next(); - }); - router.use('/stacking', createStackingRouter(datastore)); - return router; - })() - ); + // Redirect to new endpoint for backward compatibility. + // TODO: remove this in the future + app.use('/extended/beta/stacking/:pool_principal/delegations', (req, res) => { + const { pool_principal } = req.params; + const newPath = `/extended/v1/pox3/${pool_principal}/delegations${getReqQuery(req)}`; + return res.redirect(newPath); + }); // Setup direct proxy to core-node RPC endpoints (/v2) // pricing endpoint diff --git a/src/api/query-helpers.ts b/src/api/query-helpers.ts index fa365535a8..4a1cfe8ff6 100644 --- a/src/api/query-helpers.ts +++ b/src/api/query-helpers.ts @@ -345,3 +345,13 @@ export function isValidTxId(tx_id: string) { return false; } } + +/** + * Returns the query string of a request, including the leading '?' character. + * If the request does not have a query string then an empty string is returned. + */ +export function getReqQuery(req: Request): string { + const fullUrl = `http://${req.headers.host}${req.originalUrl}`; + const urlObject = new URL(fullUrl); + return urlObject.search; +} diff --git a/src/api/routes/pox.ts b/src/api/routes/pox.ts index d657942a0f..c99b5a7ba1 100644 --- a/src/api/routes/pox.ts +++ b/src/api/routes/pox.ts @@ -3,13 +3,21 @@ import { asyncHandler } from '../async-handler'; import { getPagingQueryLimit, parsePagingQueryInput, ResourceType } from '../pagination'; import { PgStore } from '../../datastore/pg-store'; import { parsePoxSyntheticEvent } from '../controllers/db-controller'; -import { validatePrincipal, validateRequestHexInput } from '../query-helpers'; +import { + getBlockHeightQueryParam, + getBlockParams, + validatePrincipal, + validateRequestHexInput, +} from '../query-helpers'; +import { getETagCacheHandler, setETagCacheHeaders } from '../controllers/cache-controller'; +import { PoolDelegationsResponse } from '@stacks/stacks-blockchain-api-types'; export function createPoxEventsRouter( db: PgStore, poxVersion: 'pox2' | 'pox3' | 'pox4' ): express.Router { const router = express.Router(); + const cacheHandler = getETagCacheHandler(db); const poxTable = ( { @@ -21,6 +29,7 @@ export function createPoxEventsRouter( router.get( '/events', + cacheHandler, asyncHandler(async (req, res) => { const limit = getPagingQueryLimit(ResourceType.Pox2Event, req.query.limit); const offset = parsePagingQueryInput(req.query.offset ?? 0); @@ -36,12 +45,14 @@ export function createPoxEventsRouter( offset, results: parsedResult, }; + setETagCacheHeaders(res); res.json(response); }) ); router.get( '/tx/:tx_id', + cacheHandler, asyncHandler(async (req, res) => { const { tx_id } = req.params; validateRequestHexInput(tx_id); @@ -57,12 +68,14 @@ export function createPoxEventsRouter( const response = { results: parsedResult, }; + setETagCacheHeaders(res); res.json(response); }) ); router.get( '/stacker/:principal', + cacheHandler, asyncHandler(async (req, res) => { const { principal } = req.params; validatePrincipal(principal); @@ -78,6 +91,66 @@ export function createPoxEventsRouter( const response = { results: parsedResult, }; + setETagCacheHeaders(res); + res.json(response); + }) + ); + + router.get( + '/:pool_principal/delegations', + cacheHandler, + asyncHandler(async (req, res, next) => { + // get recent asset event associated with address + const poolPrincipal = req.params['pool_principal']; + validatePrincipal(poolPrincipal); + + const limit = getPagingQueryLimit(ResourceType.Stacker, req.query.limit); + const offset = parsePagingQueryInput(req.query.offset ?? 0); + const afterBlock = getBlockHeightQueryParam('after_block', false, req, res, next) || 0; + + const response = await db.sqlTransaction(async sql => { + const blockParams = getBlockParams(req, res, next); + let blockHeight: number; + if (blockParams.blockHeight !== undefined) { + blockHeight = blockParams.blockHeight; + } else { + blockHeight = await db.getMaxBlockHeight(sql, { + includeUnanchored: blockParams.includeUnanchored ?? false, + }); + } + + const dbBlock = await db.getBlockByHeightInternal(sql, blockHeight); + if (!dbBlock.found) { + const error = `no block at height: ${blockHeight}`; + res.status(404).json({ error: error }); + throw new Error(error); + } + const burnBlockHeight = dbBlock.result.burn_block_height; + + const stackersQuery = await db.getPoxPoolDelegations({ + delegator: poolPrincipal, + blockHeight, + burnBlockHeight, + afterBlockHeight: afterBlock, + limit, + offset, + poxTable, + }); + if (!stackersQuery.found) { + const error = `no stackers found`; + res.status(404).json({ error: error }); + throw new Error(error); + } + + const response: PoolDelegationsResponse = { + limit, + offset, + total: stackersQuery.result.total, + results: stackersQuery.result.stackers, + }; + return response; + }); + setETagCacheHeaders(res); res.json(response); }) ); diff --git a/src/api/routes/stacking.ts b/src/api/routes/stacking.ts deleted file mode 100644 index c7aea8f68e..0000000000 --- a/src/api/routes/stacking.ts +++ /dev/null @@ -1,72 +0,0 @@ -import * as express from 'express'; -import { asyncHandler } from '../async-handler'; -import { PoolDelegationsResponse } from '@stacks/stacks-blockchain-api-types'; -import { getPagingQueryLimit, parsePagingQueryInput, ResourceType } from '../pagination'; -import { PgStore } from '../../datastore/pg-store'; -import { getBlockHeightQueryParam, getBlockParams, validatePrincipal } from '../query-helpers'; -import { getETagCacheHandler, setETagCacheHeaders } from '../controllers/cache-controller'; - -export function createStackingRouter(db: PgStore): express.Router { - const router = express.Router(); - const cacheHandler = getETagCacheHandler(db); - - router.get( - '/:pool_principal/delegations', - cacheHandler, - asyncHandler(async (req, res, next) => { - // get recent asset event associated with address - const poolPrincipal = req.params['pool_principal']; - validatePrincipal(poolPrincipal); - - const limit = getPagingQueryLimit(ResourceType.Stacker, req.query.limit); - const offset = parsePagingQueryInput(req.query.offset ?? 0); - const afterBlock = getBlockHeightQueryParam('after_block', false, req, res, next) || 0; - - const response = await db.sqlTransaction(async sql => { - const blockParams = getBlockParams(req, res, next); - let blockHeight: number; - if (blockParams.blockHeight !== undefined) { - blockHeight = blockParams.blockHeight; - } else { - blockHeight = await db.getMaxBlockHeight(sql, { - includeUnanchored: blockParams.includeUnanchored ?? false, - }); - } - - const dbBlock = await db.getBlockByHeightInternal(sql, blockHeight); - if (!dbBlock.found) { - const error = `no block at height: ${blockHeight}`; - res.status(404).json({ error: error }); - throw new Error(error); - } - const burnBlockHeight = dbBlock.result.burn_block_height; - - const stackersQuery = await db.getPox3PoolDelegations({ - delegator: poolPrincipal, - blockHeight, - burnBlockHeight, - afterBlockHeight: afterBlock, - limit, - offset, - }); - if (!stackersQuery.found) { - const error = `no stackers found`; - res.status(404).json({ error: error }); - throw new Error(error); - } - - const response: PoolDelegationsResponse = { - limit, - offset, - total: stackersQuery.result.total, - results: stackersQuery.result.stackers, - }; - return response; - }); - setETagCacheHeaders(res); - res.json(response); - }) - ); - - return router; -} diff --git a/src/datastore/common.ts b/src/datastore/common.ts index d57c65c4c5..65952d5531 100644 --- a/src/datastore/common.ts +++ b/src/datastore/common.ts @@ -454,7 +454,7 @@ export type DbPoxSyntheticEventData = export type DbPoxSyntheticEvent = DbEventBase & DbPoxSyntheticEventData; -export interface DbPox3Stacker { +export interface DbPoxStacker { stacker: string; pox_addr?: string; amount_ustx: string; diff --git a/src/datastore/pg-store.ts b/src/datastore/pg-store.ts index 0ebfda9942..5f4db2dc96 100644 --- a/src/datastore/pg-store.ts +++ b/src/datastore/pg-store.ts @@ -44,7 +44,7 @@ import { DbMinerReward, DbNftEvent, DbPoxSyntheticEvent, - DbPox3Stacker, + DbPoxStacker, DbRewardSlotHolder, DbSearchResult, DbSmartContract, @@ -1905,15 +1905,15 @@ export class PgStore extends BasePgStore { }); } - // TODO: modify this function for pox4 - async getPox3PoolDelegations(args: { + async getPoxPoolDelegations(args: { delegator: string; blockHeight: number; burnBlockHeight: number; afterBlockHeight: number; limit: number; offset: number; - }): Promise> { + poxTable: PoxSyntheticEventTable; + }): Promise> { return await this.sqlTransaction(async sql => { const queryResults = await sql< { @@ -1926,11 +1926,11 @@ export class PgStore extends BasePgStore { total_rows: number; }[] >` - WITH ordered_pox3_events AS ( + WITH ordered_pox_events AS ( SELECT stacker, pox_addr, amount_ustx, unlock_burn_height::integer, tx_id, block_height, microblock_sequence, tx_index, event_index - FROM pox3_events + FROM ${sql(args.poxTable)} WHERE canonical = true AND microblock_canonical = true AND name = ${SyntheticPoxEventName.DelegateStx} AND delegate_to = ${args.delegator} AND @@ -1942,7 +1942,7 @@ export class PgStore extends BasePgStore { SELECT DISTINCT ON (stacker) stacker, pox_addr, amount_ustx, unlock_burn_height, tx_id, block_height, microblock_sequence, tx_index, event_index - FROM ordered_pox3_events + FROM ordered_pox_events ORDER BY stacker, block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC ) SELECT @@ -1954,7 +1954,7 @@ export class PgStore extends BasePgStore { OFFSET ${args.offset} `; const total = queryResults[0]?.total_rows ?? 0; - const stackers: DbPox3Stacker[] = queryResults.map(result => ({ + const stackers: DbPoxStacker[] = queryResults.map(result => ({ stacker: result.stacker, pox_addr: result.pox_addr || undefined, amount_ustx: result.amount_ustx, From 3b4325ed14d98a258aecceedfe156cefaf5508d7 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sat, 9 Dec 2023 17:31:18 +0100 Subject: [PATCH 11/23] fix: handle pox4_events during reorgs --- src/datastore/common.ts | 2 ++ src/datastore/helpers.ts | 1 + src/datastore/pg-write-store.ts | 18 ++++++++++++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/datastore/common.ts b/src/datastore/common.ts index 65952d5531..3966ccd161 100644 --- a/src/datastore/common.ts +++ b/src/datastore/common.ts @@ -971,6 +971,7 @@ export interface UpdatedEntities { nftEvents: number; pox2Events: number; pox3Events: number; + pox4Events: number; contractLogs: number; smartContracts: number; names: number; @@ -988,6 +989,7 @@ export interface UpdatedEntities { nftEvents: number; pox2Events: number; pox3Events: number; + pox4Events: number; contractLogs: number; smartContracts: number; names: number; diff --git a/src/datastore/helpers.ts b/src/datastore/helpers.ts index ea585610a0..bfc351de19 100644 --- a/src/datastore/helpers.ts +++ b/src/datastore/helpers.ts @@ -201,6 +201,7 @@ export const TX_METADATA_TABLES = [ 'nft_events', 'pox2_events', 'pox3_events', + 'pox4_events', 'contract_logs', 'stx_lock_events', 'smart_contracts', diff --git a/src/datastore/pg-write-store.ts b/src/datastore/pg-write-store.ts index 8246eafc47..bd9d6c476b 100644 --- a/src/datastore/pg-write-store.ts +++ b/src/datastore/pg-write-store.ts @@ -937,7 +937,9 @@ export class PgWriteStore extends PgStore { break; } default: { - throw new Error(`Unexpected Pox3 event name: ${(event as DbPoxSyntheticEvent).name}`); + throw new Error( + `Unexpected Pox synthetic event name: ${(event as DbPoxSyntheticEvent).name}` + ); } } await sql` @@ -2510,7 +2512,6 @@ export class PgWriteStore extends PgStore { microblocks: [], }); - // todo: do we still need pox2 marking here? const pox2Result = await sql` UPDATE pox2_events SET canonical = ${canonical} @@ -2533,6 +2534,17 @@ export class PgWriteStore extends PgStore { updatedEntities.markedNonCanonical.pox3Events += pox3Result.count; } + const pox4Result = await sql` + UPDATE pox4_events + SET canonical = ${canonical} + WHERE index_block_hash = ${indexBlockHash} AND canonical != ${canonical} + `; + if (canonical) { + updatedEntities.markedCanonical.pox4Events += pox4Result.count; + } else { + updatedEntities.markedNonCanonical.pox4Events += pox4Result.count; + } + const contractLogResult = await sql` UPDATE contract_logs SET canonical = ${canonical} @@ -2737,6 +2749,7 @@ export class PgWriteStore extends PgStore { nftEvents: 0, pox2Events: 0, pox3Events: 0, + pox4Events: 0, contractLogs: 0, smartContracts: 0, names: 0, @@ -2754,6 +2767,7 @@ export class PgWriteStore extends PgStore { nftEvents: 0, pox2Events: 0, pox3Events: 0, + pox4Events: 0, contractLogs: 0, smartContracts: 0, names: 0, From d0b2f098e0407a7cefe2eaa090ae558721de8dcf Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sat, 9 Dec 2023 17:38:09 +0100 Subject: [PATCH 12/23] test: fix pox4events reorg count --- src/tests/datastore-tests.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tests/datastore-tests.ts b/src/tests/datastore-tests.ts index 45f104c870..683651c0de 100644 --- a/src/tests/datastore-tests.ts +++ b/src/tests/datastore-tests.ts @@ -3562,6 +3562,7 @@ describe('postgres datastore', () => { nftEvents: 0, pox2Events: 0, pox3Events: 0, + pox4Events: 0, contractLogs: 0, smartContracts: 0, names: 0, @@ -3579,6 +3580,7 @@ describe('postgres datastore', () => { nftEvents: 0, pox2Events: 0, pox3Events: 0, + pox4Events: 0, contractLogs: 0, smartContracts: 0, names: 0, From 3f7f144782d600f90003f6608d16fad7a5cd07d0 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sat, 9 Dec 2023 19:52:52 +0100 Subject: [PATCH 13/23] chore: update stacks-node image to stacks 3.0 --- docker/docker-compose.dev.stacks-blockchain.yml | 2 +- docker/docker-compose.dev.stacks-krypton.yml | 2 +- stacks-blockchain/docker/Dockerfile | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/docker-compose.dev.stacks-blockchain.yml b/docker/docker-compose.dev.stacks-blockchain.yml index fc79e5f246..c07ad5ca3d 100644 --- a/docker/docker-compose.dev.stacks-blockchain.yml +++ b/docker/docker-compose.dev.stacks-blockchain.yml @@ -1,7 +1,7 @@ version: '3.7' services: stacks-blockchain: - image: "hirosystems/stacks-api-e2e:stacks2.4-f930deb" + image: "hirosystems/stacks-api-e2e:stacks3.0-7114a1b" restart: on-failure environment: STACKS_EVENT_OBSERVER: host.docker.internal:3700 diff --git a/docker/docker-compose.dev.stacks-krypton.yml b/docker/docker-compose.dev.stacks-krypton.yml index 850950fdbf..e8c27dad4f 100644 --- a/docker/docker-compose.dev.stacks-krypton.yml +++ b/docker/docker-compose.dev.stacks-krypton.yml @@ -1,7 +1,7 @@ version: '3.7' services: stacks-blockchain: - image: "hirosystems/stacks-api-e2e:stacks2.4-f930deb" + image: "hirosystems/stacks-api-e2e:stacks3.0-7114a1b" ports: - "18443:18443" # bitcoin regtest JSON-RPC interface - "18444:18444" # bitcoin regtest p2p diff --git a/stacks-blockchain/docker/Dockerfile b/stacks-blockchain/docker/Dockerfile index e9484f5704..d71a5ddf20 100644 --- a/stacks-blockchain/docker/Dockerfile +++ b/stacks-blockchain/docker/Dockerfile @@ -1,7 +1,7 @@ # Pointed to stacks-blockchain `2.1.0.0.0` git tag -FROM --platform=linux/amd64 hirosystems/stacks-api-e2e:stacks2.4-f930deb as build +FROM --platform=linux/amd64 hirosystems/stacks-api-e2e:stacks3.0-7114a1b as build -FROM --platform=linux/amd64 debian:bullseye +FROM --platform=linux/amd64 debian:bookworm COPY wait-for-it.sh /bin/wait-for-it.sh RUN chmod +x /bin/wait-for-it.sh From 007bae326f8351771399f6893e02841cf0d897b2 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sat, 9 Dec 2023 20:10:36 +0100 Subject: [PATCH 14/23] test: begin transitioning pox tests to use pox-4 --- src/test-utils/shared-setup.ts | 4 ++-- src/tests-2.4/global-setup.ts | 8 ++++---- src/tests-2.4/pox-3-btc-address-formats.ts | 8 ++++---- src/tests-2.4/pox-3-burnchain-delegate-stx.ts | 2 +- src/tests-2.4/pox-3-burnchain-stack-stx.ts | 2 +- src/tests-2.4/pox-3-delegate-aggregation.ts | 2 +- src/tests-2.4/pox-3-delegate-revoked-stacking.ts | 2 +- src/tests-2.4/pox-3-delegate-stacking.ts | 2 +- src/tests-2.4/pox-3-rosetta-btc-addr-types.ts | 2 +- src/tests-2.4/pox-3-rosetta-segwit.ts | 4 ++-- src/tests-2.4/pox-3-stack-extend-increase.ts | 2 +- 11 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/test-utils/shared-setup.ts b/src/test-utils/shared-setup.ts index b842d1276d..8b20d20601 100644 --- a/src/test-utils/shared-setup.ts +++ b/src/test-utils/shared-setup.ts @@ -22,12 +22,12 @@ async function standByForPoxToBeReady(client: StacksCoreRpcClient): Promise { cycleBlockLength = cycleCount * poxInfo.reward_cycle_length; [contractAddress, contractName] = poxInfo.contract_id.split('.'); - expect(contractName).toBe('pox-3'); + expect(contractName).toBe('pox-4'); }); test('stack-stx tx', async () => { @@ -331,7 +331,7 @@ describe('PoX-3 - Stack using supported bitcoin address formats', () => { cycleBlockLength = cycleCount * poxInfo.reward_cycle_length; [contractAddress, contractName] = poxInfo.contract_id.split('.'); - expect(contractName).toBe('pox-3'); + expect(contractName).toBe('pox-4'); }); test('stack-stx tx', async () => { @@ -584,7 +584,7 @@ describe('PoX-3 - Stack using supported bitcoin address formats', () => { cycleBlockLength = cycleCount * poxInfo.reward_cycle_length; [contractAddress, contractName] = poxInfo.contract_id.split('.'); - expect(contractName).toBe('pox-3'); + expect(contractName).toBe('pox-4'); }); test('stack-stx tx', async () => { @@ -836,7 +836,7 @@ describe('PoX-3 - Stack using supported bitcoin address formats', () => { cycleBlockLength = cycleCount * poxInfo.reward_cycle_length; [contractAddress, contractName] = poxInfo.contract_id.split('.'); - expect(contractName).toBe('pox-3'); + expect(contractName).toBe('pox-4'); }); test('stack-stx tx', async () => { diff --git a/src/tests-2.4/pox-3-burnchain-delegate-stx.ts b/src/tests-2.4/pox-3-burnchain-delegate-stx.ts index 11d27b945b..00c1cd243b 100644 --- a/src/tests-2.4/pox-3-burnchain-delegate-stx.ts +++ b/src/tests-2.4/pox-3-burnchain-delegate-stx.ts @@ -216,7 +216,7 @@ describe('PoX-3 - Stack using Bitcoin-chain ops', () => { const poxInfo = await client.getPox(); const [contractAddress, contractName] = poxInfo.contract_id.split('.'); - expect(contractName).toBe('pox-3'); + expect(contractName).toBe('pox-4'); }); test('Fund STX to new account for testing', async () => { diff --git a/src/tests-2.4/pox-3-burnchain-stack-stx.ts b/src/tests-2.4/pox-3-burnchain-stack-stx.ts index d129bdbc9c..a9ac860ab1 100644 --- a/src/tests-2.4/pox-3-burnchain-stack-stx.ts +++ b/src/tests-2.4/pox-3-burnchain-stack-stx.ts @@ -169,7 +169,7 @@ describe('PoX-3 - Stack using Bitcoin-chain ops', () => { const poxInfo = await client.getPox(); const [contractAddress, contractName] = poxInfo.contract_id.split('.'); - expect(contractName).toBe('pox-3'); + expect(contractName).toBe('pox-4'); }); test('Fund STX to new account for testing', async () => { diff --git a/src/tests-2.4/pox-3-delegate-aggregation.ts b/src/tests-2.4/pox-3-delegate-aggregation.ts index 504914925c..b6400ebedf 100644 --- a/src/tests-2.4/pox-3-delegate-aggregation.ts +++ b/src/tests-2.4/pox-3-delegate-aggregation.ts @@ -118,7 +118,7 @@ describe('PoX-3 - Delegate aggregation increase operations', () => { // wait until the start of the next cycle so we have enough blocks within the cycle to perform the various txs poxInfo = await standByForNextPoxCycle(); [contractAddress, contractName] = poxInfo.contract_id.split('.'); - expect(contractName).toBe('pox-3'); + expect(contractName).toBe('pox-4'); }); test('Perform delegate-stx operation', async () => { diff --git a/src/tests-2.4/pox-3-delegate-revoked-stacking.ts b/src/tests-2.4/pox-3-delegate-revoked-stacking.ts index 19951d762c..bf9d1b4c6e 100644 --- a/src/tests-2.4/pox-3-delegate-revoked-stacking.ts +++ b/src/tests-2.4/pox-3-delegate-revoked-stacking.ts @@ -113,7 +113,7 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { poxInfo = await standByForPoxCycle(); [contractAddress, contractName] = poxInfo.contract_id.split('.'); - expect(contractName).toBe('pox-3'); + expect(contractName).toBe('pox-4'); const balanceInfo = await testEnv.client.getAccount(STACKER.stxAddr); expect(BigInt(balanceInfo.balance)).toBeGreaterThan(0n); diff --git a/src/tests-2.4/pox-3-delegate-stacking.ts b/src/tests-2.4/pox-3-delegate-stacking.ts index e5e64e0a5b..f285d5cdf4 100644 --- a/src/tests-2.4/pox-3-delegate-stacking.ts +++ b/src/tests-2.4/pox-3-delegate-stacking.ts @@ -110,7 +110,7 @@ describe('PoX-3 - Delegate Stacking operations', () => { poxInfo = await standByForNextPoxCycle(); [contractAddress, contractName] = poxInfo.contract_id.split('.'); - expect(contractName).toBe('pox-3'); + expect(contractName).toBe('pox-4'); }); test('Perform delegate-stx operation', async () => { diff --git a/src/tests-2.4/pox-3-rosetta-btc-addr-types.ts b/src/tests-2.4/pox-3-rosetta-btc-addr-types.ts index 1da11dddf6..d59e0c7eff 100644 --- a/src/tests-2.4/pox-3-rosetta-btc-addr-types.ts +++ b/src/tests-2.4/pox-3-rosetta-btc-addr-types.ts @@ -57,7 +57,7 @@ describe.each(BTC_ADDRESS_CASES)( ustxAmount, }); expect(rosettaStackStx.tx.status).toBe(DbTxStatus.Success); - expect(rosettaStackStx.constructionMetadata.metadata.contract_name).toBe('pox-3'); + expect(rosettaStackStx.constructionMetadata.metadata.contract_name).toBe('pox-4'); }); test('Validate reward set received', async () => { diff --git a/src/tests-2.4/pox-3-rosetta-segwit.ts b/src/tests-2.4/pox-3-rosetta-segwit.ts index c878423635..42cb4db846 100644 --- a/src/tests-2.4/pox-3-rosetta-segwit.ts +++ b/src/tests-2.4/pox-3-rosetta-segwit.ts @@ -131,7 +131,7 @@ describe('PoX-3 - Rosetta - Stacking with segwit', () => { ustxAmount: ustxAmount, }); - expect(stackingResult.constructionMetadata.metadata.contract_name).toBe('pox-3'); + expect(stackingResult.constructionMetadata.metadata.contract_name).toBe('pox-4'); expect(stackingResult.constructionMetadata.metadata.burn_block_height as number).toBeTruthy(); expect(stackingResult.submitResult.transaction_identifier.hash).toBe(stackingResult.txId); expect(stackingResult.tx.contract_call_contract_id).toBe('ST000000000000000000002AMW42H.pox-3'); @@ -249,7 +249,7 @@ describe('PoX-3 - Rosetta - Stacking with segwit', () => { ustxAmount, }); - expect(rosettaStackStx.constructionMetadata.metadata.contract_name).toBe('pox-3'); + expect(rosettaStackStx.constructionMetadata.metadata.contract_name).toBe('pox-4'); expect(rosettaStackStx.constructionMetadata.metadata.burn_block_height as number).toBeTruthy(); expect(rosettaStackStx.submitResult.transaction_identifier.hash).toBe(rosettaStackStx.txId); expect(rosettaStackStx.tx.contract_call_contract_id).toBe( diff --git a/src/tests-2.4/pox-3-stack-extend-increase.ts b/src/tests-2.4/pox-3-stack-extend-increase.ts index ba18676e09..312a4b3a41 100644 --- a/src/tests-2.4/pox-3-stack-extend-increase.ts +++ b/src/tests-2.4/pox-3-stack-extend-increase.ts @@ -101,7 +101,7 @@ describe('PoX-3 - Stack extend and increase operations', () => { cycleBlockLength = lockPeriod * poxInfo.reward_cycle_length; [contractAddress, contractName] = poxInfo.contract_id.split('.'); - expect(contractName).toBe('pox-3'); + expect(contractName).toBe('pox-4'); }); test('stack-stx tx', async () => { From 753b8a47414daca7d472212b2ee846ca0ba315a8 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sun, 10 Dec 2023 13:12:47 +0100 Subject: [PATCH 15/23] chore: bump stacks-node to wip nakamoto branch --- docker/docker-compose.dev.stacks-blockchain.yml | 2 +- docker/docker-compose.dev.stacks-krypton.yml | 2 +- stacks-blockchain/docker/Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/docker-compose.dev.stacks-blockchain.yml b/docker/docker-compose.dev.stacks-blockchain.yml index c07ad5ca3d..23c338e983 100644 --- a/docker/docker-compose.dev.stacks-blockchain.yml +++ b/docker/docker-compose.dev.stacks-blockchain.yml @@ -1,7 +1,7 @@ version: '3.7' services: stacks-blockchain: - image: "hirosystems/stacks-api-e2e:stacks3.0-7114a1b" + image: "hirosystems/stacks-api-e2e:stacks3.0-800259e" restart: on-failure environment: STACKS_EVENT_OBSERVER: host.docker.internal:3700 diff --git a/docker/docker-compose.dev.stacks-krypton.yml b/docker/docker-compose.dev.stacks-krypton.yml index e8c27dad4f..58858240bf 100644 --- a/docker/docker-compose.dev.stacks-krypton.yml +++ b/docker/docker-compose.dev.stacks-krypton.yml @@ -1,7 +1,7 @@ version: '3.7' services: stacks-blockchain: - image: "hirosystems/stacks-api-e2e:stacks3.0-7114a1b" + image: "hirosystems/stacks-api-e2e:stacks3.0-800259e" ports: - "18443:18443" # bitcoin regtest JSON-RPC interface - "18444:18444" # bitcoin regtest p2p diff --git a/stacks-blockchain/docker/Dockerfile b/stacks-blockchain/docker/Dockerfile index d71a5ddf20..d962f5eb95 100644 --- a/stacks-blockchain/docker/Dockerfile +++ b/stacks-blockchain/docker/Dockerfile @@ -1,5 +1,5 @@ # Pointed to stacks-blockchain `2.1.0.0.0` git tag -FROM --platform=linux/amd64 hirosystems/stacks-api-e2e:stacks3.0-7114a1b as build +FROM --platform=linux/amd64 hirosystems/stacks-api-e2e:stacks3.0-800259e as build FROM --platform=linux/amd64 debian:bookworm From 7e5d3686d3ba6649cd185098224c9098f70884aa Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sun, 10 Dec 2023 13:42:23 +0100 Subject: [PATCH 16/23] test: switch more tests from pox-3 to pox-4 --- src/api/init.ts | 3 ++- src/tests-2.4/global-setup.ts | 4 ++-- src/tests-2.4/pox-3-burnchain-delegate-stx.ts | 6 +++--- src/tests-2.4/pox-3-burnchain-stack-stx.ts | 2 +- src/tests-2.4/pox-3-delegate-aggregation.ts | 8 ++++---- src/tests-2.4/pox-3-delegate-revoked-stacking.ts | 10 +++++----- src/tests-2.4/pox-3-delegate-stacking.ts | 10 +++++----- src/tests-2.4/pox-3-rosetta-segwit.ts | 4 ++-- src/tests-2.4/pox-3-stack-extend-increase.ts | 6 +++--- src/tests-rosetta-construction/construction.ts | 6 +++--- src/tests-rosetta/offline-api-tests.ts | 4 ++-- src/tests-rpc/core-rpc-tests.ts | 2 +- 12 files changed, 33 insertions(+), 32 deletions(-) diff --git a/src/api/init.ts b/src/api/init.ts index 774284803a..ab2acbc891 100644 --- a/src/api/init.ts +++ b/src/api/init.ts @@ -203,12 +203,13 @@ export async function startApiServer(opts: { const legacyPoxPathRouter: express.RequestHandler = (req, res) => { // Redirect old pox routes paths to new one above const newPath = req.path === '/' ? '/events' : req.path; - const baseUrl = req.baseUrl.replace(/(pox[23])_events/, '$1'); + const baseUrl = req.baseUrl.replace(/(pox[\d])_events/, '$1'); const redirectPath = `${baseUrl}${newPath}${getReqQuery(req)}`; return res.redirect(redirectPath); }; router.use('/pox2_events', legacyPoxPathRouter); router.use('/pox3_events', legacyPoxPathRouter); + router.use('/pox4_events', legacyPoxPathRouter); if (getChainIDNetwork(chainId) === 'testnet' && writeDatastore) { router.use('/faucets', createFaucetRouter(writeDatastore)); diff --git a/src/tests-2.4/global-setup.ts b/src/tests-2.4/global-setup.ts index 81e45c5992..40fdfa3ef6 100644 --- a/src/tests-2.4/global-setup.ts +++ b/src/tests-2.4/global-setup.ts @@ -11,7 +11,7 @@ export interface GlobalTestEnv { eventServer: EventStreamServer; } -async function standByForPox3ToBeReady(client: StacksCoreRpcClient): Promise { +async function standByForPox4ToBeReady(client: StacksCoreRpcClient): Promise { let tries = 0; while (true) { try { @@ -45,7 +45,7 @@ export default async (): Promise => { const eventServer = await startEventServer({ datastore: db, chainId: ChainID.Testnet }); const client = new StacksCoreRpcClient(); - await standByForPox3ToBeReady(client); + await standByForPox4ToBeReady(client); const testEnv: GlobalTestEnv = { db: db, diff --git a/src/tests-2.4/pox-3-burnchain-delegate-stx.ts b/src/tests-2.4/pox-3-burnchain-delegate-stx.ts index 00c1cd243b..d0163f15d8 100644 --- a/src/tests-2.4/pox-3-burnchain-delegate-stx.ts +++ b/src/tests-2.4/pox-3-burnchain-delegate-stx.ts @@ -332,7 +332,7 @@ describe('PoX-3 - Stack using Bitcoin-chain ops', () => { test('Ensure delegate-stx BitcoinOp parsed', async () => { const pox2Txs = await supertest(api.server) - .get(`/extended/v1/address/${BootContractAddress.testnet}.pox-3/transactions`) + .get(`/extended/v1/address/${BootContractAddress.testnet}.pox-4/transactions`) .expect(200); const delegateStxTxResp = await supertest(api.server) .get(`/extended/v1/tx/${pox2Txs.body.results[0].tx_id}`) @@ -351,7 +351,7 @@ describe('PoX-3 - Stack using Bitcoin-chain ops', () => { )})))`; expect(delegateStxTx.contract_call).toEqual({ - contract_id: 'ST000000000000000000002AMW42H.pox-3', + contract_id: 'ST000000000000000000002AMW42H.pox-4', function_name: 'delegate-stx', function_signature: '(define-public (delegate-stx (amount-ustx uint) (delegate-to principal) (until-burn-ht (optional uint)) (pox-addr (optional (tuple (hashbytes (buff 32)) (version (buff 1)))))))', @@ -418,7 +418,7 @@ describe('PoX-3 - Stack using Bitcoin-chain ops', () => { expect(coreBalanceInfo.unlock_height).toBeGreaterThan(0); // validate delegate-stack-stx pox2 event for this tx - const res: any = await fetchGet(`/extended/v1/pox3_events/tx/${delegateStackStxTxId}`); + const res: any = await fetchGet(`/extended/v1/pox4_events/tx/${delegateStackStxTxId}`); expect(res).toBeDefined(); expect(res.results).toHaveLength(1); expect(res.results[0]).toEqual( diff --git a/src/tests-2.4/pox-3-burnchain-stack-stx.ts b/src/tests-2.4/pox-3-burnchain-stack-stx.ts index a9ac860ab1..9fed5ec179 100644 --- a/src/tests-2.4/pox-3-burnchain-stack-stx.ts +++ b/src/tests-2.4/pox-3-burnchain-stack-stx.ts @@ -299,7 +299,7 @@ describe('PoX-3 - Stack using Bitcoin-chain ops', () => { expect(txObj.tx_type).toBe('contract_call'); expect(txObj.tx_status).toBe('success'); expect(txObj.sender_address).toBe(account.stxAddr); - expect(txObj.contract_call.contract_id).toBe(`${BootContractAddress.testnet}.pox-3`); + expect(txObj.contract_call.contract_id).toBe(`${BootContractAddress.testnet}.pox-4`); expect(txObj.contract_call.function_name).toBe('stack-stx'); const callArg1 = txObj.contract_call.function_args![0]; diff --git a/src/tests-2.4/pox-3-delegate-aggregation.ts b/src/tests-2.4/pox-3-delegate-aggregation.ts index b6400ebedf..c07d5ce1fa 100644 --- a/src/tests-2.4/pox-3-delegate-aggregation.ts +++ b/src/tests-2.4/pox-3-delegate-aggregation.ts @@ -220,7 +220,7 @@ describe('PoX-3 - Delegate aggregation increase operations', () => { expect(coreBalanceInfo.unlock_height).toBeGreaterThan(0); // validate delegate-stack-stx pox2 event for this tx - const res: any = await fetchGet(`/extended/v1/pox3_events/tx/${delegateStackStxTxId}`); + const res: any = await fetchGet(`/extended/v1/pox4_events/tx/${delegateStackStxTxId}`); expect(res).toBeDefined(); expect(res.results).toHaveLength(1); expect(res.results[0]).toEqual( @@ -280,7 +280,7 @@ describe('PoX-3 - Delegate aggregation increase operations', () => { expect(poxCycleAddressIndex).toEqual(0n); // validate stack-aggregation-commit pox2 event for this tx - const res: any = await fetchGet(`/extended/v1/pox3_events/tx/${stackAggrCommitTxId}`); + const res: any = await fetchGet(`/extended/v1/pox4_events/tx/${stackAggrCommitTxId}`); expect(res).toBeDefined(); expect(res.results).toHaveLength(1); expect(res.results[0]).toEqual( @@ -368,7 +368,7 @@ describe('PoX-3 - Delegate aggregation increase operations', () => { // validate delegate-stack-stx pox2 event for this tx const delegateStackIncreasePoxEvents: any = await fetchGet( - `/extended/v1/pox3_events/tx/${delegateStackIncreaseDbTx.tx_id}` + `/extended/v1/pox4_events/tx/${delegateStackIncreaseDbTx.tx_id}` ); expect(delegateStackIncreasePoxEvents).toBeDefined(); expect(delegateStackIncreasePoxEvents.results).toHaveLength(1); @@ -405,7 +405,7 @@ describe('PoX-3 - Delegate aggregation increase operations', () => { // validate stack-aggregation-commit pox2 event for this tx const stackAggreIncreasePoxEvents: any = await fetchGet( - `/extended/v1/pox3_events/tx/${stackAggrIncreaseTxId}` + `/extended/v1/pox4_events/tx/${stackAggrIncreaseTxId}` ); expect(stackAggreIncreasePoxEvents).toBeDefined(); expect(stackAggreIncreasePoxEvents.results).toHaveLength(1); diff --git a/src/tests-2.4/pox-3-delegate-revoked-stacking.ts b/src/tests-2.4/pox-3-delegate-revoked-stacking.ts index bf9d1b4c6e..a1c5be7485 100644 --- a/src/tests-2.4/pox-3-delegate-revoked-stacking.ts +++ b/src/tests-2.4/pox-3-delegate-revoked-stacking.ts @@ -169,8 +169,8 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { ); const delegateStxDbTx = await standByForTxSuccess(delegateStxTxId); - // validate delegate-stx pox3 event for this tx - const res: any = await fetchGet(`/extended/v1/pox3_events/tx/${delegateStxDbTx.tx_id}`); + // validate delegate-stx pox4 event for this tx + const res: any = await fetchGet(`/extended/v1/pox4_events/tx/${delegateStxDbTx.tx_id}`); expect(res).toBeDefined(); expect(res.results).toHaveLength(1); expect(res.results[0]).toEqual( @@ -234,7 +234,7 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { expect(coreBalanceInfo.unlock_height).toBeGreaterThan(0); // validate delegate-stack-stx pox2 event for this tx - const res: any = await fetchGet(`/extended/v1/pox3_events/tx/${delegateStackStxTxId}`); + const res: any = await fetchGet(`/extended/v1/pox4_events/tx/${delegateStackStxTxId}`); expect(res).toBeDefined(); expect(res.results).toHaveLength(1); expect(res.results[0]).toEqual( @@ -319,7 +319,7 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { STACKER.stxAddr ) ).rejects.toThrowError( - 'OptionNone result for call to ST000000000000000000002AMW42H,pox-3::get-delegation-info' + 'OptionNone result for call to ST000000000000000000002AMW42H,pox-4::get-delegation-info' ); // but stacker is still locked @@ -448,7 +448,7 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { await standByForTxSuccess(stackAggrCommitTxId); // validate stack-aggregation-commit pox2 event for this tx - const res: any = await fetchGet(`/extended/v1/pox3_events/tx/${stackAggrCommitTxId}`); + const res: any = await fetchGet(`/extended/v1/pox4_events/tx/${stackAggrCommitTxId}`); expect(res).toBeDefined(); expect(res.results).toHaveLength(1); expect(res.results[0]).toEqual( diff --git a/src/tests-2.4/pox-3-delegate-stacking.ts b/src/tests-2.4/pox-3-delegate-stacking.ts index f285d5cdf4..0faee2cb15 100644 --- a/src/tests-2.4/pox-3-delegate-stacking.ts +++ b/src/tests-2.4/pox-3-delegate-stacking.ts @@ -143,7 +143,7 @@ describe('PoX-3 - Delegate Stacking operations', () => { const delegateStxDbTx = await standByForTxSuccess(delegateStxTxId); // validate delegate-stx pox2 event for this tx - const res: any = await fetchGet(`/extended/v1/pox3_events/tx/${delegateStxDbTx.tx_id}`); + const res: any = await fetchGet(`/extended/v1/pox4_events/tx/${delegateStxDbTx.tx_id}`); expect(res).toBeDefined(); expect(res.results).toHaveLength(1); expect(res.results[0]).toEqual( @@ -236,7 +236,7 @@ describe('PoX-3 - Delegate Stacking operations', () => { expect(coreBalanceInfo.unlock_height).toBeGreaterThan(0); // validate delegate-stack-stx pox2 event for this tx - const res: any = await fetchGet(`/extended/v1/pox3_events/tx/${delegateStackStxTxId}`); + const res: any = await fetchGet(`/extended/v1/pox4_events/tx/${delegateStackStxTxId}`); expect(res).toBeDefined(); expect(res.results).toHaveLength(1); expect(res.results[0]).toEqual( @@ -300,7 +300,7 @@ describe('PoX-3 - Delegate Stacking operations', () => { // validate delegate-stack-stx pox2 event for this tx const res: any = await fetchGet( - `/extended/v1/pox3_events/tx/${delegateStackIncreaseDbTx.tx_id}` + `/extended/v1/pox4_events/tx/${delegateStackIncreaseDbTx.tx_id}` ); expect(res).toBeDefined(); expect(res.results).toHaveLength(1); @@ -362,7 +362,7 @@ describe('PoX-3 - Delegate Stacking operations', () => { expect(coreBalanceInfo.unlock_height).toBeGreaterThan(coreBalanceInfoPreIncrease.unlock_height); // validate delegate-stack-extend pox2 event for this tx - const res: any = await fetchGet(`/extended/v1/pox3_events/tx/${delegateStackExtendTxId}`); + const res: any = await fetchGet(`/extended/v1/pox4_events/tx/${delegateStackExtendTxId}`); expect(res).toBeDefined(); expect(res.results).toHaveLength(1); expect(res.results[0]).toEqual( @@ -414,7 +414,7 @@ describe('PoX-3 - Delegate Stacking operations', () => { const stackAggrCommmitDbTx = await standByForTxSuccess(stackAggrCommitTxId); // validate stack-aggregation-commit pox2 event for this tx - const res: any = await fetchGet(`/extended/v1/pox3_events/tx/${stackAggrCommitTxId}`); + const res: any = await fetchGet(`/extended/v1/pox4_events/tx/${stackAggrCommitTxId}`); expect(res).toBeDefined(); expect(res.results).toHaveLength(1); expect(res.results[0]).toEqual( diff --git a/src/tests-2.4/pox-3-rosetta-segwit.ts b/src/tests-2.4/pox-3-rosetta-segwit.ts index 42cb4db846..05bcd1e4c4 100644 --- a/src/tests-2.4/pox-3-rosetta-segwit.ts +++ b/src/tests-2.4/pox-3-rosetta-segwit.ts @@ -134,7 +134,7 @@ describe('PoX-3 - Rosetta - Stacking with segwit', () => { expect(stackingResult.constructionMetadata.metadata.contract_name).toBe('pox-4'); expect(stackingResult.constructionMetadata.metadata.burn_block_height as number).toBeTruthy(); expect(stackingResult.submitResult.transaction_identifier.hash).toBe(stackingResult.txId); - expect(stackingResult.tx.contract_call_contract_id).toBe('ST000000000000000000002AMW42H.pox-3'); + expect(stackingResult.tx.contract_call_contract_id).toBe('ST000000000000000000002AMW42H.pox-4'); }); test('Verify expected amount of STX are locked', async () => { @@ -253,7 +253,7 @@ describe('PoX-3 - Rosetta - Stacking with segwit', () => { expect(rosettaStackStx.constructionMetadata.metadata.burn_block_height as number).toBeTruthy(); expect(rosettaStackStx.submitResult.transaction_identifier.hash).toBe(rosettaStackStx.txId); expect(rosettaStackStx.tx.contract_call_contract_id).toBe( - 'ST000000000000000000002AMW42H.pox-3' + 'ST000000000000000000002AMW42H.pox-4' ); // ensure locked reported by stacks-node account RPC balance diff --git a/src/tests-2.4/pox-3-stack-extend-increase.ts b/src/tests-2.4/pox-3-stack-extend-increase.ts index 312a4b3a41..1cb492d7ff 100644 --- a/src/tests-2.4/pox-3-stack-extend-increase.ts +++ b/src/tests-2.4/pox-3-stack-extend-increase.ts @@ -173,7 +173,7 @@ describe('PoX-3 - Stack extend and increase operations', () => { expect(coreBalance.unlock_height).toBeGreaterThan(0); // validate the pox2 event for this tx - const res: any = await fetchGet(`/extended/v1/pox3_events/tx/${sendTxResult.txId}`); + const res: any = await fetchGet(`/extended/v1/pox4_events/tx/${sendTxResult.txId}`); expect(res).toBeDefined(); expect(res.results).toHaveLength(1); expect(res.results[0]).toEqual( @@ -268,7 +268,7 @@ describe('PoX-3 - Stack extend and increase operations', () => { expect(coreBalance.unlock_height).toBe(expectedUnlockHeight); // validate the pox2 event for this tx - const res: any = await fetchGet(`/extended/v1/pox3_events/tx/${sendTxResult.txId}`); + const res: any = await fetchGet(`/extended/v1/pox4_events/tx/${sendTxResult.txId}`); expect(res).toBeDefined(); expect(res.results).toHaveLength(1); expect(res.results[0]).toEqual( @@ -367,7 +367,7 @@ describe('PoX-3 - Stack extend and increase operations', () => { expect(coreBalance.unlock_height).toBeGreaterThan(coreBalancePreStackExtend.unlock_height); // validate the pox2 event for this tx - const res: any = await fetchGet(`/extended/v1/pox3_events/tx/${sendTxResult.txId}`); + const res: any = await fetchGet(`/extended/v1/pox4_events/tx/${sendTxResult.txId}`); expect(res).toBeDefined(); expect(res.results).toHaveLength(1); expect(res.results[0]).toEqual( diff --git a/src/tests-rosetta-construction/construction.ts b/src/tests-rosetta-construction/construction.ts index 3c40d9d4bd..274fbb8e11 100644 --- a/src/tests-rosetta-construction/construction.ts +++ b/src/tests-rosetta-construction/construction.ts @@ -956,7 +956,7 @@ describe('Rosetta Construction', () => { const sender = testnetKeys[0].stacksAddress; const fee = '270'; const contract_address = 'ST000000000000000000002AMW42H'; - const contract_name = 'pox-3'; + const contract_name = 'pox-4'; const stacking_amount = 5000; const burn_block_height = 200; const number_of_cycles = 5; @@ -1634,7 +1634,7 @@ describe('Rosetta Construction', () => { delegate_to: testnetKeys[1].stacksAddress, size: 260, contract_address: 'ST000000000000000000002AMW42H', - contract_name: 'pox-3', + contract_name: 'pox-4', account_sequence: nonce, recent_block_hash: '0x969e494d5aee0166016836f97bbeb3d9473bea8427e477e9de253f78d3212354', }, @@ -2220,7 +2220,7 @@ describe('Rosetta Construction', () => { // //metadata const contract_address = 'ST000000000000000000002AMW42H'; - const contract_name = 'pox-3'; + const contract_name = 'pox-4'; const metadataRequest: RosettaConstructionMetadataRequest = { network_identifier: { diff --git a/src/tests-rosetta/offline-api-tests.ts b/src/tests-rosetta/offline-api-tests.ts index 73577fa3b4..499e95e6a4 100644 --- a/src/tests-rosetta/offline-api-tests.ts +++ b/src/tests-rosetta/offline-api-tests.ts @@ -822,7 +822,7 @@ describe('Rosetta offline API', () => { const sender = testnetKeys[0].stacksAddress; const fee = '270'; const contract_address = 'ST000000000000000000002AMW42H'; - const contract_name = 'pox-3'; + const contract_name = 'pox-4'; const stacking_amount = 5000; const burn_block_height = 200; const number_of_cycles = 5; @@ -957,7 +957,7 @@ describe('Rosetta offline API', () => { const sender = testnetKeys[0].stacksAddress; const fee = '270'; const contract_address = 'ST000000000000000000002AMW42H'; - const contract_name = 'pox-3'; + const contract_name = 'pox-4'; const stacking_amount = 5000; const burn_block_height = 200; diff --git a/src/tests-rpc/core-rpc-tests.ts b/src/tests-rpc/core-rpc-tests.ts index 22c6009794..cf4c155dfc 100644 --- a/src/tests-rpc/core-rpc-tests.ts +++ b/src/tests-rpc/core-rpc-tests.ts @@ -15,7 +15,7 @@ describe('core RPC tests', () => { test('get pox info', async () => { const poxInfo = await client.getPox(); - expect(poxInfo.contract_id).toBe(`ST000000000000000000002AMW42H.pox-3`); + expect(poxInfo.contract_id).toBe(`ST000000000000000000002AMW42H.pox-4`); }); test('get account nonce', async () => { From 6b7d5f459ad17e7691e078100288dbe67463c3d1 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sun, 10 Dec 2023 14:27:28 +0100 Subject: [PATCH 17/23] fix: tx fee fall back in faucet --- src/api/routes/faucets.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/api/routes/faucets.ts b/src/api/routes/faucets.ts index c3656c670f..10f9124cab 100644 --- a/src/api/routes/faucets.ts +++ b/src/api/routes/faucets.ts @@ -226,7 +226,11 @@ export function createFaucetRouter(db: PgWriteStore): express.Router { try { return await makeSTXTokenTransfer(txOpts); } catch (error: any) { - if (fee === undefined && (error as Error).message?.includes('NoEstimateAvailable')) { + if ( + fee === undefined && + (error as Error).message && + /estimating transaction fee|NoEstimateAvailable/.test(error.message) + ) { const defaultFee = 200n; return await generateTx(network, nonce, defaultFee); } From b7bf986fc017f808829eae0b26fc4e058e663352 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sun, 10 Dec 2023 14:31:26 +0100 Subject: [PATCH 18/23] test: update delegation check endpoints --- src/tests-2.4/pox-3-delegate-aggregation.ts | 2 +- src/tests-2.4/pox-3-delegate-stacking.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tests-2.4/pox-3-delegate-aggregation.ts b/src/tests-2.4/pox-3-delegate-aggregation.ts index c07d5ce1fa..ec2f1015a6 100644 --- a/src/tests-2.4/pox-3-delegate-aggregation.ts +++ b/src/tests-2.4/pox-3-delegate-aggregation.ts @@ -153,7 +153,7 @@ describe('PoX-3 - Delegate aggregation increase operations', () => { // validate pool delegations const stackersRes: any = await fetchGet( - `/extended/beta/stacking/${delegatorAccount.stxAddr}/delegations` + `/extended/v1/pox4/${delegatorAccount.stxAddr}/delegations` ); expect(stackersRes).toBeDefined(); expect(stackersRes.total).toBe(1); diff --git a/src/tests-2.4/pox-3-delegate-stacking.ts b/src/tests-2.4/pox-3-delegate-stacking.ts index 0faee2cb15..a4a70a34b5 100644 --- a/src/tests-2.4/pox-3-delegate-stacking.ts +++ b/src/tests-2.4/pox-3-delegate-stacking.ts @@ -162,7 +162,7 @@ describe('PoX-3 - Delegate Stacking operations', () => { // validate pool delegations const stackersRes: any = await fetchGet( - `/extended/beta/stacking/${delegatorAccount.stxAddr}/delegations` + `/extended/v1/pox4/${delegatorAccount.stxAddr}/delegations` ); expect(stackersRes).toBeDefined(); expect(stackersRes.total).toBe(1); @@ -177,7 +177,7 @@ describe('PoX-3 - Delegate Stacking operations', () => { // validate pool delegations respects `after_block` limitter const stackersRes2: any = await fetchGet( - `/extended/beta/stacking/${delegatorAccount.stxAddr}/delegations?after_block=${delegateStxDbTx.block_height}` + `/extended/v1/pox4/${delegatorAccount.stxAddr}/delegations?after_block=${delegateStxDbTx.block_height}` ); expect(stackersRes2).toBeDefined(); expect(stackersRes2.total).toBe(0); From ff6c6117de57dd014021d3924da0e332e52c448e Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sun, 10 Dec 2023 14:57:53 +0100 Subject: [PATCH 19/23] chore: isolate error in delegate-revoke test --- .../pox-3-delegate-revoked-stacking.ts | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/tests-2.4/pox-3-delegate-revoked-stacking.ts b/src/tests-2.4/pox-3-delegate-revoked-stacking.ts index a1c5be7485..4273241b36 100644 --- a/src/tests-2.4/pox-3-delegate-revoked-stacking.ts +++ b/src/tests-2.4/pox-3-delegate-revoked-stacking.ts @@ -74,6 +74,7 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { amount: gasAmount, network: testEnv.stacksNetwork, anchorMode: AnchorMode.OnChainOnly, + fee: 10000n, }); const { txId: stxXferId1 } = await testEnv.client.sendTransaction( Buffer.from(stxXfer1.serialize()) @@ -88,6 +89,7 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { network: testEnv.stacksNetwork, anchorMode: AnchorMode.OnChainOnly, nonce: stxXfer1.auth.spendingCondition.nonce + 1n, + fee: 10000n, }); const { txId: stxXferId2 } = await testEnv.client.sendTransaction( Buffer.from(stxXfer2.serialize()) @@ -138,6 +140,7 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { ], network: testEnv.stacksNetwork, anchorMode: AnchorMode.OnChainOnly, + fee: 10000n, }); const delegateStackTxResult = await testEnv.client.sendTransaction( Buffer.from(delegateStackTx.serialize()) @@ -163,6 +166,7 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { ], network: testEnv.stacksNetwork, anchorMode: AnchorMode.OnChainOnly, + fee: 10000n, }); const { txId: delegateStxTxId } = await testEnv.client.sendTransaction( Buffer.from(delegateStxTx.serialize()) @@ -222,6 +226,7 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { ], network: testEnv.stacksNetwork, anchorMode: AnchorMode.OnChainOnly, + fee: 10000n, }); const { txId: delegateStackStxTxId } = await testEnv.client.sendTransaction( Buffer.from(delegateStackStxTx.serialize()) @@ -271,9 +276,14 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { functionArgs: [], network: testEnv.stacksNetwork, anchorMode: AnchorMode.OnChainOnly, + fee: 10000n, }); const revokeTxResult = await testEnv.client.sendTransaction(Buffer.from(revokeTx.serialize())); - await standByForTxSuccess(revokeTxResult.txId); + const revokeStackDbTx = await standByForTxSuccess(revokeTxResult.txId); + + const revokeStackResult = decodeClarityValue(revokeStackDbTx.raw_result); + expect(revokeStackResult.repr).toEqual('(ok true)'); // ERR_STACKING_PERMISSION_DENIED + expect(revokeStackDbTx.status).toBe(DbTxStatus.Success); // revocation doesn't change anything for the previous delegate-stack-stx state const coreBalanceInfo = await testEnv.client.getAccount(STACKER.stxAddr); @@ -299,6 +309,7 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { ], network: testEnv.stacksNetwork, anchorMode: AnchorMode.OnChainOnly, + fee: 10000n, }); const delegateStackTxResult = await testEnv.client.sendTransaction( Buffer.from(delegateStackTx.serialize()) @@ -340,14 +351,15 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { ], network: testEnv.stacksNetwork, anchorMode: AnchorMode.OnChainOnly, + fee: 10000n, }); const delegateTxResult = await testEnv.client.sendTransaction( Buffer.from(delegateTx.serialize()) ); const delegateDbTx = await standByForTx(delegateTxResult.txId); - expect(delegateDbTx.status).not.toBe(DbTxStatus.Success); const delegateResult = decodeClarityValue(delegateDbTx.raw_result); expect(delegateResult.repr).toEqual('(err 3)'); // ERR_STACKING_ALREADY_STACKED + expect(delegateDbTx.status).not.toBe(DbTxStatus.Success); }); test('Try to perform delegate-stack-increase - without delegation', async () => { @@ -363,16 +375,17 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { ], network: testEnv.stacksNetwork, anchorMode: AnchorMode.OnChainOnly, + fee: 10000n, }); const { txId: delegateStackIncreaseTxId } = await testEnv.client.sendTransaction( Buffer.from(delegateStackIncreaseTx.serialize()) ); const delegateStackIncreaseTxResult = await standByForTx(delegateStackIncreaseTxId); - expect(delegateStackIncreaseTxResult.status).not.toBe(DbTxStatus.Success); const delegateStackIncreaseResult = decodeClarityValue( delegateStackIncreaseTxResult.raw_result ); expect(delegateStackIncreaseResult.repr).toEqual('(err 9)'); // ERR_STACKING_PERMISSION_DENIED + expect(delegateStackIncreaseTxResult.status).not.toBe(DbTxStatus.Success); }); test('Try to perform delegate-stack-extend - without delegation', async () => { @@ -388,14 +401,15 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { ], network: testEnv.stacksNetwork, anchorMode: AnchorMode.OnChainOnly, + fee: 10000n, }); const { txId: delegateStackextendTxId } = await testEnv.client.sendTransaction( Buffer.from(delegateStackextendTx.serialize()) ); const delegateStackextendTxResult = await standByForTx(delegateStackextendTxId); - expect(delegateStackextendTxResult.status).not.toBe(DbTxStatus.Success); const delegateStackextendResult = decodeClarityValue(delegateStackextendTxResult.raw_result); expect(delegateStackextendResult.repr).toEqual('(err 9)'); // ERR_STACKING_PERMISSION_DENIED + expect(delegateStackextendTxResult.status).not.toBe(DbTxStatus.Success); }); test('Try to perform delegate-stack-stx - without delegation', async () => { @@ -416,6 +430,7 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { ], network: testEnv.stacksNetwork, anchorMode: AnchorMode.OnChainOnly, + fee: 10000n, }); const { txId: delegateStackStxTxId } = await testEnv.client.sendTransaction( Buffer.from(delegateStackStxTx.serialize()) @@ -441,6 +456,7 @@ describe('PoX-3 - Delegate Revoked Stacking', () => { ], network: testEnv.stacksNetwork, anchorMode: AnchorMode.OnChainOnly, + fee: 10000n, }); const { txId: stackAggrCommitTxId } = await testEnv.client.sendTransaction( Buffer.from(stackAggrCommitTx.serialize()) From fbdf8221ec7202f06780b4f3e669fa6c502e3a19 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sun, 10 Dec 2023 15:06:43 +0100 Subject: [PATCH 20/23] chore: pox3 to pox4 misc renames --- .github/workflows/ci.yml | 24 +++++++++---------- .vscode/launch.json | 4 ++-- package.json | 4 ++-- .../block-zero-handling.ts | 0 src/{tests-2.4 => tests-2.5}/env-setup.ts | 0 src/{tests-2.4 => tests-2.5}/faucet-stx.ts | 0 src/{tests-2.4 => tests-2.5}/global-setup.ts | 0 .../global-teardown.ts | 0 .../pox-4-btc-address-formats.ts} | 10 ++++---- .../pox-4-burnchain-delegate-stx.ts} | 2 +- .../pox-4-burnchain-stack-stx.ts} | 2 +- .../pox-4-delegate-aggregation.ts} | 2 +- .../pox-4-delegate-revoked-stacking.ts} | 2 +- .../pox-4-delegate-stacking.ts} | 2 +- .../pox-4-rosetta-btc-addr-types.ts} | 2 +- .../pox-4-rosetta-cycle-phases.ts} | 2 +- .../pox-4-rosetta-segwit.ts} | 2 +- .../pox-4-stack-extend-increase.ts} | 2 +- ...{jest.config.2.4.js => jest.config.2.5.js} | 16 ++++++------- 19 files changed, 38 insertions(+), 38 deletions(-) rename src/{tests-2.4 => tests-2.5}/block-zero-handling.ts (100%) rename src/{tests-2.4 => tests-2.5}/env-setup.ts (100%) rename src/{tests-2.4 => tests-2.5}/faucet-stx.ts (100%) rename src/{tests-2.4 => tests-2.5}/global-setup.ts (100%) rename src/{tests-2.4 => tests-2.5}/global-teardown.ts (100%) rename src/{tests-2.4/pox-3-btc-address-formats.ts => tests-2.5/pox-4-btc-address-formats.ts} (99%) rename src/{tests-2.4/pox-3-burnchain-delegate-stx.ts => tests-2.5/pox-4-burnchain-delegate-stx.ts} (99%) rename src/{tests-2.4/pox-3-burnchain-stack-stx.ts => tests-2.5/pox-4-burnchain-stack-stx.ts} (99%) rename src/{tests-2.4/pox-3-delegate-aggregation.ts => tests-2.5/pox-4-delegate-aggregation.ts} (99%) rename src/{tests-2.4/pox-3-delegate-revoked-stacking.ts => tests-2.5/pox-4-delegate-revoked-stacking.ts} (99%) rename src/{tests-2.4/pox-3-delegate-stacking.ts => tests-2.5/pox-4-delegate-stacking.ts} (99%) rename src/{tests-2.4/pox-3-rosetta-btc-addr-types.ts => tests-2.5/pox-4-rosetta-btc-addr-types.ts} (97%) rename src/{tests-2.4/pox-3-rosetta-cycle-phases.ts => tests-2.5/pox-4-rosetta-cycle-phases.ts} (96%) rename src/{tests-2.4/pox-3-rosetta-segwit.ts => tests-2.5/pox-4-rosetta-segwit.ts} (99%) rename src/{tests-2.4/pox-3-stack-extend-increase.ts => tests-2.5/pox-4-stack-extend-increase.ts} (99%) rename tests/{jest.config.2.4.js => jest.config.2.5.js} (51%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 789774a203..1c7b32333b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -520,7 +520,7 @@ jobs: flag-name: run-${{ github.job }} parallel: true - test-2_4: + test-2_5: strategy: fail-fast: false matrix: @@ -528,16 +528,16 @@ jobs: [ block-zero-handling, faucet-stx, - pox-3-btc-address-formats, - pox-3-delegate-aggregation, - pox-3-delegate-stacking, - pox-3-delegate-revoked-stacking, - pox-3-stack-extend-increase, - pox-3-rosetta-btc-addr-types, - pox-3-rosetta-cycle-phases, - pox-3-rosetta-segwit, - pox-3-burnchain-stack-stx, - pox-3-burnchain-delegate-stx, + pox-4-btc-address-formats, + pox-4-delegate-aggregation, + pox-4-delegate-stacking, + pox-4-delegate-revoked-stacking, + pox-4-stack-extend-increase, + pox-4-rosetta-btc-addr-types, + pox-4-rosetta-cycle-phases, + pox-4-rosetta-segwit, + pox-4-burnchain-stack-stx, + pox-4-burnchain-delegate-stx, ] runs-on: ubuntu-latest steps: @@ -575,7 +575,7 @@ jobs: npm run devenv:logs-krypton -- --no-color &> docker-compose-logs.txt & - name: Run tests - run: npm run test:2.4 -- --testPathPattern "${{ matrix.suite }}" + run: npm run test:2.5 -- --testPathPattern "${{ matrix.suite }}" - name: Print integration environment logs run: cat docker-compose-logs.txt diff --git a/.vscode/launch.json b/.vscode/launch.json index 845f9a4802..42580ff805 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -316,14 +316,14 @@ { "type": "node", "request": "launch", - "name": "Jest: 2.4", + "name": "Jest: 2.5", "program": "${workspaceFolder}/node_modules/.bin/jest", "args": [ "--testTimeout=3600000", "--runInBand", "--no-cache", "--config", - "${workspaceRoot}/tests/jest.config.2.4.js" + "${workspaceRoot}/tests/jest.config.2.5.js", ], "outputCapture": "std", "console": "integratedTerminal", diff --git a/package.json b/package.json index 5541d46493..da1ae344c2 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "dev:follower": "npm run devenv:build && concurrently npm:dev npm:devenv:follower", "test": "cross-env NODE_ENV=test jest --config ./tests/jest.config.js --coverage --runInBand", "test:subnets": "cross-env NODE_ENV=test jest --config ./tests/jest.config.subnets.js --coverage --runInBand", - "test:2.4": "cross-env NODE_ENV=test jest --config ./tests/jest.config.2.4.js --coverage --runInBand", + "test:2.5": "cross-env NODE_ENV=test jest --config ./tests/jest.config.2.5.js --coverage --runInBand", "test:rosetta": "cross-env NODE_ENV=test jest --config ./tests/jest.config.rosetta.js --coverage --runInBand", "test:rosetta-construction": "cross-env NODE_ENV=test jest --config ./tests/jest.config.rosetta-construction.js --coverage --runInBand", "test:rosetta-cli:data": "cross-env NODE_ENV=test STACKS_CHAIN_ID=0x80000000 jest --config ./tests/jest.config.rosetta-cli-data.js --coverage --runInBand", @@ -24,7 +24,7 @@ "test:watch": "cross-env NODE_ENV=test jest --config ./tests/jest.config.js --watch", "test:integration": "concurrently \"docker compose -f docker/docker-compose.dev.postgres.yml up --force-recreate -V\" \"cross-env NODE_ENV=test jest --config ./tests/jest.config.js --no-cache --runInBand; npm run devenv:stop:pg\"", "test:integration:subnets": "concurrently --hide \"devenv:deploy:subnets\" \"npm:devenv:deploy:subnets\" \"cross-env NODE_ENV=test jest --config ./tests/jest.config.subnets.js --no-cache --runInBand; npm run devenv:stop:subnets\"", - "test:integration:2.4": "concurrently --hide \"devenv:deploy-krypton\" \"npm:devenv:deploy-krypton\" \"cross-env NODE_ENV=test jest --config ./tests/jest.config.2.4.js --no-cache --runInBand; npm run devenv:stop-krypton\"", + "test:integration:2.5": "concurrently --hide \"devenv:deploy-krypton\" \"npm:devenv:deploy-krypton\" \"cross-env NODE_ENV=test jest --config ./tests/jest.config.2.5.js --no-cache --runInBand; npm run devenv:stop-krypton\"", "test:integration:rosetta": "concurrently \"npm:devenv:deploy-krypton\" \"cross-env NODE_ENV=test jest --config ./tests/jest.config.rosetta.js --no-cache --runInBand; npm run devenv:stop-krypton\"", "test:integration:rosetta-construction": "concurrently \"npm:devenv:deploy-krypton\" \"cross-env NODE_ENV=test jest --config ./tests/jest.config.rosetta-construction.js --no-cache --runInBand; npm run devenv:stop-krypton\"", "test:integration:rosetta-cli:data": "concurrently \"npm:devenv:deploy-krypton\" \"cross-env NODE_ENV=test STACKS_CHAIN_ID=0x80000000 jest --config ./tests/jest.config.rosetta-cli-data.js --no-cache --runInBand; npm run devenv:stop-krypton\"", diff --git a/src/tests-2.4/block-zero-handling.ts b/src/tests-2.5/block-zero-handling.ts similarity index 100% rename from src/tests-2.4/block-zero-handling.ts rename to src/tests-2.5/block-zero-handling.ts diff --git a/src/tests-2.4/env-setup.ts b/src/tests-2.5/env-setup.ts similarity index 100% rename from src/tests-2.4/env-setup.ts rename to src/tests-2.5/env-setup.ts diff --git a/src/tests-2.4/faucet-stx.ts b/src/tests-2.5/faucet-stx.ts similarity index 100% rename from src/tests-2.4/faucet-stx.ts rename to src/tests-2.5/faucet-stx.ts diff --git a/src/tests-2.4/global-setup.ts b/src/tests-2.5/global-setup.ts similarity index 100% rename from src/tests-2.4/global-setup.ts rename to src/tests-2.5/global-setup.ts diff --git a/src/tests-2.4/global-teardown.ts b/src/tests-2.5/global-teardown.ts similarity index 100% rename from src/tests-2.4/global-teardown.ts rename to src/tests-2.5/global-teardown.ts diff --git a/src/tests-2.4/pox-3-btc-address-formats.ts b/src/tests-2.5/pox-4-btc-address-formats.ts similarity index 99% rename from src/tests-2.4/pox-3-btc-address-formats.ts rename to src/tests-2.5/pox-4-btc-address-formats.ts index f3c75dceb2..07431ae8b6 100644 --- a/src/tests-2.4/pox-3-btc-address-formats.ts +++ b/src/tests-2.5/pox-4-btc-address-formats.ts @@ -21,13 +21,13 @@ import { } from '../test-utils/test-helpers'; import { hexToBuffer } from '@hirosystems/api-toolkit'; -describe('PoX-3 - Stack using supported bitcoin address formats', () => { +describe('PoX-4 - Stack using supported bitcoin address formats', () => { test('Standby for next cycle', async () => { const poxInfo = await testEnv.client.getPox(); await standByUntilBurnBlock(poxInfo.next_cycle.reward_phase_start_block_height); // a good time to stack }); - describe('PoX-3 - Stacking operations P2SH-P2WPKH', () => { + describe('PoX-4 - Stacking operations P2SH-P2WPKH', () => { const account = testnetKeys[1]; let btcAddr: string; let btcRegtestAccount: VerboseKeyOutput; @@ -276,7 +276,7 @@ describe('PoX-3 - Stack using supported bitcoin address formats', () => { }); }); - describe('PoX-3 - Stacking operations P2WPKH', () => { + describe('PoX-4 - Stacking operations P2WPKH', () => { const account = testnetKeys[1]; let btcAddr: string; let btcRegtestAddr: string; @@ -524,7 +524,7 @@ describe('PoX-3 - Stack using supported bitcoin address formats', () => { }); }); - describe('PoX-3 - Stacking operations P2WSH', () => { + describe('PoX-4 - Stacking operations P2WSH', () => { const account = testnetKeys[1]; let btcAddr: string; let btcRegtestAddr: string; @@ -776,7 +776,7 @@ describe('PoX-3 - Stack using supported bitcoin address formats', () => { }); }); - describe('PoX-3 - Stacking operations P2TR', () => { + describe('PoX-4 - Stacking operations P2TR', () => { const account = testnetKeys[2]; let btcAddr: string; let btcRegtestAddr: string; diff --git a/src/tests-2.4/pox-3-burnchain-delegate-stx.ts b/src/tests-2.5/pox-4-burnchain-delegate-stx.ts similarity index 99% rename from src/tests-2.4/pox-3-burnchain-delegate-stx.ts rename to src/tests-2.5/pox-4-burnchain-delegate-stx.ts index d0163f15d8..3992482548 100644 --- a/src/tests-2.4/pox-3-burnchain-delegate-stx.ts +++ b/src/tests-2.5/pox-4-burnchain-delegate-stx.ts @@ -172,7 +172,7 @@ async function createPox2DelegateStx(args: { }; } -describe('PoX-3 - Stack using Bitcoin-chain ops', () => { +describe('PoX-4 - Stack using Bitcoin-chain ops', () => { const seedAccount = testnetKeys[0]; let db: PgWriteStore; diff --git a/src/tests-2.4/pox-3-burnchain-stack-stx.ts b/src/tests-2.5/pox-4-burnchain-stack-stx.ts similarity index 99% rename from src/tests-2.4/pox-3-burnchain-stack-stx.ts rename to src/tests-2.5/pox-4-burnchain-stack-stx.ts index 9fed5ec179..1667d4a00a 100644 --- a/src/tests-2.4/pox-3-burnchain-stack-stx.ts +++ b/src/tests-2.5/pox-4-burnchain-stack-stx.ts @@ -134,7 +134,7 @@ async function createPox2StackStx(args: { }; } -describe('PoX-3 - Stack using Bitcoin-chain ops', () => { +describe('PoX-4 - Stack using Bitcoin-chain ops', () => { const seedAccount = testnetKeys[0]; let db: PgWriteStore; diff --git a/src/tests-2.4/pox-3-delegate-aggregation.ts b/src/tests-2.5/pox-4-delegate-aggregation.ts similarity index 99% rename from src/tests-2.4/pox-3-delegate-aggregation.ts rename to src/tests-2.5/pox-4-delegate-aggregation.ts index ec2f1015a6..4d48151086 100644 --- a/src/tests-2.4/pox-3-delegate-aggregation.ts +++ b/src/tests-2.5/pox-4-delegate-aggregation.ts @@ -32,7 +32,7 @@ import { } from 'stacks-encoding-native-js'; import { AddressStxBalanceResponse } from '@stacks/stacks-blockchain-api-types'; -describe('PoX-3 - Delegate aggregation increase operations', () => { +describe('PoX-4 - Delegate aggregation increase operations', () => { const seedKey = testnetKeys[4].secretKey; const delegatorKey = '04608922f3ce63971bb120fa9c9454c5bd06370f61414040a737a6ee8ef8a10f01'; const delegateeKey = 'b038e143cf4ee4c079b3c3605a8ed28732e5745c138b728408e80faf7a59b8c201'; diff --git a/src/tests-2.4/pox-3-delegate-revoked-stacking.ts b/src/tests-2.5/pox-4-delegate-revoked-stacking.ts similarity index 99% rename from src/tests-2.4/pox-3-delegate-revoked-stacking.ts rename to src/tests-2.5/pox-4-delegate-revoked-stacking.ts index 4273241b36..492c55b5d2 100644 --- a/src/tests-2.4/pox-3-delegate-revoked-stacking.ts +++ b/src/tests-2.5/pox-4-delegate-revoked-stacking.ts @@ -30,7 +30,7 @@ import { testEnv, } from '../test-utils/test-helpers'; -describe('PoX-3 - Delegate Revoked Stacking', () => { +describe('PoX-4 - Delegate Revoked Stacking', () => { const seedKey = testnetKeys[4].secretKey; const delegatorKey = '72e8e3725324514c38c2931ed337ab9ab8d8abaae83ed2275456790194b1fd3101'; const delegateeKey = '0d174cf0be276cedcf21727611ef2504aed093d8163f65985c07760fda12a7ea01'; diff --git a/src/tests-2.4/pox-3-delegate-stacking.ts b/src/tests-2.5/pox-4-delegate-stacking.ts similarity index 99% rename from src/tests-2.4/pox-3-delegate-stacking.ts rename to src/tests-2.5/pox-4-delegate-stacking.ts index a4a70a34b5..8274cd12d4 100644 --- a/src/tests-2.4/pox-3-delegate-stacking.ts +++ b/src/tests-2.5/pox-4-delegate-stacking.ts @@ -24,7 +24,7 @@ import { import { ClarityValueTuple, ClarityValueUInt } from 'stacks-encoding-native-js'; import { AddressStxBalanceResponse } from '@stacks/stacks-blockchain-api-types'; -describe('PoX-3 - Delegate Stacking operations', () => { +describe('PoX-4 - Delegate Stacking operations', () => { const seedKey = testnetKeys[4].secretKey; const delegatorKey = '72e8e3725324514c38c2931ed337ab9ab8d8abaae83ed2275456790194b1fd3101'; const delegateeKey = '0d174cf0be276cedcf21727611ef2504aed093d8163f65985c07760fda12a7ea01'; diff --git a/src/tests-2.4/pox-3-rosetta-btc-addr-types.ts b/src/tests-2.5/pox-4-rosetta-btc-addr-types.ts similarity index 97% rename from src/tests-2.4/pox-3-rosetta-btc-addr-types.ts rename to src/tests-2.5/pox-4-rosetta-btc-addr-types.ts index d59e0c7eff..ffa65abe7e 100644 --- a/src/tests-2.4/pox-3-rosetta-btc-addr-types.ts +++ b/src/tests-2.5/pox-4-rosetta-btc-addr-types.ts @@ -21,7 +21,7 @@ const BTC_ADDRESS_CASES = [ ] as const; describe.each(BTC_ADDRESS_CASES)( - 'PoX-3 - Rosetta - Stack with BTC address format $addressFormat', + 'PoX-4 - Rosetta - Stack with BTC address format $addressFormat', ({ addressFormat }) => { let poxInfo: CoreRpcPoxInfo; const account = testnetKeys[1]; diff --git a/src/tests-2.4/pox-3-rosetta-cycle-phases.ts b/src/tests-2.5/pox-4-rosetta-cycle-phases.ts similarity index 96% rename from src/tests-2.4/pox-3-rosetta-cycle-phases.ts rename to src/tests-2.5/pox-4-rosetta-cycle-phases.ts index 6305cc648f..c117f031f1 100644 --- a/src/tests-2.4/pox-3-rosetta-cycle-phases.ts +++ b/src/tests-2.5/pox-4-rosetta-cycle-phases.ts @@ -11,7 +11,7 @@ const account = testnetKeys[1]; const btcAddr = '2N74VLxyT79VGHiBK2zEg3a9HJG7rEc5F3o'; describe.each(BLOCK_SHIFT_COUNT)( - 'PoX-3 - Rosetta - Stack on any phase of cycle $shift', + 'PoX-4 - Rosetta - Stack on any phase of cycle $shift', ({ shift }) => { test('Standby for cycle phase', async () => { const poxInfo = await testEnv.client.getPox(); diff --git a/src/tests-2.4/pox-3-rosetta-segwit.ts b/src/tests-2.5/pox-4-rosetta-segwit.ts similarity index 99% rename from src/tests-2.4/pox-3-rosetta-segwit.ts rename to src/tests-2.5/pox-4-rosetta-segwit.ts index 05bcd1e4c4..c7f155cf5a 100644 --- a/src/tests-2.4/pox-3-rosetta-segwit.ts +++ b/src/tests-2.5/pox-4-rosetta-segwit.ts @@ -27,7 +27,7 @@ import { } from '../test-utils/test-helpers'; import { hexToBuffer } from '@hirosystems/api-toolkit'; -describe('PoX-3 - Rosetta - Stacking with segwit', () => { +describe('PoX-4 - Rosetta - Stacking with segwit', () => { let btcAddr: string; let btcAddrTestnet: string; const seedAccount = testnetKeys[0]; diff --git a/src/tests-2.4/pox-3-stack-extend-increase.ts b/src/tests-2.5/pox-4-stack-extend-increase.ts similarity index 99% rename from src/tests-2.4/pox-3-stack-extend-increase.ts rename to src/tests-2.5/pox-4-stack-extend-increase.ts index 1cb492d7ff..8fe0bf1ef7 100644 --- a/src/tests-2.4/pox-3-stack-extend-increase.ts +++ b/src/tests-2.5/pox-4-stack-extend-increase.ts @@ -21,7 +21,7 @@ import { import { decodeBtcAddress } from '@stacks/stacking'; import { hexToBuffer } from '@hirosystems/api-toolkit'; -describe('PoX-3 - Stack extend and increase operations', () => { +describe('PoX-4 - Stack extend and increase operations', () => { const account = testnetKeys[1]; let btcAddr: string; let btcRegtestAccount: VerboseKeyOutput; diff --git a/tests/jest.config.2.4.js b/tests/jest.config.2.5.js similarity index 51% rename from tests/jest.config.2.4.js rename to tests/jest.config.2.5.js index 319503527d..65102bd234 100644 --- a/tests/jest.config.2.4.js +++ b/tests/jest.config.2.5.js @@ -3,19 +3,19 @@ const config = { preset: 'ts-jest', testEnvironment: 'node', rootDir: `${require('path').dirname(__dirname)}/src`, - testMatch: ['/tests-2.4/**/*.ts'], + testMatch: ['/tests-2.5/**/*.ts'], testPathIgnorePatterns: [ - '/tests-2.4/global-setup.ts', - '/tests-2.4/global-teardown.ts', - '/tests-2.4/env-setup.ts', - '/tests-2.4/test-helpers.ts', + '/tests-2.5/global-setup.ts', + '/tests-2.5/global-teardown.ts', + '/tests-2.5/env-setup.ts', + '/tests-2.5/test-helpers.ts', ], collectCoverageFrom: ['/**/*.ts'], coveragePathIgnorePatterns: ['/tests*'], coverageDirectory: '/../coverage', - globalSetup: '/tests-2.4/global-setup.ts', - globalTeardown: '/tests-2.4/global-teardown.ts', - setupFilesAfterEnv: ['/tests-2.4/env-setup.ts'], + globalSetup: '/tests-2.5/global-setup.ts', + globalTeardown: '/tests-2.5/global-teardown.ts', + setupFilesAfterEnv: ['/tests-2.5/env-setup.ts'], testTimeout: 60_000, verbose: true, bail: true, From d5fb7d14901251850069d85b00abc716e8787146 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sun, 10 Dec 2023 15:12:26 +0100 Subject: [PATCH 21/23] ci: rename 2.4 to 2.5 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c7b32333b..0a3d205330 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -855,7 +855,7 @@ jobs: - lint - lint-docs - test - - test-2_4 + - test-2_5 - test-bns - test-rosetta - test-rosetta-cli-construction From f8b4aa8148ba0237b90b042908899a73c09b0062 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Sun, 10 Dec 2023 17:08:21 +0100 Subject: [PATCH 22/23] test: remove no-longer applicable delegate-stx while stacking test --- .../pox-4-delegate-revoked-stacking.ts | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/src/tests-2.5/pox-4-delegate-revoked-stacking.ts b/src/tests-2.5/pox-4-delegate-revoked-stacking.ts index 492c55b5d2..e2eeac0032 100644 --- a/src/tests-2.5/pox-4-delegate-revoked-stacking.ts +++ b/src/tests-2.5/pox-4-delegate-revoked-stacking.ts @@ -320,48 +320,6 @@ describe('PoX-4 - Delegate Revoked Stacking', () => { expect(delegateStackResult.repr).toEqual('(err 9)'); // ERR_STACKING_PERMISSION_DENIED }); - test('Try to perform delegate-stx - again', async () => { - // make sure stacker is not currently delegating - await expect( - readOnlyFnCall( - [contractAddress, contractName], - 'get-delegation-info', - [standardPrincipalCV(STACKER.stxAddr)], - STACKER.stxAddr - ) - ).rejects.toThrowError( - 'OptionNone result for call to ST000000000000000000002AMW42H,pox-4::get-delegation-info' - ); - - // but stacker is still locked - const coreBalanceInfo = await testEnv.client.getAccount(STACKER.stxAddr); - expect(BigInt(coreBalanceInfo.locked)).toBe(DELEGATE_HALF_AMOUNT); - - // delegate with the full amount - const delegateTx = await makeContractCall({ - senderKey: STACKER.secretKey, - contractAddress, - contractName, - functionName: 'delegate-stx', - functionArgs: [ - uintCV(DELEGATE_FULL_AMOUNT), - standardPrincipalCV(POOL.stxAddr), // delegate-to - noneCV(), // untilBurnBlockHeight - someCV(STACKER.poxAddrClar), // pox-addr - ], - network: testEnv.stacksNetwork, - anchorMode: AnchorMode.OnChainOnly, - fee: 10000n, - }); - const delegateTxResult = await testEnv.client.sendTransaction( - Buffer.from(delegateTx.serialize()) - ); - const delegateDbTx = await standByForTx(delegateTxResult.txId); - const delegateResult = decodeClarityValue(delegateDbTx.raw_result); - expect(delegateResult.repr).toEqual('(err 3)'); // ERR_STACKING_ALREADY_STACKED - expect(delegateDbTx.status).not.toBe(DbTxStatus.Success); - }); - test('Try to perform delegate-stack-increase - without delegation', async () => { const delegateStackIncreaseTx = await makeContractCall({ senderKey: POOL.secretKey, From d8c91d59d8aaefb45ba410a5328b5f5034eee7b3 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Tue, 12 Dec 2023 16:16:32 +0100 Subject: [PATCH 23/23] chore: remove incorrect commend in pox4 test --- src/tests-2.5/pox-4-delegate-revoked-stacking.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests-2.5/pox-4-delegate-revoked-stacking.ts b/src/tests-2.5/pox-4-delegate-revoked-stacking.ts index e2eeac0032..d29f4c8e76 100644 --- a/src/tests-2.5/pox-4-delegate-revoked-stacking.ts +++ b/src/tests-2.5/pox-4-delegate-revoked-stacking.ts @@ -282,7 +282,7 @@ describe('PoX-4 - Delegate Revoked Stacking', () => { const revokeStackDbTx = await standByForTxSuccess(revokeTxResult.txId); const revokeStackResult = decodeClarityValue(revokeStackDbTx.raw_result); - expect(revokeStackResult.repr).toEqual('(ok true)'); // ERR_STACKING_PERMISSION_DENIED + expect(revokeStackResult.repr).toEqual('(ok true)'); expect(revokeStackDbTx.status).toBe(DbTxStatus.Success); // revocation doesn't change anything for the previous delegate-stack-stx state