Skip to content

Commit bcf0f4b

Browse files
authored
Merge pull request #6447 from rdeioris/feat/tenureblocks_byhash_byheight_endpoints
Added /v3/tenures/blocks/hash/{burnchain_block_hash} and /v3/tenures/blocks/hash/{burnchain_block_height} endpoints
2 parents 06a01b5 + 6268d22 commit bcf0f4b

File tree

11 files changed

+934
-17
lines changed

11 files changed

+934
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
1212
- Renamed `clarity-serialization` to `clarity-types`.
1313
- Add `stackerdb_timeout_secs` to miner config for limiting duration of StackerDB HTTP requests.
1414
- When determining a global transaction replay set, the state evaluator now uses a longest-common-prefix algorithm to find a replay set in the case where a single replay set has less than 70% of signer weight.
15-
- New endpoint /v3/tenures/blocks/ allowing retrieving the list of stacks blocks from a burn block
15+
- New endpoints /v3/tenures/blocks/, /v3/tenures/blocks/hash, /v3/tenures/blocks/height allowing retrieving the list of stacks blocks from a burn block
1616
- Creates epoch 3.3 and costs-4 in preparation for a hardfork to activate Clarity 4
1717
- Adds support for new Clarity 4 builtins (not activated until epoch 3.3):
1818
- `contract-hash?`

docs/rpc/openapi.yaml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,73 @@ paths:
10931093
"500":
10941094
$ref: "#/components/responses/InternalServerError"
10951095

1096+
/v3/tenures/blocks/hash/{burnchain_block_hash}:
1097+
get:
1098+
summary: Get the list of Nakamoto Stacks blocks in a tenure given Bitcoin block hash
1099+
tags:
1100+
- Blocks
1101+
security: []
1102+
operationId: getTenureBlocksByHash
1103+
description: |
1104+
Get the list of Nakamoto blocks in a tenure given the Bitcoin block hash. The blocks will be
1105+
shown in order from highest to lowest. This is only for Nakamoto blocks, Epoch2 ones will not be shown.
1106+
parameters:
1107+
- name: burnchain_block_hash
1108+
in: path
1109+
description: The hex-encoded Bitcoin block hash of the tenure to query (64 hexadecimal characters, without 0x prefix)
1110+
required: true
1111+
schema:
1112+
type: string
1113+
pattern: "^[0-9a-f]{64}$"
1114+
responses:
1115+
"200":
1116+
description: List of Stacks blocks in the tenure
1117+
content:
1118+
application/json:
1119+
schema:
1120+
$ref: "#/components/schemas/TenureBlocks"
1121+
example:
1122+
$ref: "./components/examples/tenure-blocks.example.json"
1123+
"400":
1124+
$ref: "#/components/responses/BadRequest"
1125+
"404":
1126+
$ref: "#/components/responses/NotFound"
1127+
"500":
1128+
$ref: "#/components/responses/InternalServerError"
1129+
1130+
/v3/tenures/blocks/height/{burnchain_block_height}:
1131+
get:
1132+
summary: Get the list of Nakamoto Stacks blocks in a tenure given Bitcoin block height
1133+
tags:
1134+
- Blocks
1135+
security: []
1136+
operationId: getTenureBlocksByHeight
1137+
description: |
1138+
Get the list of Nakamoto blocks in a tenure given the Bitcoin block height. The blocks will be
1139+
shown in order from highest to lowest. This is only for Nakamoto blocks, Epoch2 ones will not be shown.
1140+
parameters:
1141+
- name: burnchain_block_height
1142+
in: path
1143+
description: The Bitcoin block height of the tenure to query
1144+
required: true
1145+
schema:
1146+
type: integer
1147+
responses:
1148+
"200":
1149+
description: List of Stacks blocks in the tenure
1150+
content:
1151+
application/json:
1152+
schema:
1153+
$ref: "#/components/schemas/TenureBlocks"
1154+
example:
1155+
$ref: "./components/examples/tenure-blocks.example.json"
1156+
"400":
1157+
$ref: "#/components/responses/BadRequest"
1158+
"404":
1159+
$ref: "#/components/responses/NotFound"
1160+
"500":
1161+
$ref: "#/components/responses/InternalServerError"
1162+
10961163
/v3/sortitions:
10971164
get:
10981165
summary: Get latest sortition information

