Skip to content

Commit 20b75d4

Browse files
committed
chore: merge develop into master-develop
2 parents 984362f + 0203d36 commit 20b75d4

29 files changed

+516
-230
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,7 @@ jobs:
530530
type=ref,event=pr
531531
type=semver,pattern={{version}},value=${{ steps.semantic.outputs.new_release_version }},enable=${{ steps.semantic.outputs.new_release_version != '' }}
532532
type=semver,pattern={{major}}.{{minor}},value=${{ steps.semantic.outputs.new_release_version }},enable=${{ steps.semantic.outputs.new_release_version != '' }}
533+
type=raw,value=latest,enable={{is_default_branch}}
533534
534535
- name: Docker Standalone Meta
535536
id: meta_standalone
@@ -543,6 +544,7 @@ jobs:
543544
type=ref,event=pr
544545
type=semver,pattern={{version}},value=${{ steps.semantic.outputs.new_release_version }},enable=${{ steps.semantic.outputs.new_release_version != '' }}
545546
type=semver,pattern={{major}}.{{minor}},value=${{ steps.semantic.outputs.new_release_version }},enable=${{ steps.semantic.outputs.new_release_version != '' }}
547+
type=raw,value=latest,enable={{is_default_branch}}
546548
547549
- name: Login to DockerHub
548550
uses: docker/login-action@v1

