add OpenAPI docs for RPC endpoints, update /v2/pox return based on PR feedback

This commit is contained in:
Aaron Blankstein
2021-02-23 11:31:10 -06:00
parent 345bd0d49f
commit 8650dff243
26 changed files with 1102 additions and 117 deletions

View File

@@ -75,69 +75,7 @@ Reason types without additional information will not have a
### GET /v2/pox
Get current PoX-relevant information.
Returns JSON data in the form:
```
{
"contract_id": "SP000000000000000000002Q6VF78.pox",
"first_burnchain_block_height": 666050,
"next_cycle_cur_threshold": 70000000000,
"cur_cycle_threshold": 70000000000,
"cur_cycle_stacked_ustx": 202157971547640,
"next_cycle_stacked_ustx": 151807880982060,
"reward_slots": 4000,
"next_rewards_start": 672350,
"next_prepare_phase_start": 672250,
"min_stacking_increment_ustx": 52251700044,
"pox_activation_threshold": 52251700044388,
"prepare_cycle_length": 100,
"rejection_fraction": 25,
"reward_cycle_id": 2,
"reward_cycle_length": 2100,
"rejection_votes_left_required": 261258500221925,
"total_liquid_supply_ustx": 1045034000887772,
"next_reward_cycle_in": 1318,
"next_prepare_phase_in": 1218
}
```
* `contract_id` is the contract identifier for the PoX contract.
* `cur_cycle_threshold` is the threshold amount for obtaining a slot in the
active reward cycle.
* `next_cycle_cur_threshold` is the _current_ microstacks threshold for the
_next_ reward cycle. If more STX is locked for participation in that cycle,
the threshold may increase.
* `first_burnchain_block_height` is the first burn block evaluated in this Stacks
chain.
* `cur_cycle_stacked_ustx` is the total amount of stacked microstacks in the
active reward cycle.
* `next_cycle_stacked_ustx` is the total amount of stacked microstacks in the
next reward cycle.
* `reward_slots` is the number of reward slots in a reward cycle
* `next_rewards_start` is the burn block height when the next reward
cycle begins
* `next_prepare_phase_start` is the burn block height when the next prepare
phase begins. Any eligible stacks _must_ be stacked before this block.
* `min_stacking_increment_ustx` is the minimum amount that can be used to
submit a `stack-stx` call.
* `pox_activation_threshold` is the threshold of stacking participation that
must be reached for PoX to activate in any cycle.
* `prepare_cycle_length` is the length in burn blocks of the prepare phase
* `rejection_fraction` is the fraction of liquid STX that must vote to reject
PoX in order to prevent the next reward cycle from activating.
* `rejection_votes_left_required` is the remaining amount of liquid
STX that must vote to reject the next reward cycle to prevent the next
reward cycle from activating.
* `reward_cycle_id` is the active reward cycle number
* `reward_cycle_length` is the length in burn blocks of a whole PoX
cycle (reward phase and prepare phase)
* `total_liquid_supply_ustx` is the current total amount of liquid microstacks.
* `next_reward_cycle_in` is the number of burn blocks until the next reward
cycle begins.
* `next_prepare_phase_in` is the number of burn blocks until the next prepare
phase starts.
Get current PoX-relevant information. See OpenAPI [spec](./rpc/openapi.yaml) for details.
### GET /v2/accounts/[Principal]

View File

@@ -0,0 +1,4 @@
{
"okay": false,
"cause": "Unchecked(PublicFunctionNotReadOnly(..."
}

View File

@@ -0,0 +1,4 @@
{
"okay": true,
"result": "0x111..."
}

View File

@@ -0,0 +1,19 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "GET request to get contract source",
"title": "ReadOnlyFunctionSuccessResponse",
"type": "object",
"additionalProperties": false,
"required": ["okay"],
"properties": {
"okay": {
"type": "boolean"
},
"result": {
"type": "string"
},
"cause": {
"type": "string"
}
}
}

View File

@@ -0,0 +1,8 @@
{
"balance": "0x0000000000000000000000000007a120",
"locked": "0x0000000000000000000000000007a120",
"unlock_height": 126,
"nonce": 2867,
"balance_proof": "0xabce",
"nonce_proof": "0xabcd"
}

View File

@@ -0,0 +1,28 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "GET request for account data",
"title": "AccountDataResponse",
"type": "object",
"additionalProperties": false,
"required": ["balance", "locked", "unlock_height", "nonce", "balance_proof", "nonce_proof"],
"properties": {
"balance": {
"type": "string"
},
"locked": {
"type": "string"
},
"unlock_height": {
"type": "integer"
},
"nonce": {
"type": "integer"
},
"balance_proof": {
"type": "string"
},
"nonce_proof": {
"type": "string"
}
}
}

View File

@@ -0,0 +1,4 @@
{
"data": "0x0a0c000000010a6d6f6e737465722d69640100000000000000000000000000000001",
"proof": "0x123..."
}

View File

@@ -0,0 +1,17 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Response of get data map entry request",
"title": "MapEntryResponse",
"type": "object",
"required": ["data"],
"properties": {
"data": {
"type": "string",
"description": "Hex-encoded string of clarity value. It is always an optional tuple."
},
"proof": {
"type": "string",
"description": "Hex-encoded string of the MARF proof for the data"
}
}
}

View File