stackslib/src/chainstate/nakamoto/mod.rs

Lines changed: 132 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2846,6 +2846,27 @@ impl NakamotoChainState {
28462846
Self::get_block_header_nakamoto(chainstate_conn.sqlite(), &block_id)
28472847
}
28482848

2849+
/// Get the first canonical block header in a vector of height-ordered candidates
2850+
fn get_highest_canonical_block_header_from_candidates(
2851+
sort_db: &SortitionDB,
2852+
candidates: Vec<StacksHeaderInfo>,
2853+
) -> Result<Option<StacksHeaderInfo>, ChainstateError> {
2854+
let canonical_sortition_handle = sort_db.index_handle_at_tip();
2855+
for candidate in candidates.into_iter() {
2856+
let Some(ref candidate_ch) = candidate.burn_view else {
2857+
// this is an epoch 2.x header, no burn view to check
2858+
return Ok(Some(candidate));
2859+
};
2860+
let in_canonical_fork = canonical_sortition_handle.processed_block(&candidate_ch)?;
2861+
if in_canonical_fork {
2862+
return Ok(Some(candidate));
2863+
}
2864+
}
2865+
2866+
// did not find any blocks in candidates
2867+
Ok(None)
2868+
}
2869+
28492870
/// Get the highest block in the given tenure on a given fork.
28502871
/// Only works on Nakamoto blocks.
28512872
/// TODO: unit test
@@ -2879,20 +2900,7 @@ impl NakamotoChainState {
28792900
tenure_id,
28802901
)?;
28812902

2882-
let canonical_sortition_handle = sort_db.index_handle_at_tip();
2883-
for candidate in candidates.into_iter() {
2884-
let Some(ref candidate_ch) = candidate.burn_view else {
2885-
// this is an epoch 2.x header, no burn view to check
2886-
return Ok(Some(candidate));
2887-
};
2888-
let in_canonical_fork = canonical_sortition_handle.processed_block(&candidate_ch)?;
2889-
if in_canonical_fork {
2890-
return Ok(Some(candidate));
2891-
}
2892-
}
2893-
2894-
// did not find any blocks in the tenure
2895-
Ok(None)
2903+
Self::get_highest_canonical_block_header_from_candidates(sort_db, candidates)
28962904
}
28972905

28982906
/// DO NOT USE IN CONSENSUS CODE. Different nodes can have different blocks for the same
@@ -2931,6 +2939,116 @@ impl NakamotoChainState {
29312939
Ok(Vec::from_iter(epoch2_x))
29322940
}
29332941

