@@ -43,13 +43,26 @@ pub struct RPCGetHealthResponse {
43
43
pub node_stacks_tip_height : u64 ,
44
44
}
45
45
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
+
46
55
#[ derive( Clone ) ]
47
56
/// Empty request handler for the GET /v3/health endpoint
48
- pub struct RPCGetHealthRequestHandler { }
57
+ pub struct RPCGetHealthRequestHandler {
58
+ neighbors_scope : Option < NeighborsScope > ,
59
+ }
49
60
50
61
impl RPCGetHealthRequestHandler {
51
62
pub fn new ( ) -> Self {
52
- Self { }
63
+ Self {
64
+ neighbors_scope : None ,
65
+ }
53
66
}
54
67
}
55
68
@@ -82,7 +95,14 @@ impl HttpRequest for RPCGetHealthRequestHandler {
82
95
) ) ;
83
96
}
84
97
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)
86
106
}
87
107
}
88
108
@@ -97,7 +117,9 @@ fn create_error_response(
97
117
98
118
impl RPCRequestHandler for RPCGetHealthRequestHandler {
99
119
/// Reset internal state
100
- fn restart ( & mut self ) { }
120
+ fn restart ( & mut self ) {
121
+ self . neighbors_scope = None ;
122
+ }
101
123
102
124
/// Make the response
103
125
fn try_handle_request (
@@ -106,47 +128,51 @@ impl RPCRequestHandler for RPCGetHealthRequestHandler {
106
128
_contents : HttpRequestContents ,
107
129
node : & mut StacksNodeState ,
108
130
) -> 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
+
109
137
node. with_node_state ( |network, _sortdb, _chainstate, _mempool, _rpc_args| {
110
138
let current_epoch = network. get_current_epoch ( ) ;
111
139
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 ,
131
149
)
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
+ } ;
135
160
136
- let peer_max_stacks_height_opt = {
161
+ let peer_max_stacks_height_opt = {
137
162
if current_epoch. epoch_id < StacksEpochId :: Epoch30 {
138
163
// When the node enters Epoch 3.0, ibd is not accurate. In nakamoto it's always set to false.
139
164
// See the implementation of `RunLoop::start` in `testnet/stacks-node/src/run_loop/nakamoto.rs`,
140
165
// specifically the section and comment where `let ibd = false`, for details.
141
166
let ibd = infer_initial_burnchain_block_download (
142
167
& network. burnchain ,
143
- bitcoin_last_processed_height ,
144
- bitcoin_tip_height ,
168
+ network . burnchain_tip . block_height ,
169
+ network . chain_view . burn_block_height ,
145
170
) ;
171
+
146
172
// get max block height amongst bootstrap nodes
147
173
match network. inv_state . as_ref ( ) {
148
174
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)
150
176
}
151
177
None => {
152
178
return create_error_response (
@@ -156,9 +182,11 @@ impl RPCRequestHandler for RPCGetHealthRequestHandler {
156
182
}
157
183
}
158
184
} 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 ( ) ) ;
160
186
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
+ }
162
190
None => {
163
191
return create_error_response (
164
192
& preamble,
@@ -169,10 +197,12 @@ impl RPCRequestHandler for RPCGetHealthRequestHandler {
169
197
}
170
198
} ;
171
199
172
- match peer_max_stacks_height_opt {
200
+ match peer_max_stacks_height_opt {
173
201
Some ( max_stacks_height_of_neighbors) => {
174
202
// 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) ;
176
206
177
207
let preamble = HttpResponsePreamble :: ok_json ( & preamble) ;
178
208
let data = RPCGetHealthResponse {
@@ -206,12 +236,15 @@ impl HttpResponse for RPCGetHealthRequestHandler {
206
236
207
237
impl StacksHttpRequest {
208
238
/// 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 {
210
240
StacksHttpRequest :: new_for_peer (
211
241
host,
212
242
"GET" . into ( ) ,
213
243
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
+ ) ,
215
248
)
216
249
. expect ( "FATAL: failed to construct request from infallible data" )
217
250
}
0 commit comments