@@ -0,0 +1,134 @@
{
"functions": [
{
"name": "get-value",
"access": "public",
"args": [
{
"name": "key",
"type": {
"buffer": {
"length": 32
}
}
}
],
"outputs": {
"type": {
"response": {
"ok": {
"buffer": {
"length": 32
}
},
"error": "int128"
}
}
}
},
{
"name": "set-value",
"access": "public",
"args": [
{
"name": "key",
"type": {
"buffer": {
"length": 32
}
}
},
{
"name": "value",
"type": {
"buffer": {
"length": 32
}
}
}
],
"outputs": {
"type": {
"response": {
"ok": "uint128",
"error": "none"
}
}
}
},
{
"name": "test-emit-event",
"access": "public",
"args": [],
"outputs": {
"type": {
"response": {
"ok": "uint128",
"error": "none"
}
}
}
},
{
"name": "test-event-types",
"access": "public",
"args": [],
"outputs": {
"type": {
"response": {
"ok": "uint128",
"error": "none"
}
}
}
}
],
"variables": [
{
"name": "recipient",
"type": "principal",
"access": "constant"
},
{
"name": "sender",
"type": "principal",
"access": "constant"
}
],
"maps": [
{
"name": "store",
"key": [
{
"name": "key",
"type": {
"buffer": {
"length": 32
}
}
}
],
"value": [
{
"name": "value",
"type": {
"buffer": {
"length": 32
}
}
}
]
}
],
"fungible_tokens": [
{
"name": "novel-token-19"
}
],
"non_fungible_tokens": [
{
"name": "hello-nft",
"type": "uint128"
}
]
}

View File

@@ -0,0 +1,44 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "GET request to get contract interface",
"title": "ContractInterfaceResponse",
"type": "object",
"required": ["functions", "variables", "maps", "fungible_tokens", "non_fungible_tokens"],
"properties": {
"functions": {
"type": "array",
"items": {
"type": "object"
},
"description": "List of defined methods"
},
"variables": {
"type": "array",
"items": {
"type": "object"
},
"description": "List of defined variables"
},
"maps": {
"type": "array",
"items": {
"type": "object"
},
"description": "List of defined data-maps"
},
"fungible_tokens": {
"type": "array",
"items": {
"type": "object"
},
"description": "List of fungible tokens in the contract"
},
"non_fungible_tokens": {
"type": "array",
"items": {
"type": "object"
},
"description": "List of non-fungible tokens in the contract"
}
}
}

View File

@@ -0,0 +1,5 @@
{
"source": "(define-constant sender 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)\n(define-constant recipient 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G)\n\n(define-fungible-token novel-token-19)\n(begin (ft-mint? novel-token-19 u12 sender))\n(begin (ft-transfer? novel-token-19 u2 sender recipient))\n\n(define-non-fungible-token hello-nft uint)\n(begin (nft-mint? hello-nft u1 sender))\n(begin (nft-mint? hello-nft u2 sender))\n(begin (nft-transfer? hello-nft u1 sender recipient))\n\n(define-public (test-emit-event)\n (begin\n (print \"Event! Hello world\")\n (ok u1)))\n(begin (test-emit-event))\n\n(define-public (test-event-types)\n (begin\n (unwrap-panic (ft-mint? novel-token-19 u3 recipient))\n (unwrap-panic (nft-mint? hello-nft u2 recipient))\n (unwrap-panic (stx-transfer? u60 tx-sender 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR))\n (unwrap-panic (stx-burn? u20 tx-sender))\n (ok u1)))\n\n(define-map store ((key (buff 32))) ((value (buff 32))))\n(define-public (get-value (key (buff 32)))\n (begin\n (match (map-get? store ((key key)))\n entry (ok (get value entry))\n (err 0))))\n(define-public (set-value (key (buff 32)) (value (buff 32)))\n (begin\n (map-set store ((key key)) ((value value)))\n (ok u1)))",
"publish_height": 3196,
"proof": "0000001104060000001ec4e..."
}

View File

@@ -0,0 +1,19 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "GET request to get contract source",
"title": "ContractSourceResponse",
"type": "object",
"additionalProperties": false,
"required": ["source", "publish_height", "proof"],
"properties": {
"source": {
"type": "string"
},
"publish_height": {
"type": "integer"
},
"proof": {
"type": "string"
}
}
}

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "GET fee estimates",
"title": "CoreNodeFeeResponse",
"type": "string",
"additionalProperties": false
}

View File

@@ -0,0 +1,15 @@
{
"peer_version": 385875968,
"pox_consensus": "17f76e597bab45646956f38dd39573085d72cbc0",
"burn_block_height": 16,
"stable_pox_consensus": "8e0561978fc5506b68a589c402dad97e862edb59",
"stable_burn_block_height": 15,
"server_version": "blockstack-core 0.0.1 => 23.0.0.0 (, release build, linux [x86_64])",
"network_id": 2147483648,
"parent_network_id": 3669344250,
"stacks_tip_height": 15,
"stacks_tip": "b1807a2d3f7f8c7922f7c1d60d7c34145ade05d789640dc7dc9ec1021e07bb54",
"stacks_tip_consensus_hash": "17f76e597bab45646956f38dd39573085d72cbc0",
"unanchored_tip": "0000000000000000000000000000000000000000000000000000000000000000",
"exit_at_block_height": null
}

View File

