From 89165b2becc48b9e83f92f54564434fde291a403 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Mon, 24 Aug 2020 12:20:19 -0600 Subject: [PATCH] feat: expose target block time #192 --- client/package.json | 2 +- client/src/generated/apis/NetworkInfoApi.ts | 100 ++++++++++++++++++ client/src/generated/apis/index.ts | 1 + .../models/NetworkBlockTimeResponse.ts | 57 ++++++++++ .../models/NetworkBlockTimesResponse.ts | 72 +++++++++++++ .../NetworkBlockTimesResponseMainnet.ts | 57 ++++++++++ client/src/generated/models/index.ts | 3 + ...network-block-time-by-network.example.json | 3 + ...-network-block-time-by-network.schema.json | 6 ++ .../info/get-network-block-times.example.json | 8 ++ .../info/get-network-block-times.schema.json | 16 +++ docs/entities/info/block-time.example.json | 3 + docs/entities/info/block-time.schema.json | 12 +++ docs/index.d.ts | 25 +++++ docs/openapi.yaml | 39 +++++++ src/api/init.ts | 2 + src/api/routes/info.ts | 60 +++++++++++ src/api/routes/tx.ts | 12 ++- src/helpers.ts | 5 + src/tests/api-tests.ts | 27 +++++ 20 files changed, 504 insertions(+), 6 deletions(-) create mode 100644 client/src/generated/apis/NetworkInfoApi.ts create mode 100644 client/src/generated/models/NetworkBlockTimeResponse.ts create mode 100644 client/src/generated/models/NetworkBlockTimesResponse.ts create mode 100644 client/src/generated/models/NetworkBlockTimesResponseMainnet.ts create mode 100644 docs/api/info/get-network-block-time-by-network.example.json create mode 100644 docs/api/info/get-network-block-time-by-network.schema.json create mode 100644 docs/api/info/get-network-block-times.example.json create mode 100644 docs/api/info/get-network-block-times.schema.json create mode 100644 docs/entities/info/block-time.example.json create mode 100644 docs/entities/info/block-time.schema.json create mode 100644 src/api/routes/info.ts diff --git a/client/package.json b/client/package.json index 6173eeca..52806a6c 100644 --- a/client/package.json +++ b/client/package.json @@ -25,7 +25,7 @@ "lint": "eslint . --ext .ts -f codeframe", "lint:prettier": "prettier --check ./src/**/*.{ts}", "open": "http-server -o 9222 -o index.html", - "prep-openapi": "rimraf ./.tmp && swagger-cli bundle --dereference -o ./.tmp/openapi.json ../docs/openapi.yaml && shx sed -i '^.*\\$schema.*$' '' ./.tmp/openapi.json", + "prep-openapi": "rimraf ./.tmp && rimraf ./src/generated && swagger-cli bundle --dereference -o ./.tmp/openapi.json ../docs/openapi.yaml && shx sed -i '^.*\\$schema.*$' '' ./.tmp/openapi.json", "generate-openapi": "npm run prep-openapi && openapi-generator generate --skip-validate-spec -g typescript-fetch --additional-properties=typescriptThreePlus=true,supportsES6=true,legacyDiscriminatorBehavior=false,enumPropertyNaming=original,modelPropertyNaming=original -i ./.tmp/openapi.json -o ./src/generated" }, "prettier": "@blockstack/prettier-config", diff --git a/client/src/generated/apis/NetworkInfoApi.ts b/client/src/generated/apis/NetworkInfoApi.ts new file mode 100644 index 00000000..143e1846 --- /dev/null +++ b/client/src/generated/apis/NetworkInfoApi.ts @@ -0,0 +1,100 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Stacks 2.0 Blockchain API + * This is the documentation for the Stacks 2.0 Blockchain API. It is comprised of two parts; the Stacks Blockchain API and the Stacks Core API. [![Run in Postman](https://run.pstmn.io/button.svg)](https://app.getpostman.com/run-collection/614feab5c108d292bffa#?env%5BStacks%20Blockchain%20API%5D=W3sia2V5Ijoic3R4X2FkZHJlc3MiLCJ2YWx1ZSI6IlNUMlRKUkhESE1ZQlE0MTdIRkIwQkRYNDMwVFFBNVBYUlg2NDk1RzFWIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJibG9ja19pZCIsInZhbHVlIjoiMHgiLCJlbmFibGVkIjp0cnVlfSx7ImtleSI6Im9mZnNldCIsInZhbHVlIjoiMCIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoibGltaXRfdHgiLCJ2YWx1ZSI6IjIwMCIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoibGltaXRfYmxvY2siLCJ2YWx1ZSI6IjMwIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJ0eF9pZCIsInZhbHVlIjoiMHg1NDA5MGMxNmE3MDJiNzUzYjQzMTE0ZTg4NGJjMTlhODBhNzk2MzhmZDQ0OWE0MGY4MDY4Y2RmMDAzY2RlNmUwIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJjb250cmFjdF9pZCIsInZhbHVlIjoiU1RKVFhFSlBKUFBWRE5BOUIwNTJOU1JSQkdRQ0ZOS1ZTMTc4VkdIMS5oZWxsb193b3JsZFxuIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJidGNfYWRkcmVzcyIsInZhbHVlIjoiYWJjIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJjb250cmFjdF9hZGRyZXNzIiwidmFsdWUiOiJTVEpUWEVKUEpQUFZETkE5QjA1Mk5TUlJCR1FDRk5LVlMxNzhWR0gxIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJjb250cmFjdF9uYW1lIiwidmFsdWUiOiJoZWxsb193b3JsZCIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoiY29udHJhY3RfbWFwIiwidmFsdWUiOiJzdG9yZSIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoiY29udHJhY3RfbWV0aG9kIiwidmFsdWUiOiJnZXQtdmFsdWUiLCJlbmFibGVkIjp0cnVlfV0=) ## Design ### Stacks Core API vs Stacks Blockchain API The blockchain\'s Rust implementation exposes a JSON RPC endpoint (\"Stacks Core API\"), which can be used to interface with the blockchain. It can be used directly. [See the documentation for the `stacks-blockchain` in its Github repository](https://github.com/blockstack/stacks-blockchain/) All `/v2/` routes a proxied to a Blockstack PBC-hosted Stacks Node. For a trustless architecture, you should make these requests to a self-hosted node. All `/extended/` routes are provided by the Stacks 2.0 Blockchain API directly. They extend the Stacks Core API capabilities to make it easier to integrate with. ### Pagination To make API responses more compact, lists returned by the API are paginated. For lists, the response body includes: - `limit`: the number of list items return per response - `offset`: the number of elements to skip (starting from `0`) - `total`: the number of all available list items - `results`: the array of list items (length of array equals the set limit) Using the `limit` and `offset` properties, you can paginate through the entire list by increasing the offset by the limit until you reach the total. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import { + NetworkBlockTimeResponse, + NetworkBlockTimeResponseFromJSON, + NetworkBlockTimeResponseToJSON, + NetworkBlockTimesResponse, + NetworkBlockTimesResponseFromJSON, + NetworkBlockTimesResponseToJSON, +} from '../models'; + +export interface GetNetworkBlockTimeByNetworkRequest { + network: GetNetworkBlockTimeByNetworkNetworkEnum; +} + +/** + * + */ +export class NetworkInfoApi extends runtime.BaseAPI { + + /** + * Get a given network\'s target block time + */ + async getNetworkBlockTimeByNetworkRaw(requestParameters: GetNetworkBlockTimeByNetworkRequest): Promise> { + if (requestParameters.network === null || requestParameters.network === undefined) { + throw new runtime.RequiredError('network','Required parameter requestParameters.network was null or undefined when calling getNetworkBlockTimeByNetwork.'); + } + + const queryParameters: runtime.HTTPQuery = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/extended/v1/info/network_block_time/{network}`.replace(`{${"network"}}`, encodeURIComponent(String(requestParameters.network))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }); + + return new runtime.JSONApiResponse(response, (jsonValue) => NetworkBlockTimeResponseFromJSON(jsonValue)); + } + + /** + * Get a given network\'s target block time + */ + async getNetworkBlockTimeByNetwork(requestParameters: GetNetworkBlockTimeByNetworkRequest): Promise { + const response = await this.getNetworkBlockTimeByNetworkRaw(requestParameters); + return await response.value(); + } + + /** + * Get the network target block time + */ + async getNetworkBlockTimesRaw(): Promise> { + const queryParameters: runtime.HTTPQuery = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/extended/v1/info/network_block_times`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }); + + return new runtime.JSONApiResponse(response, (jsonValue) => NetworkBlockTimesResponseFromJSON(jsonValue)); + } + + /** + * Get the network target block time + */ + async getNetworkBlockTimes(): Promise { + const response = await this.getNetworkBlockTimesRaw(); + return await response.value(); + } + +} + +/** + * @export + * @enum {string} + */ +export enum GetNetworkBlockTimeByNetworkNetworkEnum { + testnet = 'testnet', + mainnet = 'mainnet' +} diff --git a/client/src/generated/apis/index.ts b/client/src/generated/apis/index.ts index a1e42275..f7d6af3f 100644 --- a/client/src/generated/apis/index.ts +++ b/client/src/generated/apis/index.ts @@ -3,6 +3,7 @@ export * from './BlocksApi'; export * from './FaucetsApi'; export * from './FeesApi'; export * from './InfoApi'; +export * from './NetworkInfoApi'; export * from './SearchApi'; export * from './SmartContractsApi'; export * from './TransactionsApi'; diff --git a/client/src/generated/models/NetworkBlockTimeResponse.ts b/client/src/generated/models/NetworkBlockTimeResponse.ts new file mode 100644 index 00000000..13e51c92 --- /dev/null +++ b/client/src/generated/models/NetworkBlockTimeResponse.ts @@ -0,0 +1,57 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Stacks 2.0 Blockchain API + * This is the documentation for the Stacks 2.0 Blockchain API. It is comprised of two parts; the Stacks Blockchain API and the Stacks Core API. [![Run in Postman](https://run.pstmn.io/button.svg)](https://app.getpostman.com/run-collection/614feab5c108d292bffa#?env%5BStacks%20Blockchain%20API%5D=W3sia2V5Ijoic3R4X2FkZHJlc3MiLCJ2YWx1ZSI6IlNUMlRKUkhESE1ZQlE0MTdIRkIwQkRYNDMwVFFBNVBYUlg2NDk1RzFWIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJibG9ja19pZCIsInZhbHVlIjoiMHgiLCJlbmFibGVkIjp0cnVlfSx7ImtleSI6Im9mZnNldCIsInZhbHVlIjoiMCIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoibGltaXRfdHgiLCJ2YWx1ZSI6IjIwMCIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoibGltaXRfYmxvY2siLCJ2YWx1ZSI6IjMwIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJ0eF9pZCIsInZhbHVlIjoiMHg1NDA5MGMxNmE3MDJiNzUzYjQzMTE0ZTg4NGJjMTlhODBhNzk2MzhmZDQ0OWE0MGY4MDY4Y2RmMDAzY2RlNmUwIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJjb250cmFjdF9pZCIsInZhbHVlIjoiU1RKVFhFSlBKUFBWRE5BOUIwNTJOU1JSQkdRQ0ZOS1ZTMTc4VkdIMS5oZWxsb193b3JsZFxuIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJidGNfYWRkcmVzcyIsInZhbHVlIjoiYWJjIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJjb250cmFjdF9hZGRyZXNzIiwidmFsdWUiOiJTVEpUWEVKUEpQUFZETkE5QjA1Mk5TUlJCR1FDRk5LVlMxNzhWR0gxIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJjb250cmFjdF9uYW1lIiwidmFsdWUiOiJoZWxsb193b3JsZCIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoiY29udHJhY3RfbWFwIiwidmFsdWUiOiJzdG9yZSIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoiY29udHJhY3RfbWV0aG9kIiwidmFsdWUiOiJnZXQtdmFsdWUiLCJlbmFibGVkIjp0cnVlfV0=) ## Design ### Stacks Core API vs Stacks Blockchain API The blockchain\'s Rust implementation exposes a JSON RPC endpoint (\"Stacks Core API\"), which can be used to interface with the blockchain. It can be used directly. [See the documentation for the `stacks-blockchain` in its Github repository](https://github.com/blockstack/stacks-blockchain/) All `/v2/` routes a proxied to a Blockstack PBC-hosted Stacks Node. For a trustless architecture, you should make these requests to a self-hosted node. All `/extended/` routes are provided by the Stacks 2.0 Blockchain API directly. They extend the Stacks Core API capabilities to make it easier to integrate with. ### Pagination To make API responses more compact, lists returned by the API are paginated. For lists, the response body includes: - `limit`: the number of list items return per response - `offset`: the number of elements to skip (starting from `0`) - `total`: the number of all available list items - `results`: the array of list items (length of array equals the set limit) Using the `limit` and `offset` properties, you can paginate through the entire list by increasing the offset by the limit until you reach the total. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * GET request that target block time for a given network + * @export + * @interface NetworkBlockTimeResponse + */ +export interface NetworkBlockTimeResponse { + /** + * + * @type {number} + * @memberof NetworkBlockTimeResponse + */ + target_block_time: number; +} + +export function NetworkBlockTimeResponseFromJSON(json: any): NetworkBlockTimeResponse { + return NetworkBlockTimeResponseFromJSONTyped(json, false); +} + +export function NetworkBlockTimeResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): NetworkBlockTimeResponse { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'target_block_time': json['target_block_time'], + }; +} + +export function NetworkBlockTimeResponseToJSON(value?: NetworkBlockTimeResponse | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'target_block_time': value.target_block_time, + }; +} + + diff --git a/client/src/generated/models/NetworkBlockTimesResponse.ts b/client/src/generated/models/NetworkBlockTimesResponse.ts new file mode 100644 index 00000000..f9c25887 --- /dev/null +++ b/client/src/generated/models/NetworkBlockTimesResponse.ts @@ -0,0 +1,72 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Stacks 2.0 Blockchain API + * This is the documentation for the Stacks 2.0 Blockchain API. It is comprised of two parts; the Stacks Blockchain API and the Stacks Core API. [![Run in Postman](https://run.pstmn.io/button.svg)](https://app.getpostman.com/run-collection/614feab5c108d292bffa#?env%5BStacks%20Blockchain%20API%5D=W3sia2V5Ijoic3R4X2FkZHJlc3MiLCJ2YWx1ZSI6IlNUMlRKUkhESE1ZQlE0MTdIRkIwQkRYNDMwVFFBNVBYUlg2NDk1RzFWIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJibG9ja19pZCIsInZhbHVlIjoiMHgiLCJlbmFibGVkIjp0cnVlfSx7ImtleSI6Im9mZnNldCIsInZhbHVlIjoiMCIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoibGltaXRfdHgiLCJ2YWx1ZSI6IjIwMCIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoibGltaXRfYmxvY2siLCJ2YWx1ZSI6IjMwIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJ0eF9pZCIsInZhbHVlIjoiMHg1NDA5MGMxNmE3MDJiNzUzYjQzMTE0ZTg4NGJjMTlhODBhNzk2MzhmZDQ0OWE0MGY4MDY4Y2RmMDAzY2RlNmUwIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJjb250cmFjdF9pZCIsInZhbHVlIjoiU1RKVFhFSlBKUFBWRE5BOUIwNTJOU1JSQkdRQ0ZOS1ZTMTc4VkdIMS5oZWxsb193b3JsZFxuIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJidGNfYWRkcmVzcyIsInZhbHVlIjoiYWJjIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJjb250cmFjdF9hZGRyZXNzIiwidmFsdWUiOiJTVEpUWEVKUEpQUFZETkE5QjA1Mk5TUlJCR1FDRk5LVlMxNzhWR0gxIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJjb250cmFjdF9uYW1lIiwidmFsdWUiOiJoZWxsb193b3JsZCIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoiY29udHJhY3RfbWFwIiwidmFsdWUiOiJzdG9yZSIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoiY29udHJhY3RfbWV0aG9kIiwidmFsdWUiOiJnZXQtdmFsdWUiLCJlbmFibGVkIjp0cnVlfV0=) ## Design ### Stacks Core API vs Stacks Blockchain API The blockchain\'s Rust implementation exposes a JSON RPC endpoint (\"Stacks Core API\"), which can be used to interface with the blockchain. It can be used directly. [See the documentation for the `stacks-blockchain` in its Github repository](https://github.com/blockstack/stacks-blockchain/) All `/v2/` routes a proxied to a Blockstack PBC-hosted Stacks Node. For a trustless architecture, you should make these requests to a self-hosted node. All `/extended/` routes are provided by the Stacks 2.0 Blockchain API directly. They extend the Stacks Core API capabilities to make it easier to integrate with. ### Pagination To make API responses more compact, lists returned by the API are paginated. For lists, the response body includes: - `limit`: the number of list items return per response - `offset`: the number of elements to skip (starting from `0`) - `total`: the number of all available list items - `results`: the array of list items (length of array equals the set limit) Using the `limit` and `offset` properties, you can paginate through the entire list by increasing the offset by the limit until you reach the total. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import { + NetworkBlockTimesResponseMainnet, + NetworkBlockTimesResponseMainnetFromJSON, + NetworkBlockTimesResponseMainnetFromJSONTyped, + NetworkBlockTimesResponseMainnetToJSON, +} from './'; + +/** + * GET request that returns network target block times + * @export + * @interface NetworkBlockTimesResponse + */ +export interface NetworkBlockTimesResponse { + /** + * + * @type {NetworkBlockTimesResponseMainnet} + * @memberof NetworkBlockTimesResponse + */ + mainnet: NetworkBlockTimesResponseMainnet; + /** + * + * @type {NetworkBlockTimesResponseMainnet} + * @memberof NetworkBlockTimesResponse + */ + testnet: NetworkBlockTimesResponseMainnet; +} + +export function NetworkBlockTimesResponseFromJSON(json: any): NetworkBlockTimesResponse { + return NetworkBlockTimesResponseFromJSONTyped(json, false); +} + +export function NetworkBlockTimesResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): NetworkBlockTimesResponse { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'mainnet': NetworkBlockTimesResponseMainnetFromJSON(json['mainnet']), + 'testnet': NetworkBlockTimesResponseMainnetFromJSON(json['testnet']), + }; +} + +export function NetworkBlockTimesResponseToJSON(value?: NetworkBlockTimesResponse | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'mainnet': NetworkBlockTimesResponseMainnetToJSON(value.mainnet), + 'testnet': NetworkBlockTimesResponseMainnetToJSON(value.testnet), + }; +} + + diff --git a/client/src/generated/models/NetworkBlockTimesResponseMainnet.ts b/client/src/generated/models/NetworkBlockTimesResponseMainnet.ts new file mode 100644 index 00000000..e94a0f4d --- /dev/null +++ b/client/src/generated/models/NetworkBlockTimesResponseMainnet.ts @@ -0,0 +1,57 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Stacks 2.0 Blockchain API + * This is the documentation for the Stacks 2.0 Blockchain API. It is comprised of two parts; the Stacks Blockchain API and the Stacks Core API. [![Run in Postman](https://run.pstmn.io/button.svg)](https://app.getpostman.com/run-collection/614feab5c108d292bffa#?env%5BStacks%20Blockchain%20API%5D=W3sia2V5Ijoic3R4X2FkZHJlc3MiLCJ2YWx1ZSI6IlNUMlRKUkhESE1ZQlE0MTdIRkIwQkRYNDMwVFFBNVBYUlg2NDk1RzFWIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJibG9ja19pZCIsInZhbHVlIjoiMHgiLCJlbmFibGVkIjp0cnVlfSx7ImtleSI6Im9mZnNldCIsInZhbHVlIjoiMCIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoibGltaXRfdHgiLCJ2YWx1ZSI6IjIwMCIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoibGltaXRfYmxvY2siLCJ2YWx1ZSI6IjMwIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJ0eF9pZCIsInZhbHVlIjoiMHg1NDA5MGMxNmE3MDJiNzUzYjQzMTE0ZTg4NGJjMTlhODBhNzk2MzhmZDQ0OWE0MGY4MDY4Y2RmMDAzY2RlNmUwIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJjb250cmFjdF9pZCIsInZhbHVlIjoiU1RKVFhFSlBKUFBWRE5BOUIwNTJOU1JSQkdRQ0ZOS1ZTMTc4VkdIMS5oZWxsb193b3JsZFxuIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJidGNfYWRkcmVzcyIsInZhbHVlIjoiYWJjIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJjb250cmFjdF9hZGRyZXNzIiwidmFsdWUiOiJTVEpUWEVKUEpQUFZETkE5QjA1Mk5TUlJCR1FDRk5LVlMxNzhWR0gxIiwiZW5hYmxlZCI6dHJ1ZX0seyJrZXkiOiJjb250cmFjdF9uYW1lIiwidmFsdWUiOiJoZWxsb193b3JsZCIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoiY29udHJhY3RfbWFwIiwidmFsdWUiOiJzdG9yZSIsImVuYWJsZWQiOnRydWV9LHsia2V5IjoiY29udHJhY3RfbWV0aG9kIiwidmFsdWUiOiJnZXQtdmFsdWUiLCJlbmFibGVkIjp0cnVlfV0=) ## Design ### Stacks Core API vs Stacks Blockchain API The blockchain\'s Rust implementation exposes a JSON RPC endpoint (\"Stacks Core API\"), which can be used to interface with the blockchain. It can be used directly. [See the documentation for the `stacks-blockchain` in its Github repository](https://github.com/blockstack/stacks-blockchain/) All `/v2/` routes a proxied to a Blockstack PBC-hosted Stacks Node. For a trustless architecture, you should make these requests to a self-hosted node. All `/extended/` routes are provided by the Stacks 2.0 Blockchain API directly. They extend the Stacks Core API capabilities to make it easier to integrate with. ### Pagination To make API responses more compact, lists returned by the API are paginated. For lists, the response body includes: - `limit`: the number of list items return per response - `offset`: the number of elements to skip (starting from `0`) - `total`: the number of all available list items - `results`: the array of list items (length of array equals the set limit) Using the `limit` and `offset` properties, you can paginate through the entire list by increasing the offset by the limit until you reach the total. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * TargetBlockTime + * @export + * @interface NetworkBlockTimesResponseMainnet + */ +export interface NetworkBlockTimesResponseMainnet { + /** + * + * @type {number} + * @memberof NetworkBlockTimesResponseMainnet + */ + target_block_time: number; +} + +export function NetworkBlockTimesResponseMainnetFromJSON(json: any): NetworkBlockTimesResponseMainnet { + return NetworkBlockTimesResponseMainnetFromJSONTyped(json, false); +} + +export function NetworkBlockTimesResponseMainnetFromJSONTyped(json: any, ignoreDiscriminator: boolean): NetworkBlockTimesResponseMainnet { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'target_block_time': json['target_block_time'], + }; +} + +export function NetworkBlockTimesResponseMainnetToJSON(value?: NetworkBlockTimesResponseMainnet | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'target_block_time': value.target_block_time, + }; +} + + diff --git a/client/src/generated/models/index.ts b/client/src/generated/models/index.ts index 8b9ff39a..ca2d2618 100644 --- a/client/src/generated/models/index.ts +++ b/client/src/generated/models/index.ts @@ -9,5 +9,8 @@ export * from './BlockListResponse'; export * from './ContractInterfaceResponse'; export * from './ContractSourceResponse'; export * from './CoreNodeInfoResponse'; +export * from './NetworkBlockTimeResponse'; +export * from './NetworkBlockTimesResponse'; +export * from './NetworkBlockTimesResponseMainnet'; export * from './RunFaucetResponse'; export * from './TransactionResults'; diff --git a/docs/api/info/get-network-block-time-by-network.example.json b/docs/api/info/get-network-block-time-by-network.example.json new file mode 100644 index 00000000..d4a0b00c --- /dev/null +++ b/docs/api/info/get-network-block-time-by-network.example.json @@ -0,0 +1,3 @@ +{ + "target_block_time": 600 +} diff --git a/docs/api/info/get-network-block-time-by-network.schema.json b/docs/api/info/get-network-block-time-by-network.schema.json new file mode 100644 index 00000000..cb73fe07 --- /dev/null +++ b/docs/api/info/get-network-block-time-by-network.schema.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "GET request that target block time for a given network", + "title": "NetworkBlockTimeResponse", + "$ref": "../../entities/info/block-time.schema.json" +} diff --git a/docs/api/info/get-network-block-times.example.json b/docs/api/info/get-network-block-times.example.json new file mode 100644 index 00000000..924f1f8c --- /dev/null +++ b/docs/api/info/get-network-block-times.example.json @@ -0,0 +1,8 @@ +{ + "testnet": { + "target_block_time": 120 + }, + "mainnet": { + "target_block_time": 600 + } +} diff --git a/docs/api/info/get-network-block-times.schema.json b/docs/api/info/get-network-block-times.schema.json new file mode 100644 index 00000000..4534cfd1 --- /dev/null +++ b/docs/api/info/get-network-block-times.schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "GET request that returns network target block times", + "title": "NetworkBlockTimesResponse", + "type": "object", + "additionalProperties": false, + "required": ["mainnet", "testnet"], + "properties": { + "mainnet": { + "$ref": "../../entities/info/block-time.schema.json" + }, + "testnet": { + "$ref": "../../entities/info/block-time.schema.json" + } + } +} diff --git a/docs/entities/info/block-time.example.json b/docs/entities/info/block-time.example.json new file mode 100644 index 00000000..d4a0b00c --- /dev/null +++ b/docs/entities/info/block-time.example.json @@ -0,0 +1,3 @@ +{ + "target_block_time": 600 +} diff --git a/docs/entities/info/block-time.schema.json b/docs/entities/info/block-time.schema.json new file mode 100644 index 00000000..fdf6956e --- /dev/null +++ b/docs/entities/info/block-time.schema.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "TargetBlockTime", + "type": "object", + "additionalProperties": false, + "required": ["target_block_time", "total_sent", "total_received"], + "properties": { + "target_block_time": { + "type": "integer" + } + } +} diff --git a/docs/index.d.ts b/docs/index.d.ts index a4be8180..4521965d 100644 --- a/docs/index.d.ts +++ b/docs/index.d.ts @@ -184,6 +184,31 @@ export interface RunFaucetResponse { txRaw?: string; } +/** + * GET request that target block time for a given network + */ +export interface NetworkBlockTimeResponse { + target_block_time: number; +} + +/** + * GET request that returns network target block times + */ +export interface NetworkBlockTimesResponse { + /** + * TargetBlockTime + */ + mainnet: { + target_block_time: number; + }; + /** + * TargetBlockTime + */ + testnet: { + target_block_time: number; + }; +} + /** * GET request that returns transactions */ diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 07300592..30a48dbc 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -613,6 +613,45 @@ paths: success: value: { status: 'ready' } + /extended/v1/info/network_block_times: + get: + tags: + - NetworkInfo + operationId: get_network_block_times + summary: Get the network target block time + responses: + 200: + description: Success + content: + application/json: + example: + $ref: ./api/info/get-network-block-times.example.json + schema: + $ref: ./api/info/get-network-block-times.schema.json + /extended/v1/info/network_block_time/{network}: + get: + tags: + - NetworkInfo + operationId: get_network_block_time_by_network + summary: Get a given network's target block time + parameters: + - in: path + name: network + required: true + schema: + type: string + enum: [testnet, mainnet] + description: Which network to retrieve the target block time of + responses: + 200: + description: Success + content: + application/json: + example: + $ref: ./api/info/get-network-block-time-by-network.example.json + schema: + $ref: ./api/info/get-network-block-time-by-network.schema.json + /extended/v1/search/{id}: get: summary: Search diff --git a/src/api/init.ts b/src/api/init.ts index 19fb16d7..0e33ad79 100644 --- a/src/api/init.ts +++ b/src/api/init.ts @@ -10,6 +10,7 @@ import * as WebSocket from 'ws'; import { DataStore } from '../datastore/common'; import { createTxRouter } from './routes/tx'; import { createDebugRouter } from './routes/debug'; +import { createInfoRouter } from './routes/info'; import { createContractRouter } from './routes/contract'; import { createCoreNodeRpcProxyRouter } from './routes/core-node-rpc-proxy'; import { createBlockRouter } from './routes/block'; @@ -69,6 +70,7 @@ export async function startApiServer(datastore: DataStore): Promise { router.use('/contract', createContractRouter(datastore)); router.use('/address', createAddressRouter(datastore)); router.use('/search', createSearchRouter(datastore)); + router.use('/info', createInfoRouter(datastore)); router.use('/debug', createDebugRouter(datastore)); router.use('/status', (req, res) => res.status(200).json({ status: 'ready' })); router.use('/faucets', createFaucetRouter(datastore)); diff --git a/src/api/routes/info.ts b/src/api/routes/info.ts new file mode 100644 index 00000000..b904268f --- /dev/null +++ b/src/api/routes/info.ts @@ -0,0 +1,60 @@ +import * as express from 'express'; +import { addAsync, RouterWithAsync } from '@awaitjs/express'; +import { DataStore } from '../../datastore/common'; +import { validate } from '../validate'; +import { isProdEnv } from '../../helpers'; +import { + NetworkBlockTimesResponse, + NetworkBlockTimeResponse, +} from '@blockstack/stacks-blockchain-api-types'; + +const enum TargetBlockTime { + /** + * This is currently the Stacks 2.0 testnet, which uses a regtest bitcoin node with a + * controller service that controls the block mining. The configured time can be found at + * https://github.com/blockstackpbc/k8s/blob/5a3ae6abe74b736a0f21566a187838b00425e045/blockstack-core/v2/argon/bitcoin/staging/configmap.yaml#L7 + */ + Testnet = 2 * 60, // 2 minutes + /** + * Mainnet uses burnchain's block time (i.e. Bitcoin mainnet's 10 minute block time) + */ + Mainnet = 10 * 60, // 10 minutes +} + +export function createInfoRouter(db: DataStore): RouterWithAsync { + const router = addAsync(express.Router()); + + router.getAsync('/network_block_times', async (req, res) => { + const response: NetworkBlockTimesResponse = { + testnet: { target_block_time: TargetBlockTime.Testnet }, + mainnet: { target_block_time: TargetBlockTime.Mainnet }, + }; + if (!isProdEnv) { + const schemaPath = require.resolve( + '@blockstack/stacks-blockchain-api-types/api/info/get-network-block-times.schema.json' + ); + await validate(schemaPath, response); + } + res.json(response); + }); + + router.getAsync('/network_block_time/:network', async (req, res) => { + const { network } = req.params || req.query; + if (!network || !['testnet', 'mainnet'].includes(network)) { + res.status(400).json({ error: '`network` param must be `testnet` or `mainnet`' }).send(); + return; + } + const response: NetworkBlockTimeResponse = { + target_block_time: network === 'testnet' ? TargetBlockTime.Testnet : TargetBlockTime.Mainnet, + }; + if (!isProdEnv) { + const schemaPath = require.resolve( + '@blockstack/stacks-blockchain-api-types/api/info/get-network-block-time-by-network.schema.json' + ); + await validate(schemaPath, response); + } + res.json(response); + }); + + return router; +} diff --git a/src/api/routes/tx.ts b/src/api/routes/tx.ts index 42af9109..3cecde8d 100644 --- a/src/api/routes/tx.ts +++ b/src/api/routes/tx.ts @@ -7,7 +7,7 @@ import { parseTxTypeStrings, parseDbMempoolTx, } from '../controllers/db-controller'; -import { waiter, has0xPrefix, logError } from '../../helpers'; +import { waiter, has0xPrefix, logError, isProdEnv } from '../../helpers'; import { parseLimitQuery, parsePagingQueryInput } from '../pagination'; import { validate } from '../validate'; import { @@ -58,10 +58,12 @@ export function createTxRouter(db: DataStore): RouterWithAsync { return txQuery.result; }); const response: TransactionResults = { limit, offset, total, results }; - const schemaPath = require.resolve( - '@blockstack/stacks-blockchain-api-types/api/transaction/get-transactions.schema.json' - ); - await validate(schemaPath, response); + if (!isProdEnv) { + const schemaPath = require.resolve( + '@blockstack/stacks-blockchain-api-types/api/transaction/get-transactions.schema.json' + ); + await validate(schemaPath, response); + } res.json(response); }); diff --git a/src/helpers.ts b/src/helpers.ts index ffa86fc0..4d74836f 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -9,6 +9,11 @@ import { TransactionStatus } from '@blockstack/stacks-blockchain-api-types'; export const isDevEnv = process.env.NODE_ENV === 'development'; export const isTestEnv = process.env.NODE_ENV === 'test'; +export const isProdEnv = + process.env.NODE_ENV === 'production' || + process.env.NODE_ENV === 'prod' || + !process.env.NODE_ENV || + (!isTestEnv && !isDevEnv); export const APP_DIR = __dirname; export const REPO_DIR = path.dirname(__dirname); diff --git a/src/tests/api-tests.ts b/src/tests/api-tests.ts index 670eee05..f8c091bc 100644 --- a/src/tests/api-tests.ts +++ b/src/tests/api-tests.ts @@ -51,6 +51,33 @@ describe('api tests', () => { api = await startApiServer(db); }); + test('info block time', async () => { + const query1 = await supertest(api.server).get(`/extended/v1/info/network_block_times`); + expect(query1.status).toBe(200); + expect(query1.type).toBe('application/json'); + expect(JSON.parse(query1.text)).toEqual({ + testnet: { target_block_time: 120 }, + mainnet: { target_block_time: 600 }, + }); + + const query2 = await supertest(api.server).get(`/extended/v1/info/network_block_time/mainnet`); + expect(query2.status).toBe(200); + expect(query2.type).toBe('application/json'); + expect(JSON.parse(query2.text)).toEqual({ target_block_time: 600 }); + + const query3 = await supertest(api.server).get(`/extended/v1/info/network_block_time/testnet`); + expect(query3.status).toBe(200); + expect(query3.type).toBe('application/json'); + expect(JSON.parse(query3.text)).toEqual({ target_block_time: 120 }); + + const query4 = await supertest(api.server).get(`/extended/v1/info/network_block_time/badnet`); + expect(query4.status).toBe(400); + expect(query4.type).toBe('application/json'); + expect(JSON.parse(query4.text)).toEqual({ + error: '`network` param must be `testnet` or `mainnet`', + }); + }); + test('fetch mempool-tx', async () => { const mempoolTx: DbMempoolTx = { tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000',