Skip to content

Commit 6b100ef

Browse files
committed
add support for download state confirmed. Add query param to query all peers
1 parent 13381ca commit 6b100ef

File tree

7 files changed

+330
-123
lines changed

7 files changed

+330
-123
lines changed

docs/rpc/openapi.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,19 @@ paths:
909909
tags:
910910
- Info
911911
operationId: get_health
912+
parameters:
913+
- in: query
914+
name: neighbors
915+
description: "Specifies the set of peers to use for health checks.
916+
- `initial` (default): Use only the initial bootstrap peers.
917+
- `all`: Use all connected peers."
918+
required: false
919+
schema:
920+
type: string
921+
enum:
922+
- initial
923+
- all
924+
default: initial
912925
responses:
913926
200:
914927
description: Success
@@ -918,6 +931,13 @@ paths:
918931
$ref: ./api/core-node/get-health.schema.json
919932
example:
920933
$ref: ./api/core-node/get-health.example.json
934+
400:
935+
description: Bad request, such as an invalid `neighbors` query parameter.
936+
content:
937+
application/json:
938+
schema:
939+
type: string
940+
example: "Invalid `neighbors` query parameter: allowed values are `initial` or `all`"
921941
500:
922942
description: |
923943
Failed to query for health (e.g., no data or no valid peers to query from).

stackslib/src/net/api/gethealth.rs

Lines changed: 69 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,26 @@ pub struct RPCGetHealthResponse {
4343
pub node_stacks_tip_height: u64,
4444
}
4545

46+
const NEIGHBORS_SCOPE_PARAM_NAME: &str = "neighbors";
47+
48+
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
49+
#[serde(rename_all = "lowercase")]
50+
pub enum NeighborsScope {
51+
Initial,
52+
All,
53+
}
54+
4655
#[derive(Clone)]
4756
/// Empty request handler for the GET /v3/health endpoint
48-
pub struct RPCGetHealthRequestHandler {}
57+
pub struct RPCGetHealthRequestHandler {
58+
neighbors_scope: Option<NeighborsScope>,
59+
}
4960

5061
impl RPCGetHealthRequestHandler {
5162
pub fn new() -> Self {
52-
Self {}
63+
Self {
64+
neighbors_scope: None,
65+
}
5366
}
5467
}
5568

@@ -82,7 +95,14 @@ impl HttpRequest for RPCGetHealthRequestHandler {
8295
));
8396
}
8497

85-
Ok(HttpRequestContents::new().query_string(query))
98+
let req_contents = HttpRequestContents::new().query_string(query);
99+
if let Some(scope) = req_contents.get_query_arg(NEIGHBORS_SCOPE_PARAM_NAME) {
100+
self.neighbors_scope = Some(serde_json::from_str(scope.as_str()).map_err(|_e| {
101+
Error::Http(400, format!("Invalid `neighbors` query parameter: `{}`, allowed values are `initial` or `all`", scope))
102+
})?);
103+
}
104+
105+
Ok(req_contents)
86106
}
87107
}
88108