@@ -0,0 +1,76 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "GET request that core node information",
"title": "CoreNodeInfoResponse",
"type": "object",
"additionalProperties": false,
"required": [
"peer_version",
"pox_consensus",
"burn_block_height",
"stable_pox_consensus",
"stable_burn_block_height",
"server_version",
"network_id",
"parent_network_id",
"stacks_tip_height",
"stacks_tip",
"stacks_tip_consensus_hash",
"unanchored_tip",
"exit_at_block_height"
],
"properties": {
"peer_version": {
"type": "integer",
"description": "identifies the version number for the networking communication, this should not change while a node is running, and will only change if there's an upgrade"
},
"pox_consensus": {
"type": "string",
"description": "is a hash used to identify the burnchain view for a node. it incorporates bitcoin chain information and PoX information. nodes that disagree on this value will appear to each other as forks. this value will change after every block"
},
"burn_block_height": {
"type": "integer",
"description": "latest bitcoin chain height"
},
"stable_pox_consensus": {
"type": "string",
"description": "same as burn_consensus, but evaluated at stable_burn_block_height"
},
"stable_burn_block_height": {
"type": "integer",
"description": "leftover from stacks 1.0, basically always burn_block_height - 1"
},
"server_version": {
"type": "string",
"description": "is a version descriptor"
},
"network_id": {
"type": "integer",
"description": "is similar to peer_version and will be used to differentiate between different testnets. this value will be different between mainnet and testnet. once launched, this value will not change"
},
"parent_network_id": {
"type": "integer",
"description": "same as network_id, but for bitcoin"
},
"stacks_tip_height": {
"type": "integer",
"description": "the latest Stacks chain height. Stacks forks can occur independent of the Bitcoin chain, that height doesn't increase 1-to-1 with the Bitcoin height"
},
"stacks_tip": {
"type": "string",
"description": "the best known block hash for the Stack chain (not including any pending microblocks)"
},
"stacks_tip_consensus_hash": {
"type": "string",
"description": "the burn chain (i.e., bitcoin) consensus hash at the time that stacks_tip was mined"
},
"unanchored_tip": {
"type": "string",
"description": "the latest microblock hash if any microblocks were processed. if no microblock has been processed for the current block, a 000.., hex array is returned"
},
"exit_at_block_height": {
"type": "integer",
"description": "the block height at which the testnet network will be reset. not applicable for mainnet"
}
}
}

View File

@@ -0,0 +1,33 @@
{
"contract_id": "SP000000000000000000002Q6VF78.pox",
"pox_activation_threshold_ustx": 52329761604388,
"first_burnchain_block_height": 666050,
"prepare_phase_block_length": 100,
"reward_phase_block_length": 2000,
"reward_slots": 4000,
"rejection_fraction": 25,
"total_liquid_supply_ustx": 1046595232087772,
"current_cycle": {
"id": 2,
"min_threshold_ustx": 70000000000,
"stacked_ustx": 202157971547640,
"is_pox_active": true
},
"next_cycle": {
"id": 3,
"min_threshold_ustx": 70000000000,
"min_increment_ustx": 52329761604,
"stacked_ustx": 162057034977640,
"prepare_phase_start_block_height": 672250,
"blocks_until_prepare_phase": 407,
"reward_phase_start_block_height": 672350,
"blocks_until_reward_phase": 507,
"ustx_until_pox_rejection": 261648808021925
},
"min_amount_ustx": 70000000000,
"prepare_cycle_length": 100,
"reward_cycle_id": 2,
"reward_cycle_length": 2100,
"rejection_votes_left_required": 261648808021925,
"next_reward_cycle_in": 507
}

View File

