feat: add /extended/v2/blocks/:height_or_hash (#1774)

* docs: openapi

* feat: get single block
This commit is contained in:
Rafael Cárdenas
2023-12-15 11:10:41 -06:00
committed by GitHub
parent 4c7333bc95
commit e532a5e173
4 changed files with 167 additions and 12 deletions

View File

@@ -720,6 +720,35 @@ paths:
$ref: ./api/blocks/get-nakamoto-blocks.schema.json
example:
$ref: ./api/blocks/get-nakamoto-blocks.example.json
/extended/v2/blocks/{height_or_hash}:
get:
summary: Get block
description: |
Retrieves a single block
tags:
- Blocks
operationId: get_block
parameters:
- name: height_or_hash
in: path
description: filter by block height, hash, index block hash or the constant `latest` to filter for the most recent block
required: true
schema:
oneOf:
- type: integer
example: 42000
- type: string
example: "0x4839a8b01cfb39ffcc0d07d3db31e848d5adf5279d529ed5062300b9f353ff79"
responses:
200:
description: Block
content:
application/json:
schema:
$ref: ./entities/blocks/nakamoto-block.schema.json
example:
$ref: ./entities/blocks/nakamoto-block.example.json
/extended/v1/block:
get:
@@ -769,8 +798,12 @@ paths:
type: string
example: "0x4839a8b01cfb39ffcc0d07d3db31e848d5adf5279d529ed5062300b9f353ff79"
get:
deprecated: true
summary: Get block by hash
description: Retrieves block details of a specific block for a given chain height. You can use the hash from your latest block ('get_block_list' API) to get your block details.
description: |
**NOTE:** This endpoint is deprecated in favor of [Get block](#operation/get_block).
Retrieves block details of a specific block for a given chain height. You can use the hash from your latest block ('get_block_list' API) to get your block details.
tags:
- Blocks
operationId: get_block_by_hash
@@ -799,8 +832,12 @@ paths:
type: number
example: 10000
get:
deprecated: true
summary: Get block by height
description: Retrieves block details of a specific block at a given block height
description: |
**NOTE:** This endpoint is deprecated in favor of [Get block](#operation/get_block).
Retrieves block details of a specific block at a given block height
tags:
- Blocks
operationId: get_block_by_height

View File

@@ -6,8 +6,13 @@ import {
} from '../../../api/controllers/cache-controller';
import { asyncHandler } from '../../async-handler';
import { NakamotoBlockListResponse } from 'docs/generated';
import { BlockLimitParamSchema, BlocksQueryParams, CompiledBlocksQueryParams } from './schemas';
import { parseDbNakamotoBlock, validRequestQuery } from './helpers';
import {
BlocksQueryParams,
BurnBlockParams,
CompiledBlocksQueryParams,
CompiledBurnBlockParams,
} from './schemas';
import { parseDbNakamotoBlock, validRequestParams, validRequestQuery } from './helpers';
export function createV2BlocksRouter(db: PgStore): express.Router {
const router = express.Router();
@@ -31,5 +36,23 @@ export function createV2BlocksRouter(db: PgStore): express.Router {
res.json(response);
})
);
router.get(
'/:height_or_hash',
cacheHandler,
asyncHandler(async (req, res) => {
if (!validRequestParams(req, res, CompiledBurnBlockParams)) return;
const params = req.params as BurnBlockParams;
const block = await db.getV2Block(params);
if (!block) {
res.status(404).json({ errors: 'Not found' });
return;
}
setETagCacheHeaders(res);
res.json(parseDbNakamotoBlock(block));
})
);
return router;
}

View File

@@ -465,6 +465,7 @@ export class PgStore extends BasePgStore {
/**
* Returns Block information with metadata, including accepted and streamed microblocks hash
* @returns `BlocksWithMetadata` object including list of Blocks with metadata and total count.
* @deprecated use `getV2Blocks`
*/
async getBlocksWithMetadata({
limit,
@@ -596,14 +597,7 @@ export class PgStore extends BasePgStore {
: undefined;
// Obtain blocks and transaction counts in the same query.
const blocksQuery = await sql<
(BlockQueryResult & {
tx_ids: string;
microblocks_accepted: string;
microblocks_streamed: string;
total: number;
})[]
>`
const blocksQuery = await sql<(BlockQueryResult & { tx_ids: string; total: number })[]>`
WITH block_count AS (
${
'burn_block_hash' in args
@@ -656,6 +650,39 @@ export class PgStore extends BasePgStore {
});
}
async getV2Block(args: BurnBlockParams): Promise<BlockWithTransactionIds | undefined> {
return await this.sqlTransaction(async sql => {
const filter =
args.height_or_hash === 'latest'
? sql`index_block_hash = (SELECT index_block_hash FROM blocks WHERE canonical = TRUE ORDER BY block_height DESC LIMIT 1)`
: CompiledBurnBlockHashParam.Check(args.height_or_hash)
? sql`(
block_hash = ${normalizeHashString(args.height_or_hash)}
OR index_block_hash = ${normalizeHashString(args.height_or_hash)}
)`
: sql`block_height = ${args.height_or_hash}`;
const blockQuery = await sql<(BlockQueryResult & { tx_ids: string })[]>`
SELECT
${sql(BLOCK_COLUMNS)},
(
SELECT STRING_AGG(tx_id,',')
FROM txs
WHERE index_block_hash = blocks.index_block_hash
AND canonical = true
AND microblock_canonical = true
) AS tx_ids
FROM blocks
WHERE canonical = true AND ${filter}
LIMIT 1
`;
if (blockQuery.count > 0)
return {
...parseBlockQueryResult(blockQuery[0]),
tx_ids: blockQuery[0].tx_ids ? blockQuery[0].tx_ids.split(',') : [],
};
});
}
async getBlockTxs(indexBlockHash: string) {
const result = await this.sql<{ tx_id: string; tx_index: number }[]>`
SELECT tx_id, tx_index

View File

@@ -731,4 +731,72 @@ describe('block tests', () => {
fetch = await supertest(api.server).get(`/extended/v2/blocks?burn_block_hash=testvalue`);
expect(fetch.status).toBe(400);
});
test('blocks v2 retrieved by hash or height', async () => {
for (let i = 1; i < 6; i++) {
const block = new TestBlockBuilder({
block_height: i,
block_hash: `0x000000000000000000000000000000000000000000000000000000000000000${i}`,
index_block_hash: `0x000000000000000000000000000000000000000000000000000000000000011${i}`,
parent_index_block_hash: `0x000000000000000000000000000000000000000000000000000000000000011${
i - 1
}`,
parent_block_hash: `0x000000000000000000000000000000000000000000000000000000000000000${
i - 1
}`,
burn_block_height: 700000,
burn_block_hash: '0x00000000000000000001e2ee7f0c6bd5361b5e7afd76156ca7d6f524ee5ca3d8',
})
.addTx({ tx_id: `0x000${i}` })
.build();
await db.update(block);
}
// Get latest
const block5 = {
burn_block_hash: '0x00000000000000000001e2ee7f0c6bd5361b5e7afd76156ca7d6f524ee5ca3d8',
burn_block_height: 700000,
burn_block_time: 94869286,
burn_block_time_iso: '1973-01-03T00:34:46.000Z',
canonical: true,
execution_cost_read_count: 0,
execution_cost_read_length: 0,
execution_cost_runtime: 0,
execution_cost_write_count: 0,
execution_cost_write_length: 0,
hash: '0x0000000000000000000000000000000000000000000000000000000000000005',
height: 5,
index_block_hash: '0x0000000000000000000000000000000000000000000000000000000000000115',
miner_txid: '0x4321',
parent_block_hash: '0x0000000000000000000000000000000000000000000000000000000000000004',
parent_index_block_hash: '0x0000000000000000000000000000000000000000000000000000000000000114',
txs: ['0x0005'],
};
let fetch = await supertest(api.server).get(`/extended/v2/blocks/latest`);
let json = JSON.parse(fetch.text);
expect(fetch.status).toBe(200);
expect(json).toStrictEqual(block5);
// Get by height
fetch = await supertest(api.server).get(`/extended/v2/blocks/5`);
json = JSON.parse(fetch.text);
expect(fetch.status).toBe(200);
expect(json).toStrictEqual(block5);
// Get by hash
fetch = await supertest(api.server).get(
`/extended/v2/blocks/0x0000000000000000000000000000000000000000000000000000000000000005`
);
json = JSON.parse(fetch.text);
expect(fetch.status).toBe(200);
expect(json).toStrictEqual(block5);
// Get by index block hash
fetch = await supertest(api.server).get(
`/extended/v2/blocks/0x0000000000000000000000000000000000000000000000000000000000000115`
);
json = JSON.parse(fetch.text);
expect(fetch.status).toBe(200);
expect(json).toStrictEqual(block5);
});
});