@@ -97,7 +117,9 @@ fn create_error_response(
97117

98118
impl RPCRequestHandler for RPCGetHealthRequestHandler {
99119
/// Reset internal state
100-
fn restart(&mut self) {}
120+
fn restart(&mut self) {
121+
self.neighbors_scope = None;
122+
}
101123

102124
/// Make the response
103125
fn try_handle_request(
@@ -106,47 +128,51 @@ impl RPCRequestHandler for RPCGetHealthRequestHandler {
106128
_contents: HttpRequestContents,
107129
node: &mut StacksNodeState,
108130
) -> Result<(HttpResponsePreamble, HttpResponseContents), NetError> {
131+
let neighbors_scope = self
132+
.neighbors_scope
133+
.take()
134+
.unwrap_or(NeighborsScope::Initial);
135+
let use_all_neighbors = neighbors_scope == NeighborsScope::All;
136+
109137
node.with_node_state(|network, _sortdb, _chainstate, _mempool, _rpc_args| {
110138
let current_epoch = network.get_current_epoch();
111139

112-
let initial_neighbors = PeerDB::get_valid_initial_neighbors(
113-
network.peerdb.conn(),
114-
network.local_peer.network_id,
115-
current_epoch.network_epoch,
116-
network.peer_version,
117-
network.chain_view.burn_block_height,
118-
)
119-
.map_err(NetError::from)?;
120-
121-
let node_stacks_tip_height = network.stacks_tip.height;
122-
let bitcoin_tip_height = network.chain_view.burn_block_height;
123-
let bitcoin_last_processed_height = network.burnchain_tip.block_height;
124-
// no bootstrap nodes found, unable to determine health.
125-
if initial_neighbors.len() == 0 {
126-
return StacksHttpResponse::new_error(
127-
&preamble,
128-
&HttpServerError::new(
129-
"No viable bootstrap peers found, unable to determine health".into(),
130-
),
140+
let neighbors_arg = if use_all_neighbors {
141+
None
142+
} else {
143+
let initial_neighbors = PeerDB::get_valid_initial_neighbors(
144+
network.peerdb.conn(),
145+
network.local_peer.network_id,
146+
current_epoch.network_epoch,
147+
network.peer_version,
148+
network.chain_view.burn_block_height,
131149
)
132-
.try_into_contents()
133-
.map_err(NetError::from);
134-
}
150+
.map_err(NetError::from)?;
151+
152+
if initial_neighbors.is_empty() {
153+
return create_error_response(
154+
&preamble,
155+
"No viable bootstrap peers found, unable to determine health",
156+
);
157+
}
158+
Some(initial_neighbors)
159+
};
135160

136-
let peer_max_stacks_height_opt = {
161+
let peer_max_stacks_height_opt = {
137162
if current_epoch.epoch_id < StacksEpochId::Epoch30 {
138163
// When the node enters Epoch 3.0, ibd is not accurate. In nakamoto it's always set to false.
139164
// See the implementation of `RunLoop::start` in `testnet/stacks-node/src/run_loop/nakamoto.rs`,
140165
// specifically the section and comment where `let ibd = false`, for details.
141166
let ibd = infer_initial_burnchain_block_download(
142167
&network.burnchain,
143-
bitcoin_last_processed_height,
144-
bitcoin_tip_height,
168+
network.burnchain_tip.block_height,
169+
network.chain_view.burn_block_height,
145170
);
171+
146172
// get max block height amongst bootstrap nodes
147173
match network.inv_state.as_ref() {
148174
Some(inv_state) => {
149-
inv_state.get_max_stacks_height_of_neighbors(&initial_neighbors, ibd)
175+
inv_state.get_max_stacks_height_of_neighbors(neighbors_arg.as_deref(), ibd)
150176
}
151177
None => {
152178
return create_error_response(
@@ -156,9 +182,11 @@ impl RPCRequestHandler for RPCGetHealthRequestHandler {
156182
}
157183
}
158184
} else {
159-
let initial_neighbours_addresses: Vec<NeighborAddress> = initial_neighbors.iter().map(NeighborAddress::from_neighbor).collect();
185+
let neighbors_arg: Option<Vec<NeighborAddress>> = neighbors_arg.as_ref().map(|v| v.iter().map(NeighborAddress::from_neighbor).collect());
160186
match network.block_downloader_nakamoto.as_ref() {
161-
Some(block_downloader_nakamoto) => block_downloader_nakamoto.get_max_stacks_height_of_neighbors(&initial_neighbours_addresses),
187+
Some(block_downloader_nakamoto) => {
188+
block_downloader_nakamoto.get_max_stacks_height_of_neighbors(neighbors_arg.as_deref())
189+
}
162190
None => {
163191
return create_error_response(
164192
&preamble,
@@ -169,10 +197,12 @@ impl RPCRequestHandler for RPCGetHealthRequestHandler {
169197
}
170198
};
171199

172-
match peer_max_stacks_height_opt {
200+
match peer_max_stacks_height_opt {
173201
Some(max_stacks_height_of_neighbors) => {
174202
// There could be a edge case where our node is ahead of all peers.
175-
let difference_from_max_peer = max_stacks_height_of_neighbors.saturating_sub(node_stacks_tip_height);
203+
let node_stacks_tip_height = network.stacks_tip.height;
204+
let difference_from_max_peer =
205+
max_stacks_height_of_neighbors.saturating_sub(node_stacks_tip_height);
176206

177207
let preamble = HttpResponsePreamble::ok_json(&preamble);
178208
let data = RPCGetHealthResponse {
@@ -206,12 +236,15 @@ impl HttpResponse for RPCGetHealthRequestHandler {
206236

207237
impl StacksHttpRequest {
208238
/// Make a new get-unconfirmed-tx request
209-
pub fn new_gethealth(host: PeerHost) -> StacksHttpRequest {
239+
pub fn new_gethealth(host: PeerHost, neighbors_scope: NeighborsScope) -> StacksHttpRequest {
210240
StacksHttpRequest::new_for_peer(
211241
host,
212242
"GET".into(),
213243
format!("/v3/health"),
214-
HttpRequestContents::new(),
244+
HttpRequestContents::new().query_arg(
245+
NEIGHBORS_SCOPE_PARAM_NAME.into(),
246+
serde_json::to_string(&neighbors_scope).unwrap(),
247+
),
215248
)
216249
.expect("FATAL: failed to construct request from infallible data")
217250
}

0 commit comments

Comments
 (0)