.vscode/launch.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,23 @@
155155
"preLaunchTask": "stacks-node:deploy-dev",
156156
"postDebugTask": "stacks-node:stop-dev"
157157
},
158+
{
159+
"type": "node",
160+
"request": "launch",
161+
"name": "Jest: Event Replay",
162+
"program": "${workspaceFolder}/node_modules/.bin/jest",
163+
"args": [
164+
"--testTimeout=3600000",
165+
"--runInBand",
166+
"--no-cache",
167+
"--config",
168+
"${workspaceRoot}/jest.config.event-replay.js"
169+
],
170+
"outputCapture": "std",
171+
"console": "integratedTerminal",
172+
"preLaunchTask": "stacks-node:deploy-dev",
173+
"postDebugTask": "stacks-node:stop-dev"
174+
},
158175
{
159176
"type": "node",
160177
"request": "launch",

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
* sql transaction consistency ([#1410](https://github.com/hirosystems/stacks-blockchain-api/issues/1410)) ([01e26d9](https://github.com/hirosystems/stacks-blockchain-api/commit/01e26d9c89472c8e07ee9d44372d3de86ee0fdb0))
7777

7878

79+
7980
## [6.1.1](https://github.com/hirosystems/stacks-blockchain-api/compare/v6.1.0...v6.1.1) (2022-10-24)
8081

8182

jest.config.event-replay.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module.exports = {
2+
preset: 'ts-jest',
3+
rootDir: 'src',
4+
testMatch: ['<rootDir>/tests-event-replay/**/*.ts'],
5+
testPathIgnorePatterns: [
6+
'<rootDir>/tests-event-replay/setup.ts',
7+
'<rootDir>/tests-event-replay/teardown.ts',
8+
],
9+
collectCoverageFrom: ['<rootDir>/**/*.ts'],
10+
coveragePathIgnorePatterns: ['<rootDir>/tests*'],
11+
coverageDirectory: '../coverage',
12+
globalSetup: '<rootDir>/tests-event-replay/setup.ts',
13+
globalTeardown: '<rootDir>/tests-event-replay/teardown.ts',
14+
testTimeout: 20000,
15+
};

src/api/pagination.ts

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,58 @@ export function parsePagingQueryInput(val: any) {
2525
return parsedInput;
2626
}
2727

28-
interface ParseLimitQueryParams {
29-
maxItems: number;
30-
errorMsg: string;
28+
export enum ResourceType {
29+
Block,
30+
Tx,
31+
Event,
32+
Burnchain,
33+
Contract,
34+
Microblock,
35+
Token,
3136
}
3237

33-
export function parseLimitQuery({ maxItems, errorMsg }: ParseLimitQueryParams) {
34-
return (val: any) => {
35-
const limit = parsePagingQueryInput(val);
36-
if (limit > maxItems)
37-
throw new InvalidRequestError(errorMsg, InvalidRequestErrorType.invalid_query);
38-
return limit;
39-
};
38+
const pagingQueryLimits: Record<ResourceType, { defaultLimit: number; maxLimit: number }> = {
39+
[ResourceType.Block]: {
40+
defaultLimit: 20,
41+
maxLimit: 30,
42+
},
43+
[ResourceType.Tx]: {
44+
defaultLimit: 20,
45+
maxLimit: 50,
46+
},
47+
[ResourceType.Event]: {
48+
defaultLimit: 20,
49+
maxLimit: 50,
50+
},
51+
[ResourceType.Burnchain]: {
52+
defaultLimit: 96,
53+
maxLimit: 250,
54+
},
55+
[ResourceType.Contract]: {
56+
defaultLimit: 20,
57+
maxLimit: 50,
58+
},
59+
[ResourceType.Microblock]: {
60+
defaultLimit: 20,
61+
maxLimit: 200,
62+
},
63+
[ResourceType.Token]: {
64+
defaultLimit: 50,
65+
maxLimit: 200,
66+
},
67+
};
68+
69+
export function getPagingQueryLimit(resourceType: ResourceType, limitOverride?: any) {
70+
const pagingQueryLimit = pagingQueryLimits[resourceType];
71+
if (!limitOverride) {
72+
return pagingQueryLimit.defaultLimit;
73+
}
74+
const newLimit = parsePagingQueryInput(limitOverride);
75+
if (newLimit > pagingQueryLimit.maxLimit) {
76+
throw new InvalidRequestError(
77+
`'limit' must be equal to or less than ${pagingQueryLimit.maxLimit}`,
78+
InvalidRequestErrorType.invalid_query
79+
);
80+
}
81+
return newLimit;
4082
}

src/api/routes/address.ts

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as express from 'express';
22
import { asyncHandler } from '../async-handler';
33
import * as Bluebird from 'bluebird';
44
import { BlockIdentifier } from '../../datastore/common';
5-
import { parseLimitQuery, parsePagingQueryInput } from '../pagination';
5+
import { getPagingQueryLimit, parsePagingQueryInput, ResourceType } from '../pagination';
66
import {
77
isUnanchoredRequest,
88
getBlockParams,
@@ -52,25 +52,6 @@ import {
5252
import { PgStore } from '../../datastore/pg-store';
5353
import { PgSqlClient } from '../../datastore/connection';
5454

55-
const MAX_TX_PER_REQUEST = 50;
56-
const MAX_ASSETS_PER_REQUEST = 50;
57-
const MAX_STX_INBOUND_PER_REQUEST = 500;
58-
59-
const parseTxQueryLimit = parseLimitQuery({
60-
maxItems: MAX_TX_PER_REQUEST,
61-
errorMsg: '`limit` must be equal to or less than ' + MAX_TX_PER_REQUEST,
62-
});
63-
64-
const parseAssetsQueryLimit = parseLimitQuery({
65-
maxItems: MAX_ASSETS_PER_REQUEST,
66-
errorMsg: '`limit` must be equal to or less than ' + MAX_ASSETS_PER_REQUEST,
67-
});
68-
69-
const parseStxInboundLimit = parseLimitQuery({
70-
maxItems: MAX_STX_INBOUND_PER_REQUEST,
71-
errorMsg: '`limit` must be equal to or less than ' + MAX_STX_INBOUND_PER_REQUEST,
72-
});
73-
7455
async function getBlockHeight(
7556
untilBlock: number | string | undefined,
7657
req: Request,
@@ -233,7 +214,7 @@ export function createAddressRouter(db: PgStore, chainId: ChainID): express.Rout
233214
const principal = req.params['principal'];
234215
validatePrincipal(principal);
235216
const untilBlock = parseUntilBlockQuery(req, res, next);
236-
const limit = parseTxQueryLimit(req.query.limit ?? 20);
217+
const limit = getPagingQueryLimit(ResourceType.Tx, req.query.limit);
237218
const offset = parsePagingQueryInput(req.query.offset ?? 0);
238219

239220
const response = await db.sqlTransaction(async sql => {
@@ -336,7 +317,7 @@ export function createAddressRouter(db: PgStore, chainId: ChainID): express.Rout
336317
} else {
337318
blockHeight = await getBlockHeight(untilBlock, req, res, next, db);
338319
}
339-
const limit = parseTxQueryLimit(req.query.limit ?? 20);
320+
const limit = getPagingQueryLimit(ResourceType.Tx, req.query.limit);
340321
const offset = parsePagingQueryInput(req.query.offset ?? 0);
341322
const { results: txResults, total } = await db.getAddressTxsWithAssetTransfers({
342323
stxAddress: stxAddress,
@@ -408,7 +389,7 @@ export function createAddressRouter(db: PgStore, chainId: ChainID): express.Rout
408389
const stxAddress = req.params['stx_address'];
409390
validatePrincipal(stxAddress);
410391
const untilBlock = parseUntilBlockQuery(req, res, next);
411-
const limit = parseAssetsQueryLimit(req.query.limit ?? 20);
392+
const limit = getPagingQueryLimit(ResourceType.Event, req.query.limit);
412393
const offset = parsePagingQueryInput(req.query.offset ?? 0);
413394

414395
const response = await db.sqlTransaction(async sql => {
@@ -461,7 +442,7 @@ export function createAddressRouter(db: PgStore, chainId: ChainID): express.Rout
461442
blockHeight = await getBlockHeight(untilBlock, req, res, next, db);
462443
}
463444

464-
const limit = parseStxInboundLimit(req.query.limit ?? 20);
445+
const limit = getPagingQueryLimit(ResourceType.Tx, req.query.limit);
465446
const offset = parsePagingQueryInput(req.query.offset ?? 0);
466447
const { results, total } = await db.getInboundTransfers({
467448
stxAddress,
@@ -507,7 +488,7 @@ export function createAddressRouter(db: PgStore, chainId: ChainID): express.Rout
507488
// get recent asset event associated with address
508489
const stxAddress = req.params['stx_address'];
509490
validatePrincipal(stxAddress);
510-
const limit = parseAssetsQueryLimit(req.query.limit ?? 20);
491+
const limit = getPagingQueryLimit(ResourceType.Event, req.query.limit);
511492
const offset = parsePagingQueryInput(req.query.offset ?? 0);
512493
const includeUnanchored = isUnanchoredRequest(req, res, next);
513494
const untilBlock = parseUntilBlockQuery(req, res, next);
@@ -557,7 +538,7 @@ export function createAddressRouter(db: PgStore, chainId: ChainID): express.Rout
557538
'/:address/mempool',
558539
mempoolCacheHandler,
559540
asyncHandler(async (req, res, next) => {
560-
const limit = parseTxQueryLimit(req.query.limit ?? MAX_TX_PER_REQUEST);
541+
const limit = getPagingQueryLimit(ResourceType.Tx, req.query.limit);
561542
const offset = parsePagingQueryInput(req.query.offset ?? 0);
562543

563544
const address = req.params['address'];

src/api/routes/block.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,22 @@
11
import * as express from 'express';
2-
import * as Bluebird from 'bluebird';
32
import { BlockListResponse } from '@stacks/stacks-blockchain-api-types';
4-
53
import { getBlockFromDataStore, getBlocksWithMetadata } from '../controllers/db-controller';
6-
import { timeout, waiter, has0xPrefix } from '../../helpers';
4+
import { has0xPrefix } from '../../helpers';
75
import { InvalidRequestError, InvalidRequestErrorType } from '../../errors';
8-
import { parseLimitQuery, parsePagingQueryInput } from '../pagination';
6+
import { getPagingQueryLimit, parsePagingQueryInput, ResourceType } from '../pagination';
97
import { getBlockHeightPathParam, validateRequestHexInput } from '../query-helpers';
108
import { getETagCacheHandler, setETagCacheHeaders } from '../controllers/cache-controller';
119
import { asyncHandler } from '../async-handler';
1210
import { PgStore } from '../../datastore/pg-store';
1311

14-
const MAX_BLOCKS_PER_REQUEST = 30;
15-
16-
const parseBlockQueryLimit = parseLimitQuery({
17-
maxItems: MAX_BLOCKS_PER_REQUEST,
18-
errorMsg: '`limit` must be equal to or less than ' + MAX_BLOCKS_PER_REQUEST,
19-
});
20-
2112
export function createBlockRouter(db: PgStore): express.Router {
2213
const router = express.Router();
2314
const cacheHandler = getETagCacheHandler(db);
2415
router.get(
2516
'/',
2617
cacheHandler,
2718
asyncHandler(async (req, res) => {
28-
const limit = parseBlockQueryLimit(req.query.limit ?? 20);
19+
const limit = getPagingQueryLimit(ResourceType.Block, req.query.limit);
2920
const offset = parsePagingQueryInput(req.query.offset ?? 0);
3021

3122
const { results, total } = await getBlocksWithMetadata({ offset, limit, db });

src/api/routes/burnchain.ts

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,16 @@ import {
1010

1111
import { isValidBitcoinAddress, tryConvertC32ToBtc } from '../../helpers';
1212
import { InvalidRequestError, InvalidRequestErrorType } from '../../errors';
13-
import { parseLimitQuery, parsePagingQueryInput } from '../pagination';
13+
import { getPagingQueryLimit, parsePagingQueryInput, ResourceType } from '../pagination';
1414
import { PgStore } from '../../datastore/pg-store';
1515

16-
const MAX_BLOCKS_PER_REQUEST = 250;
17-
18-
const parseQueryLimit = parseLimitQuery({
19-
maxItems: MAX_BLOCKS_PER_REQUEST,
20-
errorMsg: '`limit` must be equal to or less than ' + MAX_BLOCKS_PER_REQUEST,
21-
});
22-
2316
export function createBurnchainRouter(db: PgStore): express.Router {
2417
const router = express.Router();
2518

2619
router.get(
2720
'/reward_slot_holders',
2821
asyncHandler(async (req, res) => {
29-
const limit = parseQueryLimit(req.query.limit ?? 96);
22+
const limit = getPagingQueryLimit(ResourceType.Burnchain, req.query.limit);
3023
const offset = parsePagingQueryInput(req.query.offset ?? 0);
3124

3225
const queryResults = await db.getBurnchainRewardSlotHolders({ offset, limit });
@@ -53,7 +46,7 @@ export function createBurnchainRouter(db: PgStore): express.Router {
5346
router.get(
5447
'/reward_slot_holders/:address',
5548
asyncHandler(async (req, res) => {
56-
const limit = parseQueryLimit(req.query.limit ?? 96);
49+
const limit = getPagingQueryLimit(ResourceType.Burnchain, req.query.limit);
5750
const offset = parsePagingQueryInput(req.query.offset ?? 0);
5851
const { address } = req.params;
5952

@@ -102,7 +95,7 @@ export function createBurnchainRouter(db: PgStore): express.Router {
10295
router.get(
10396
'/rewards',
10497
asyncHandler(async (req, res) => {
105-
const limit = parseQueryLimit(req.query.limit ?? 96);
98+
const limit = getPagingQueryLimit(ResourceType.Burnchain, req.query.limit);
10699
const offset = parsePagingQueryInput(req.query.offset ?? 0);
107100

108101
const queryResults = await db.getBurnchainRewards({ offset, limit });
@@ -127,7 +120,7 @@ export function createBurnchainRouter(db: PgStore): express.Router {
127120
router.get(
128121
'/rewards/:address',
129122
asyncHandler(async (req, res) => {
130-
const limit = parseQueryLimit(req.query.limit ?? 96);
123+
const limit = getPagingQueryLimit(ResourceType.Burnchain, req.query.limit);
131124
const offset = parsePagingQueryInput(req.query.offset ?? 0);
132125
const { address } = req.params;
133126

src/api/routes/contract.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
11
import * as express from 'express';
22
import { asyncHandler } from '../async-handler';
3-
import { parseLimitQuery, parsePagingQueryInput } from '../pagination';
3+
import { getPagingQueryLimit, parsePagingQueryInput, ResourceType } from '../pagination';
44
import { parseDbEvent } from '../controllers/db-controller';
55
import { parseTraitAbi } from '../query-helpers';
66
import { PgStore } from '../../datastore/pg-store';
77

8-
const MAX_EVENTS_PER_REQUEST = 50;
9-
const parseContractEventsQueryLimit = parseLimitQuery({
10-
maxItems: MAX_EVENTS_PER_REQUEST,
11-
errorMsg: '`limit` must be equal to or less than ' + MAX_EVENTS_PER_REQUEST,
12-
});
13-
148
export function createContractRouter(db: PgStore): express.Router {
159
const router = express.Router();
1610

1711
router.get(
1812
'/by_trait',
1913
asyncHandler(async (req, res, next) => {
2014
const trait_abi = parseTraitAbi(req, res, next);
21-
const limit = parseContractEventsQueryLimit(req.query.limit ?? 20);
15+
const limit = getPagingQueryLimit(ResourceType.Contract, req.query.limit);
2216
const offset = parsePagingQueryInput(req.query.offset ?? 0);
2317
const smartContracts = await db.getSmartContractByTrait({
2418
trait: trait_abi,
@@ -58,7 +52,7 @@ export function createContractRouter(db: PgStore): express.Router {
5852
'/:contract_id/events',
5953
asyncHandler(async (req, res) => {
6054
const { contract_id } = req.params;
61-
const limit = parseContractEventsQueryLimit(req.query.limit ?? 20);
55+
const limit = getPagingQueryLimit(ResourceType.Contract, req.query.limit);
6256
const offset = parsePagingQueryInput(req.query.offset ?? 0);
6357
const eventsQuery = await db.getSmartContractEvents({
6458
contractId: contract_id,

src/api/routes/microblock.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,17 @@ import {
1111
getUnanchoredTxsFromDataStore,
1212
} from '../controllers/db-controller';
1313
import { has0xPrefix } from '../../helpers';
14-
import { parseLimitQuery, parsePagingQueryInput } from '../pagination';
14+
import { getPagingQueryLimit, parsePagingQueryInput, ResourceType } from '../pagination';
1515
import { validateRequestHexInput } from '../query-helpers';
1616
import { PgStore } from '../../datastore/pg-store';
1717

18-
const MAX_MICROBLOCKS_PER_REQUEST = 200;
19-
20-
const parseMicroblockQueryLimit = parseLimitQuery({
21-
maxItems: MAX_MICROBLOCKS_PER_REQUEST,
22-
errorMsg: '`limit` must be equal to or less than ' + MAX_MICROBLOCKS_PER_REQUEST,
23-
});
24-
2518
export function createMicroblockRouter(db: PgStore): express.Router {
2619
const router = express.Router();
2720

2821
router.get(
2922
'/',
3023
asyncHandler(async (req, res) => {
31-
const limit = parseMicroblockQueryLimit(req.query.limit ?? 20);
24+
const limit = getPagingQueryLimit(ResourceType.Microblock, req.query.limit);
3225
const offset = parsePagingQueryInput(req.query.offset ?? 0);
3326
const query = await getMicroblocksFromDataStore({ db, offset, limit });
3427
const response: MicroblockListResponse = {

0 commit comments

Comments
 (0)