14
14
// You should have received a copy of the GNU General Public License
15
15
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16
16
17
+ use std:: fmt;
18
+ use std:: str:: FromStr ;
19
+
17
20
use regex:: { Captures , Regex } ;
18
21
use stacks_common:: types:: net:: PeerHost ;
19
22
use stacks_common:: types:: StacksEpochId ;
@@ -43,13 +46,53 @@ pub struct RPCGetHealthResponse {
43
46
pub node_stacks_tip_height : u64 ,
44
47
}
45
48
49
+ const NEIGHBORS_SCOPE_PARAM_NAME : & str = "neighbors" ;
50
+
51
+ #[ derive( Clone , Debug , PartialEq ) ]
52
+ pub enum NeighborsScope {
53
+ Initial ,
54
+ All ,
55
+ }
56
+
57
+ impl FromStr for NeighborsScope {
58
+ type Err = crate :: net:: http:: Error ;
59
+
60
+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
61
+ match s {
62
+ "initial" => Ok ( NeighborsScope :: Initial ) ,
63
+ "all" => Ok ( NeighborsScope :: All ) ,
64
+ _ => Err ( crate :: net:: http:: Error :: Http (
65
+ 400 ,
66
+ format ! (
67
+ "Invalid `neighbors` query parameter: `{}`, allowed values are `initial` or `all`" ,
68
+ s
69
+ ) ,
70
+ ) ) ,
71
+ }
72
+ }
73
+ }
74
+
75
+ impl fmt:: Display for NeighborsScope {
76
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
77
+ let s = match self {
78
+ NeighborsScope :: Initial => "initial" ,
79
+ NeighborsScope :: All => "all" ,
80
+ } ;
81
+ write ! ( f, "{s}" )
82
+ }
83
+ }
84
+
46
85
#[ derive( Clone ) ]
47
86
/// Empty request handler for the GET /v3/health endpoint
48
- pub struct RPCGetHealthRequestHandler { }
87
+ pub struct RPCGetHealthRequestHandler {
88
+ neighbors_scope : Option < NeighborsScope > ,
89
+ }
49
90
50
91
impl RPCGetHealthRequestHandler {
51
92
pub fn new ( ) -> Self {
52
- Self { }
93
+ Self {
94
+ neighbors_scope : None ,
95
+ }
53
96
}
54
97
}
55
98
@@ -82,7 +125,12 @@ impl HttpRequest for RPCGetHealthRequestHandler {
82
125
) ) ;
83
126
}
84
127
85
- Ok ( HttpRequestContents :: new ( ) . query_string ( query) )
128
+ let req_contents = HttpRequestContents :: new ( ) . query_string ( query) ;
129
+ if let Some ( scope) = req_contents. get_query_arg ( NEIGHBORS_SCOPE_PARAM_NAME ) {
130
+ self . neighbors_scope = Some ( scope. parse ( ) ?) ;
131
+ }
132
+
133
+ Ok ( req_contents)
86
134
}
87
135
}
88
136
@@ -97,7 +145,9 @@ fn create_error_response(
97
145
98
146
impl RPCRequestHandler for RPCGetHealthRequestHandler {
99
147
/// Reset internal state
100
- fn restart ( & mut self ) { }
148
+ fn restart ( & mut self ) {
149
+ self . neighbors_scope = None ;
150
+ }
101
151
102
152
/// Make the response
103
153
fn try_handle_request (
@@ -106,47 +156,51 @@ impl RPCRequestHandler for RPCGetHealthRequestHandler {
106
156
_contents : HttpRequestContents ,
107
157
node : & mut StacksNodeState ,
108
158
) -> Result < ( HttpResponsePreamble , HttpResponseContents ) , NetError > {
159
+ let neighbors_scope = self
160
+ . neighbors_scope
161
+ . take ( )
162
+ . unwrap_or ( NeighborsScope :: Initial ) ;
163
+ let use_all_neighbors = neighbors_scope == NeighborsScope :: All ;
164
+
109
165
node. with_node_state ( |network, _sortdb, _chainstate, _mempool, _rpc_args| {
110
166
let current_epoch = network. get_current_epoch ( ) ;
111
167
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
- ) ,
168
+ let neighbors_arg = if use_all_neighbors {
169
+ None
170
+ } else {
171
+ let initial_neighbors = PeerDB :: get_valid_initial_neighbors (
172
+ network. peerdb . conn ( ) ,
173
+ network. local_peer . network_id ,
174
+ current_epoch. network_epoch ,
175
+ network. peer_version ,
176
+ network. chain_view . burn_block_height ,
131
177
)
132
- . try_into_contents ( )
133
- . map_err ( NetError :: from) ;
134
- }
178
+ . map_err ( NetError :: from) ?;
135
179
136
- let peer_max_stacks_height_opt = {
180
+ if initial_neighbors. is_empty ( ) {
181
+ return create_error_response (
182
+ & preamble,
183
+ "No viable bootstrap peers found, unable to determine health" ,
184
+ ) ;
185
+ }
186
+ Some ( initial_neighbors)
187
+ } ;
188
+
189
+ let peer_max_stacks_height_opt = {
137
190
if current_epoch. epoch_id < StacksEpochId :: Epoch30 {
138
191
// When the node enters Epoch 3.0, ibd is not accurate. In nakamoto it's always set to false.
139
192
// See the implementation of `RunLoop::start` in `testnet/stacks-node/src/run_loop/nakamoto.rs`,
140
193
// specifically the section and comment where `let ibd = false`, for details.
141
194
let ibd = infer_initial_burnchain_block_download (
142
195
& network. burnchain ,
143
- bitcoin_last_processed_height ,
144
- bitcoin_tip_height ,
196
+ network . burnchain_tip . block_height ,
197
+ network . chain_view . burn_block_height ,
145
198
) ;
199
+
146
200
// get max block height amongst bootstrap nodes
147
201
match network. inv_state . as_ref ( ) {
148
202
Some ( inv_state) => {
149
- inv_state. get_max_stacks_height_of_neighbors ( & initial_neighbors , ibd)
203
+ inv_state. get_max_stacks_height_of_neighbors ( neighbors_arg . as_deref ( ) , ibd)
150
204
}
151
205
None => {
152
206
return create_error_response (
@@ -156,9 +210,11 @@ impl RPCRequestHandler for RPCGetHealthRequestHandler {
156
210
}
157
211
}
158
212
} else {
159
- let initial_neighbours_addresses : Vec < NeighborAddress > = initial_neighbors . iter ( ) . map ( NeighborAddress :: from_neighbor) . collect ( ) ;
213
+ let neighbors_arg : Option < Vec < NeighborAddress > > = neighbors_arg . as_ref ( ) . map ( |v| v . iter ( ) . map ( NeighborAddress :: from_neighbor) . collect ( ) ) ;
160
214
match network. block_downloader_nakamoto . as_ref ( ) {
161
- Some ( block_downloader_nakamoto) => block_downloader_nakamoto. get_max_stacks_height_of_neighbors ( & initial_neighbours_addresses) ,
215
+ Some ( block_downloader_nakamoto) => {
216
+ block_downloader_nakamoto. get_max_stacks_height_of_neighbors ( neighbors_arg. as_deref ( ) )
217
+ }
162
218
None => {
163
219
return create_error_response (
164
220
& preamble,
@@ -169,10 +225,12 @@ impl RPCRequestHandler for RPCGetHealthRequestHandler {
169
225
}
170
226
} ;
171
227
172
- match peer_max_stacks_height_opt {
228
+ match peer_max_stacks_height_opt {
173
229
Some ( max_stacks_height_of_neighbors) => {
174
230
// 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) ;
231
+ let node_stacks_tip_height = network. stacks_tip . height ;
232
+ let difference_from_max_peer =
233
+ max_stacks_height_of_neighbors. saturating_sub ( node_stacks_tip_height) ;
176
234
177
235
let preamble = HttpResponsePreamble :: ok_json ( & preamble) ;
178
236
let data = RPCGetHealthResponse {
@@ -205,13 +263,15 @@ impl HttpResponse for RPCGetHealthRequestHandler {
205
263
}
206
264
207
265
impl StacksHttpRequest {
208
- /// Make a new get-unconfirmed-tx request
209
- pub fn new_gethealth ( host : PeerHost ) -> StacksHttpRequest {
266
+ pub fn new_gethealth ( host : PeerHost , neighbors_scope : NeighborsScope ) -> StacksHttpRequest {
210
267
StacksHttpRequest :: new_for_peer (
211
268
host,
212
269
"GET" . into ( ) ,
213
- format ! ( "/v3/health" ) ,
214
- HttpRequestContents :: new ( ) ,
270
+ "/v3/health" . into ( ) ,
271
+ HttpRequestContents :: new ( ) . query_arg (
272
+ NEIGHBORS_SCOPE_PARAM_NAME . into ( ) ,
273
+ neighbors_scope. to_string ( ) ,
274
+ ) ,
215
275
)
216
276
. expect ( "FATAL: failed to construct request from infallible data" )
217
277
}
0 commit comments