@@ -0,0 +1,160 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Get Proof of Transfer (PoX) information",
"title": "CoreNodePoxResponse",
"type": "object",
"additionalProperties": false,
"required": [
"contract_id",
"first_burnchain_block_height",
"pox_activation_threshold_ustx",
"prepare_phase_block_length",
"reward_phase_block_length",
"reward_slots",
"rejection_fraction",
"total_liquid_supply_ustx",
"current_cycle",
"next_cycle",
"reward_cycle_length",
"min_amount_ustx",
"reward_cycle_id",
"prepare_cycle_length",
"rejection_votes_left_required"
],
"properties": {
"contract_id": {
"type": "string",
"description": "The contract identifier for the PoX contract"
},
"first_burnchain_block_height": {
"type": "integer",
"description": "The first burn block evaluated in this Stacks chain"
},
"pox_activation_threshold_ustx": {
"type": "integer",
"description": "The threshold of stacking participation that must be reached for PoX to activate in any cycle"
},
"rejection_fraction": {
"type": "integer",
"description": "The fraction of liquid STX that must vote to reject PoX in order to prevent the next reward cycle from activating."
},
"reward_phase_block_length": {
"type": "integer",
"description": "The length in burn blocks of the reward phase"
},
"prepare_phase_block_length": {
"type": "integer",
"description": "The length in burn blocks of the prepare phase"
},
"reward_slots": {
"type": "integer",
"description": "The number of reward slots in a reward cycle"
},
"total_liquid_supply_ustx": {
"type": "integer",
"description": "The current total amount of liquid microstacks."
},
"reward_cycle_length": {
"type": "integer",
"description": "The length in burn blocks of a whole PoX cycle (reward phase and prepare phase)"
},
"current_cycle": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"min_threshold_ustx",
"stacked_ustx",
"is_pox_active"
],
"properties": {
"id": {
"type": "integer",
"description": "The reward cycle number"
},
"min_threshold_ustx": {
"type": "integer",
"description": "The threshold amount for obtaining a slot in this reward cycle."
},
"stacked_ustx": {
"type": "integer",
"description": "The total amount of stacked microstacks in this reward cycle."
},
"is_pox_active": {
"type": "boolean",
"description": "Whether or not PoX is active during this reward cycle."
},
}
},
"next_cycle": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"min_threshold_ustx",
"stacked_ustx",
"min_increment_ustx",
"prepare_phase_start_block_height",
"blocks_until_prepare_phase",
"reward_phase_start_block_height",
"blocks_until_reward_phase",
"ustx_until_pox_rejection"
],
"properties": {
"id": {
"type": "integer",
"description": "The reward cycle number"
},
"min_threshold_ustx": {
"type": "integer",
"description": "The threshold amount for obtaining a slot in this reward cycle."
},
"stacked_ustx": {
"type": "integer",
"description": "The total amount of stacked microstacks in this reward cycle."
},
"min_increment_ustx": {
"type": "integer",
"description": "The minimum amount that can be used to submit a `stack-stx` call."
},
"prepare_phase_start_block_height": {
"type": "integer",
"description": "The burn block height when the prepare phase for this cycle begins. Any eligible stacks must be stacked before this block."
},
"blocks_until_prepare_phase": {
"type": "integer",
"description": "The number of burn blocks until the prepare phase for this cycle starts. If the prepare phase for this cycle already started, this value will be negative."
},
"reward_phase_start_block_height": {
"type": "integer",
"description": "The burn block height when the reward phase for this cycle begins. Any eligible stacks must be stacked before this block."
},
"blocks_until_reward_phase": {
"type": "integer",
"description": "The number of burn blocks until this reward phase starts."
},
"ustx_until_pox_rejection": {
"type": "integer",
"description": "The remaining amount of liquid STX that must vote to reject the next reward cycle to prevent the next reward cycle from activating."
}
}
},
"reward_cycle_id": {
"type": "integer",
"deprecated": true,
"description": "The active reward cycle number"
},
"min_amount_ustx": {
"type": "integer",
"deprecated": true,
},
"prepare_cycle_length": {
"type": "integer",
"deprecated": true,
},
"rejection_votes_left_required": {
"type": "integer",
"deprecated": true,
}
}
}

View File

@@ -0,0 +1,11 @@
{
"error": "transaction rejected",
"reason": "BadNonce",
"reason_data": {
"actual": 4,
"expected": 0,
"is_origin": true,
"principal": "ST2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7G9Y0X1MH"
},
"txid": "caf6fd60ae05b0c2d19ef14ab6a7670b1095d117fa7c80224c74e76214d0a791"
}

View File

@@ -0,0 +1,25 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "GET request that returns transactions",
"title": "PostCoreNodeTransactionsError",
"type": "object",
"required": ["error", "reason", "reason_data", "txid"],
"properties": {
"error": {
"type": "string",
"description": "The error"
},
"reason": {
"type": "string",
"description": "The reason for the error"
},
"reason_data": {
"type": "object",
"description": "More details about the reason"
},
"txid": {
"type": "string",
"description": "The relevant transaction id"
}
}
}

View File

@@ -0,0 +1,20 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ReadOnlyFunctionArgs",
"description": "Describes representation of a Type-0 Stacks 2.0 transaction. https://github.com/blockstack/stacks-blockchain/blob/master/sip/sip-005-blocks-and-transactions.md#type-0-transferring-an-asset",
"type": "object",
"required": ["sender", "arguments"],
"properties": {
"sender": {
"type": "string",
"description": "The simulated tx-sender"
},
"arguments": {
"type": "array",
"description": "An array of hex serialized Clarity values",
"items": {
"type": "string"
}
}
}
}

328
docs/rpc/openapi.yaml Normal file
View File