2942+
/// DO NOT USE IN CONSENSUS CODE. Different nodes can have different blocks for the same
2943+
/// tenure.
2944+
///
2945+
/// Get the highest block in a given tenure (identified by burnchain block height) with a canonical
2946+
/// burn_view (i.e., burn_view on the canonical sortition fork). This covers only Nakamoto blocks.
2947+
/// Epoch2 blocks will not be checked.
2948+
pub fn find_highest_known_block_header_in_tenure_by_block_height(
2949+
chainstate: &StacksChainState,
2950+
sort_db: &SortitionDB,
2951+
tenure_height: u64,
2952+
) -> Result<Option<StacksHeaderInfo>, ChainstateError> {
2953+
let chainstate_db_conn = chainstate.db();
2954+
2955+
let candidates =
2956+
Self::get_highest_known_block_header_in_tenure_by_block_height_at_each_burnview(
2957+
chainstate_db_conn,
2958+
tenure_height,
2959+
)?;
2960+
2961+
Self::get_highest_canonical_block_header_from_candidates(sort_db, candidates)
2962+
}
2963+
2964+
/// DO NOT USE IN CONSENSUS CODE. Different nodes can have different blocks for the same
2965+
/// tenure.
2966+
///
2967+
/// Get the highest block in a given tenure (identified by burnchain block hash) with a canonical
2968+
/// burn_view (i.e., burn_view on the canonical sortition fork). This covers only Nakamoto blocks.
2969+
/// Epoch2 blocks will not be checked.
2970+
pub fn find_highest_known_block_header_in_tenure_by_block_hash(
2971+
chainstate: &StacksChainState,
2972+
sort_db: &SortitionDB,
2973+
tenure_block_hash: &BurnchainHeaderHash,
2974+
) -> Result<Option<StacksHeaderInfo>, ChainstateError> {
2975+
let chainstate_db_conn = chainstate.db();
2976+
2977+
let candidates =
2978+
Self::get_highest_known_block_header_in_tenure_by_block_hash_at_each_burnview(
2979+
chainstate_db_conn,
2980+
tenure_block_hash,
2981+
)?;
2982+
2983+
Self::get_highest_canonical_block_header_from_candidates(sort_db, candidates)
2984+
}
2985+
2986+
/// DO NOT USE IN CONSENSUS CODE. Different nodes can have different blocks for the same
2987+
/// tenure.
2988+
///
2989+
/// Get the highest blocks in a given tenure (identified by burnchain block height) at each burn view
2990+
/// active in that tenure. If there are ties at a given burn view, they will both be returned
2991+
fn get_highest_known_block_header_in_tenure_by_block_height_at_each_burnview(
2992+
db: &Connection,
2993+
tenure_height: u64,
2994+
) -> Result<Vec<StacksHeaderInfo>, ChainstateError> {
2995+
// see if we have a nakamoto block in this tenure
2996+
let qry = "
2997+
SELECT h.*
2998+
FROM nakamoto_block_headers h
2999+
JOIN (
3000+
SELECT burn_view, MAX(block_height) AS max_height
3001+
FROM nakamoto_block_headers
3002+
WHERE burn_header_height = ?1
3003+
GROUP BY burn_view
3004+
) maxed
3005+
ON h.burn_view = maxed.burn_view
3006+
AND h.block_height = maxed.max_height
3007+
WHERE h.burn_header_height = ?1
3008+
ORDER BY h.block_height DESC, h.timestamp
3009+
";
3010+
let args = params![tenure_height];
3011+
let out = query_rows(db, qry, args)?;
3012+
if !out.is_empty() {
3013+
return Ok(out);
3014+
}
3015+
3016+
Err(ChainstateError::NoSuchBlockError)
3017+
}
3018+
3019+
/// DO NOT USE IN CONSENSUS CODE. Different nodes can have different blocks for the same
3020+
/// tenure.
3021+
///
3022+
/// Get the highest blocks in a given tenure (identified by burnchain block hash) at each burn view
3023+
/// active in that tenure. If there are ties at a given burn view, they will both be returned
3024+
fn get_highest_known_block_header_in_tenure_by_block_hash_at_each_burnview(
3025+
db: &Connection,
3026+
tenure_block_hash: &BurnchainHeaderHash,
3027+
) -> Result<Vec<StacksHeaderInfo>, ChainstateError> {
3028+
// see if we have a nakamoto block in this tenure
3029+
let qry = "
3030+
SELECT h.*
3031+
FROM nakamoto_block_headers h
3032+
JOIN (
3033+
SELECT burn_view, MAX(block_height) AS max_height
3034+
FROM nakamoto_block_headers
3035+
WHERE burn_header_hash = ?1
3036+
GROUP BY burn_view
3037+
) maxed
3038+
ON h.burn_view = maxed.burn_view
3039+
AND h.block_height = maxed.max_height
3040+
WHERE h.burn_header_hash = ?1
3041+
ORDER BY h.block_height DESC, h.timestamp
3042+
";
3043+
let args = params![tenure_block_hash];
3044+
let out = query_rows(db, qry, args)?;
3045+
if !out.is_empty() {
3046+
return Ok(out);
3047+
}
3048+
3049+
Err(ChainstateError::NoSuchBlockError)
3050+
}
3051+
29343052
/// Get the VRF proof for a Stacks block.
29353053
/// For Nakamoto blocks, this is the VRF proof contained in the coinbase of the tenure-start
29363054
/// block of the given tenure identified by the consensus hash.

stackslib/src/net/api/gettenureblocks.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ impl HttpResponse for RPCNakamotoTenureBlocksRequestHandler {
321321
}
322322

323323
impl StacksHttpRequest {
324-
/// Make a new getinfo request to this endpoint
324+
/// Make a new request to this endpoint
325325
pub fn new_get_tenure_blocks(
326326
host: PeerHost,
327327
consensus_hash: &ConsensusHash,

0 commit comments

Comments
 (0)