@@ -24,6 +24,7 @@ use dpp::version::PlatformVersion;
2424
2525use lru:: LruCache ;
2626use reqwest:: Client ;
27+ use serde:: Deserialize ;
2728use std:: collections:: HashMap ;
2829use std:: error:: Error as StdError ;
2930#[ cfg( not( target_arch = "wasm32" ) ) ]
@@ -33,7 +34,6 @@ use std::sync::{Arc, Mutex};
3334#[ cfg( not( target_arch = "wasm32" ) ) ]
3435use std:: time:: Duration ;
3536use tracing:: { debug, info} ;
36- #[ cfg( not( target_arch = "wasm32" ) ) ]
3737use url:: Url ;
3838
3939/// A trusted HTTP-based context provider that fetches quorum information
@@ -65,6 +65,20 @@ pub struct TrustedHttpContextProvider {
6565 refetch_if_not_found : bool ,
6666}
6767
68+ #[ derive( Debug , Deserialize ) ]
69+ struct MasternodeEntry {
70+ address : String ,
71+ status : String ,
72+ #[ serde( rename = "versionCheck" ) ]
73+ version_check : Option < String > ,
74+ }
75+
76+ #[ derive( Debug , Deserialize ) ]
77+ struct MasternodeDiscoveryResponse {
78+ success : bool ,
79+ data : Vec < MasternodeEntry > ,
80+ }
81+
6882impl TrustedHttpContextProvider {
6983 /// Verify that a URL's domain resolves
7084 #[ cfg( not( target_arch = "wasm32" ) ) ]
@@ -229,6 +243,70 @@ impl TrustedHttpContextProvider {
229243 current_count + previous_count
230244 }
231245
246+ /// Fetch DAPI HTTPS addresses from the masternode discovery endpoint
247+ pub async fn fetch_masternode_addresses (
248+ & self ,
249+ ) -> Result < Vec < Url > , TrustedContextProviderError > {
250+ let url = format ! ( "{}/masternodes" , self . base_url) ;
251+ debug ! (
252+ "Fetching masternode addresses from trusted resource: {}" ,
253+ url
254+ ) ;
255+
256+ let response = self . client . get ( & url) . send ( ) . await ?;
257+ if !response. status ( ) . is_success ( ) {
258+ return Err ( TrustedContextProviderError :: NetworkError ( format ! (
259+ "HTTP {} from {}" ,
260+ response. status( ) ,
261+ url
262+ ) ) ) ;
263+ }
264+
265+ let body = response. text ( ) . await ?;
266+ let parsed: MasternodeDiscoveryResponse = serde_json:: from_str ( & body) ?;
267+
268+ if !parsed. success {
269+ return Err ( TrustedContextProviderError :: NetworkError (
270+ "Masternode discovery response indicated failure" . to_string ( ) ,
271+ ) ) ;
272+ }
273+
274+ let dapi_port = match self . network {
275+ Network :: Dash => 443 ,
276+ Network :: Testnet => 1443 ,
277+ _ => 443 ,
278+ } ;
279+
280+ let mut addresses = Vec :: new ( ) ;
281+ for entry in parsed
282+ . data
283+ . into_iter ( )
284+ . filter ( |m| m. status == "ENABLED" && m. version_check . as_deref ( ) == Some ( "success" ) )
285+ {
286+ let host_port = entry. address ;
287+ let host = host_port
288+ . rsplit_once ( ':' )
289+ . map ( |( h, _) | h)
290+ . unwrap_or ( host_port. as_str ( ) ) ;
291+ let https_url = format ! ( "https://{}:{}" , host, dapi_port) ;
292+ let url = url:: Url :: parse ( & https_url) . map_err ( |e| {
293+ TrustedContextProviderError :: NetworkError ( format ! (
294+ "Invalid masternode URL '{}': {}" ,
295+ https_url, e
296+ ) )
297+ } ) ?;
298+ addresses. push ( url) ;
299+ }
300+
301+ if addresses. is_empty ( ) {
302+ return Err ( TrustedContextProviderError :: NetworkError (
303+ "No eligible masternode addresses discovered" . to_string ( ) ,
304+ ) ) ;
305+ }
306+
307+ Ok ( addresses)
308+ }
309+
232310 /// Fetch current quorums from the HTTP endpoint
233311 pub async fn fetch_current_quorums (
234312 & self ,
0 commit comments