@@ -0,0 +1,328 @@
openapi: 3.0.2
servers:
- url: http://localhost:20443/
description: Local
info:
title: Stacks 2.0 RPC API
version: '1.0.0'
description: |
This is the documentation for the `stacks-node` RPC interface.
paths:
/v2/transactions:
post:
summary: Broadcast raw transaction
tags:
- Transactions
description: Broadcast raw transactions on the network. You can use the [@stacks/transactions](https://github.com/blockstack/stacks.js) project to generate a raw transaction payload.
operationId: post_core_node_transactions
requestBody:
content:
application/octet-stream:
schema:
type: string
format: binary
example: binary format of 00000000010400bed38c2aadffa348931bcb542880ff79d607afec000000000000000000000000000000c800012b0b1fff6cccd0974966dcd665835838f0985be508e1322e09fb3d751eca132c492bda720f9ef1768d14fdabed6127560ba52d5e3ac470dcb60b784e97dc88c9030200000000000516df0ba3e79792be7be5e50a370289accfc8c9e032000000000000303974657374206d656d6f00000000000000000000000000000000000000000000000000
responses:
200:
description: Transaction id of successful post of a raw tx to the node's mempool
content:
text/plain:
schema:
type: string
example: '"e161978626f216b2141b156ade10501207ae535fa365a13ef5d7a7c9310a09f2"'
400:
description: Rejections result in a 400 error
content:
application/json:
schema:
$ref: ./api/transaction/post-core-node-transactions-error.schema.json
example:
$ref: ./api/transaction/post-core-node-transactions-error.example.json
/v2/contracts/interface/{contract_address}/{contract_name}:
get:
summary: Get contract interface
description: Get contract interface using a `contract_address` and `contract name`
tags:
- Smart Contracts
operationId: get_contract_interface
responses:
200:
description: Contract interface
content:
application/json:
schema:
$ref: ./api/core-node/get-contract-interface.schema.json
example:
$ref: ./api/core-node/get-contract-interface.example.json
parameters:
- name: contract_address
in: path
required: true
description: Stacks address
schema:
type: string
- name: contract_name
in: path
required: true
description: Contract name
schema:
type: string
- name: tip
in: query
schema:
type: string
description: The Stacks chain tip to query from
/v2/map_entry/{contract_address}/{contract_name}/{map_name}:
post:
summary: Get specific data-map inside a contract
tags:
- Smart Contracts
operationId: get_contract_data_map_entry
description: |
Attempt to fetch data from a contract data map. The contract is identified with [Stacks Address] and [Contract Name] in the URL path. The map is identified with [Map Name].
The key to lookup in the map is supplied via the POST body. This should be supplied as the hex string serialization of the key (which should be a Clarity value). Note, this is a JSON string atom.
In the response, `data` is the hex serialization of the map response. Note that map responses are Clarity option types, for non-existent values, this is a serialized none, and for all other responses, it is a serialized (some ...) object.
responses:
200:
description: Success
content:
application/json:
schema:
$ref: ./api/core-node/get-contract-data-map-entry.schema.json
example:
$ref: ./api/core-node/get-contract-data-map-entry.example.json
400:
description: Failed loading data map
parameters:
- name: contract_address
in: path
required: true
description: Stacks address
schema:
type: string
- name: contract_name
in: path
required: true
description: Contract name
schema:
type: string
- name: map_name
in: path
required: true
description: Map name
schema:
type: string
- name: proof
in: query
description: Returns object without the proof field when set to 0
schema:
type: integer
- name: tip
in: query
schema:
type: string
description: The Stacks chain tip to query from
x-codegen-request-body-name: key
requestBody:
description: Hex string serialization of the lookup key (which should be a Clarity value)
required: true
content:
application/json:
schema:
type: string
/v2/contracts/source/{contract_address}/{contract_name}:
get:
summary: Get contract source
tags:
- Smart Contracts
operationId: get_contract_source
description: Returns the Clarity source code of a given contract, along with the block height it was published in, and the MARF proof for the data
responses:
200:
description: Success
content:
application/json:
schema:
$ref: ./api/core-node/get-contract-source.schema.json
example:
$ref: ./api/core-node/get-contract-source.example.json
parameters:
- name: contract_address
in: path
required: true
description: Stacks address
schema:
type: string
- name: contract_name
in: path
required: true
description: Contract name
schema:
type: string
- name: proof
in: query
description: Returns object without the proof field if set to 0
schema:
type: integer
- name: tip
in: query
schema:
type: string
description: The Stacks chain tip to query from
required: false
/v2/contracts/call-read/{contract_address}/{contract_name}/{function_name}:
post:
summary: Call read-only function
tags:
- Smart Contracts
operationId: call_read_only_function
description: |
Call a read-only public function on a given smart contract.
The smart contract and function are specified using the URL path. The arguments and the simulated tx-sender are supplied via the POST body in the following JSON format:
responses:
200:
description: Success
content:
application/json:
schema:
$ref: ./api/contract/post-call-read-only-fn.schema.json
examples:
success:
$ref: ./api/contract/post-call-read-only-fn-success.example.json
fail:
$ref: ./api/contract/post-call-read-only-fn-fail.example.json
parameters:
- name: contract_address
in: path
required: true
description: Stacks address
schema:
type: string
- name: contract_name
in: path
required: true
description: Contract name
schema:
type: string
- name: function_name
in: path
required: true
description: Function name
schema:
type: string
- name: tip
in: query
schema:
type: string
description: The Stacks chain tip to query from
required: false
requestBody:
description: map of arguments and the simulated tx-sender where sender is either a Contract identifier or a normal Stacks address, and arguments is an array of hex serialized Clarity values.
required: true
content:
application/json:
schema:
$ref: './entities/contracts/read-only-function-args.schema.json'
example:
sender: 'SP31DA6FTSJX2WGTZ69SFY11BH51NZMB0ZW97B5P0.get-info'
arguments:
- '0x0011...'
- '0x00231...'
/v2/accounts/{principal}:
get:
summary: Get account info
tags:
- Accounts
operationId: get_account_info
description: |
Get the account data for the provided principal
Where balance is the hex encoding of a unsigned 128-bit integer (big-endian), nonce is a unsigned 64-bit integer, and the proofs are provided as hex strings.
For non-existent accounts, this does not 404, rather it returns an object with balance and nonce of 0.
parameters:
- name: principal
in: path
description: Stacks address or a Contract identifier (e.g. `SP31DA6FTSJX2WGTZ69SFY11BH51NZMB0ZW97B5P0.get-info`)
required: true
schema:
type: string
- name: proof
in: query
description: Returns object without the proof field if set to 0
schema:
type: integer
- name: tip
in: query
schema:
type: string
description: The Stacks chain tip to query from
responses:
200:
description: Success
content:
application/json:
schema:
$ref: ./api/core-node/get-account-data.schema.json
example:
$ref: ./api/core-node/get-account-data.example.json
/v2/fees/transfer:
get:
summary: Get estimated fee
tags:
- Fees
operationId: get_fee_transfer
description: Get an estimated fee rate for STX transfer transactions. This a a fee rate / byte, and is returned as a JSON integer
responses:
200:
description: Success
content:
application/json:
schema:
$ref: ./api/core-node/get-fee-transfer.schema.json
example:
$ref: ./api/core-node/get-fee-transfer.example.json
/v2/info:
get:
summary: Get Core API info
description: Get Core API information
tags:
- Info
operationId: get_core_api_info
responses:
200:
description: Success
content:
application/json:
schema:
$ref: ./api/core-node/get-info.schema.json
example:
$ref: ./api/core-node/get-info.example.json
/v2/pox:
get:
summary: Get PoX details
description: Get Proof of Transfer (PoX) information. Can be used for Stacking.
tags:
- Info
operationId: get_pox_info
responses:
200:
description: Success
content:
application/json:
schema:
$ref: ./api/core-node/get-pox.schema.json
example:
$ref: ./api/core-node/get-pox.example.json

View File

@@ -1455,6 +1455,15 @@ impl<'a> SortitionHandleConn<'a> {
Ok(anchor_block_hash)
}
fn get_reward_set_size(&self) -> Result<u16, db_error> {
self.get_tip_indexed(&db_keys::pox_reward_set_size())
.map(|x| {
db_keys::reward_set_size_from_string(
&x.expect("CORRUPTION: no current reward set size written"),
)
})
}
pub fn get_pox_id(&self) -> Result<PoxId, db_error> {
let pox_id = self
.get_tip_indexed(db_keys::pox_identifier())?
@@ -2938,6 +2947,24 @@ impl SortitionDB {
}
}
pub fn is_pox_active(
&self,
burnchain: &Burnchain,
block: &BlockSnapshot,
) -> Result<bool, db_error> {
let reward_start_height = burnchain.reward_cycle_to_block_height(
burnchain
.block_height_to_reward_cycle(block.block_height)
.ok_or_else(|| db_error::NotFoundError)?,
);
let sort_id_of_start =
get_ancestor_sort_id(&self.index_conn(), reward_start_height, &block.sortition_id)?
.ok_or_else(|| db_error::NotFoundError)?;
let handle = self.index_handle(&sort_id_of_start);
Ok(handle.get_reward_set_size()? > 0)
}
/// Find out how any burn tokens were destroyed in a given block on a given fork.
pub fn get_block_burn_amount(
conn: &Connection,

View File

@@ -1022,28 +1022,48 @@ pub struct RPCPeerInfoData {
pub exit_at_block_height: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RPCPoxCurrentCycleInfo {
pub id: u64,
pub min_threshold_ustx: u64,
pub stacked_ustx: u64,
pub is_pox_active: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RPCPoxNextCycleInfo {
pub id: u64,
pub min_threshold_ustx: u64,
pub min_increment_ustx: u64,
pub stacked_ustx: u64,
pub prepare_phase_start_block_height: u64,
pub blocks_until_prepare_phase: i64,
pub reward_phase_start_block_height: u64,
pub blocks_until_reward_phase: u64,
pub ustx_until_pox_rejection: u64,
}
/// The data we return on GET /v2/pox
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RPCPoxInfoData {
pub contract_id: String,
pub pox_activation_threshold_ustx: u64,
pub first_burnchain_block_height: u64,
pub next_cycle_cur_threshold: u64,
pub cur_cycle_threshold: u64,
pub cur_cycle_stacked_ustx: u64,
pub next_cycle_stacked_ustx: u64,
pub prepare_phase_block_length: u64,
pub reward_phase_block_length: u64,
pub reward_slots: u64,
pub next_rewards_start: u64,
pub next_prepare_phase_start: u64,
pub min_stacking_increment_ustx: u64,
pub pox_activation_threshold: u64,
pub prepare_cycle_length: u64,
pub rejection_fraction: u64,
pub total_liquid_supply_ustx: u64,
pub current_cycle: RPCPoxCurrentCycleInfo,
pub next_cycle: RPCPoxNextCycleInfo,
// below are included for backwards-compatibility
pub min_amount_ustx: u64,
pub prepare_cycle_length: u64,
pub reward_cycle_id: u64,
pub reward_cycle_length: u64,
pub rejection_votes_left_required: u64,
pub total_liquid_supply_ustx: u64,
pub next_reward_cycle_in: u64,
pub next_prepare_phase_in: u64,
}
#[derive(Debug, Clone, PartialEq, Copy, Hash)]

View File

@@ -17,11 +17,11 @@
along with Blockstack. If not, see <http://www.gnu.org/licenses/>.
*/
use std::fmt;
use std::io;
use std::io::prelude::*;
use std::io::{Read, Seek, SeekFrom, Write};
use std::net::SocketAddr;
use std::{convert::TryFrom, fmt};
use core::mempool::*;
use net::atlas::{AtlasDB, Attachment, MAX_ATTACHMENT_INV_PAGES_PER_REQUEST};
@@ -108,6 +108,8 @@ use vm::{
use rand::prelude::*;
use rand::thread_rng;
use super::{RPCPoxCurrentCycleInfo, RPCPoxNextCycleInfo};
pub const STREAM_CHUNK_SIZE: u64 = 4096;
#[derive(Default)]
@@ -338,18 +340,13 @@ impl RPCPoxInfoData {
let next_reward_cycle_in = reward_cycle_length - (effective_height % reward_cycle_length);
let next_rewards_start = burnchain_tip.block_height + next_reward_cycle_in;
let next_reward_cycle_prepare_phase_start = next_rewards_start - prepare_cycle_length;
let next_prepare_phase_start = next_rewards_start - prepare_cycle_length;
let next_prepare_phase_start =
if burnchain_tip.block_height < next_reward_cycle_prepare_phase_start {
next_reward_cycle_prepare_phase_start
} else {
// currently in a prepare phase, so the next prepare phase start is actually the reward cycle after
// next
next_reward_cycle_prepare_phase_start + reward_cycle_length
};
let next_prepare_phase_in = next_prepare_phase_start - burnchain_tip.block_height;
let next_prepare_phase_in = i64::try_from(next_prepare_phase_start)
.map_err(|_| net_error::ChainstateError("Burn block height overflowed i64".into()))?
- i64::try_from(burnchain_tip.block_height).map_err(|_| {
net_error::ChainstateError("Burn block height overflowed i64".into())
})?;
let cur_cycle_stacked_ustx =
chainstate.get_total_ustx_stacked(&sortdb, tip, reward_cycle_id as u128)?;
@@ -370,31 +367,45 @@ impl RPCPoxInfoData {
reward_slots as u128,
) as u64;
let pox_activation_threshold = (total_liquid_supply_ustx as u128)
let pox_activation_threshold_ustx = (total_liquid_supply_ustx as u128)
.checked_mul(pox_consts.pox_participation_threshold_pct as u128)
.map(|x| x / 100)
.ok_or_else(|| net_error::DBError(db_error::Overflow))?
as u64;
let cur_cycle_pox_active = sortdb.is_pox_active(burnchain, &burnchain_tip)?;
Ok(RPCPoxInfoData {
contract_id: boot::boot_code_id("pox", chainstate.mainnet).to_string(),
pox_activation_threshold_ustx,
first_burnchain_block_height,
min_stacking_increment_ustx,
next_cycle_cur_threshold: next_threshold,
cur_cycle_threshold,
next_cycle_stacked_ustx: next_cycle_stacked_ustx as u64,
cur_cycle_stacked_ustx: cur_cycle_stacked_ustx as u64,
prepare_phase_block_length: prepare_cycle_length,
reward_phase_block_length: reward_cycle_length - prepare_cycle_length,
reward_slots,
pox_activation_threshold,
next_rewards_start,
next_prepare_phase_start,
next_prepare_phase_in,
prepare_cycle_length,
rejection_fraction,
total_liquid_supply_ustx,
current_cycle: RPCPoxCurrentCycleInfo {
id: reward_cycle_id,
min_threshold_ustx: cur_cycle_threshold,
stacked_ustx: cur_cycle_stacked_ustx as u64,
is_pox_active: cur_cycle_pox_active,
},
next_cycle: RPCPoxNextCycleInfo {
id: reward_cycle_id + 1,
min_threshold_ustx: next_threshold,
min_increment_ustx: min_stacking_increment_ustx,
stacked_ustx: next_cycle_stacked_ustx as u64,
prepare_phase_start_block_height: next_prepare_phase_start,
blocks_until_prepare_phase: next_prepare_phase_in,
reward_phase_start_block_height: next_rewards_start,
blocks_until_reward_phase: next_reward_cycle_in,
ustx_until_pox_rejection: rejection_votes_left_required,
},
min_amount_ustx: next_threshold,
prepare_cycle_length,
reward_cycle_id,
reward_cycle_length,
rejection_votes_left_required,
total_liquid_supply_ustx,
next_reward_cycle_in,
})
}

View File

@@ -2090,14 +2090,15 @@ fn pox_integration_test() {
&format!("ST000000000000000000002AMW42H.pox")
);
assert_eq!(pox_info.first_burnchain_block_height, 0);
assert_eq!(pox_info.next_cycle_cur_threshold, 125080000000000);
assert_eq!(pox_info.cur_cycle_threshold, 125080000000000);
assert_eq!(pox_info.cur_cycle_stacked_ustx, 0);
assert_eq!(pox_info.next_cycle_stacked_ustx, 0);
assert_eq!(pox_info.next_cycle.min_threshold_ustx, 125080000000000);
assert_eq!(pox_info.current_cycle.min_threshold_ustx, 125080000000000);
assert_eq!(pox_info.current_cycle.stacked_ustx, 0);
assert_eq!(pox_info.current_cycle.is_pox_active, false);
assert_eq!(pox_info.next_cycle.stacked_ustx, 0);
assert_eq!(pox_info.reward_slots as u32, pox_constants.reward_slots());
assert_eq!(pox_info.next_rewards_start, 210);
assert_eq!(pox_info.next_prepare_phase_start, 205);
assert_eq!(pox_info.min_stacking_increment_ustx, 20845173515333);
assert_eq!(pox_info.next_cycle.reward_phase_start_block_height, 210);
assert_eq!(pox_info.next_cycle.prepare_phase_start_block_height, 205);
assert_eq!(pox_info.next_cycle.min_increment_ustx, 20845173515333);
assert_eq!(
pox_info.prepare_cycle_length as u32,
pox_constants.prepare_length
@@ -2107,6 +2108,8 @@ fn pox_integration_test() {
pox_constants.pox_rejection_fraction
);
assert_eq!(pox_info.reward_cycle_id, 0);
assert_eq!(pox_info.current_cycle.id, 0);
assert_eq!(pox_info.next_cycle.id, 1);
assert_eq!(
pox_info.reward_cycle_length as u32,
pox_constants.reward_cycle_length
@@ -2155,13 +2158,15 @@ fn pox_integration_test() {
&format!("ST000000000000000000002AMW42H.pox")
);
assert_eq!(pox_info.first_burnchain_block_height, 0);
assert_eq!(pox_info.next_cycle_cur_threshold, 125080000000000);
assert_eq!(pox_info.cur_cycle_threshold, 125080000000000);
assert_eq!(pox_info.cur_cycle_stacked_ustx, 1000000000000000);
assert_eq!(pox_info.next_cycle_stacked_ustx, 1000000000000000);
assert_eq!(pox_info.next_cycle.min_threshold_ustx, 125080000000000);
assert_eq!(pox_info.current_cycle.min_threshold_ustx, 125080000000000);
assert_eq!(pox_info.current_cycle.stacked_ustx, 1000000000000000);
assert!(pox_info.pox_activation_threshold_ustx > 1500000000000000);
assert_eq!(pox_info.current_cycle.is_pox_active, false);
assert_eq!(pox_info.next_cycle.stacked_ustx, 1000000000000000);
assert_eq!(pox_info.reward_slots as u32, pox_constants.reward_slots());
assert_eq!(pox_info.next_rewards_start, 225);
assert_eq!(pox_info.next_prepare_phase_start, 220);
assert_eq!(pox_info.next_cycle.reward_phase_start_block_height, 225);
assert_eq!(pox_info.next_cycle.prepare_phase_start_block_height, 220);
assert_eq!(
pox_info.prepare_cycle_length as u32,
pox_constants.prepare_length
@@ -2171,6 +2176,8 @@ fn pox_integration_test() {
pox_constants.pox_rejection_fraction
);
assert_eq!(pox_info.reward_cycle_id, 14);
assert_eq!(pox_info.current_cycle.id, 14);
assert_eq!(pox_info.next_cycle.id, 15);
assert_eq!(
pox_info.reward_cycle_length as u32,
pox_constants.reward_cycle_length
@@ -2277,18 +2284,21 @@ fn pox_integration_test() {
}
let pox_info = get_pox_info(&http_origin);
assert_eq!(
&pox_info.contract_id,
&format!("ST000000000000000000002AMW42H.pox")
);
assert_eq!(pox_info.first_burnchain_block_height, 0);
assert_eq!(pox_info.next_cycle_cur_threshold, 125080000000000);
assert_eq!(pox_info.cur_cycle_threshold, 125080000000000);
assert_eq!(pox_info.cur_cycle_stacked_ustx, 1000000000000000);
assert_eq!(pox_info.next_cycle_stacked_ustx, 2000000000000000);
assert_eq!(pox_info.next_cycle.min_threshold_ustx, 125080000000000);
assert_eq!(pox_info.current_cycle.min_threshold_ustx, 125080000000000);
assert_eq!(pox_info.current_cycle.stacked_ustx, 1000000000000000);
assert_eq!(pox_info.current_cycle.is_pox_active, false);
assert_eq!(pox_info.next_cycle.stacked_ustx, 2000000000000000);
assert_eq!(pox_info.reward_slots as u32, pox_constants.reward_slots());
assert_eq!(pox_info.next_rewards_start, 225);
assert_eq!(pox_info.next_prepare_phase_start, 235);
assert_eq!(pox_info.next_cycle.reward_phase_start_block_height, 225);
assert_eq!(pox_info.next_cycle.prepare_phase_start_block_height, 220);
assert_eq!(pox_info.next_cycle.blocks_until_prepare_phase, -4);
assert_eq!(
pox_info.prepare_cycle_length as u32,
pox_constants.prepare_length
@@ -2298,6 +2308,8 @@ fn pox_integration_test() {
pox_constants.pox_rejection_fraction
);
assert_eq!(pox_info.reward_cycle_id, 14);
assert_eq!(pox_info.current_cycle.id, 14);
assert_eq!(pox_info.next_cycle.id, 15);
assert_eq!(
pox_info.reward_cycle_length as u32,
pox_constants.reward_cycle_length
@@ -2323,6 +2335,21 @@ fn pox_integration_test() {
eprintln!("Sort height: {}", sort_height);
}
let pox_info = get_pox_info(&http_origin);
assert_eq!(
&pox_info.contract_id,
&format!("ST000000000000000000002AMW42H.pox")
);
assert_eq!(pox_info.first_burnchain_block_height, 0);
assert_eq!(pox_info.current_cycle.min_threshold_ustx, 125080000000000);
assert_eq!(pox_info.current_cycle.stacked_ustx, 2000000000000000);
assert_eq!(pox_info.current_cycle.is_pox_active, true);
assert_eq!(pox_info.next_cycle.reward_phase_start_block_height, 240);
assert_eq!(pox_info.next_cycle.prepare_phase_start_block_height, 235);
assert_eq!(pox_info.next_cycle.blocks_until_prepare_phase, -4);
assert_eq!(pox_info.next_reward_cycle_in, 1);
// we should have received _seven_ Bitcoin commitments, because our commitment was 7 * threshold
let utxos = btc_regtest_controller.get_all_utxos(&pox_pubkey);