11//! ION-related utilities.
2+ use crate :: data:: { sample_did, SAMPLE_CID } ;
23use crate :: data:: {
34 TESTNET3_TEST_ROOT_PLUS_1_SIGNING_KEY , TESTNET3_TEST_ROOT_PLUS_2_SIGNING_KEYS ,
45 TESTNET4_TEST_ROOT_PLUS_1_SIGNING_KEY , TESTNET4_TEST_ROOT_PLUS_2_SIGNING_KEYS ,
56} ;
67use crate :: {
78 config:: ion_config, MONGO_FILTER_OP_INDEX , MONGO_FILTER_TXN_NUMBER , MONGO_FILTER_TXN_TIME ,
89} ;
9- use bitcoin:: Network ;
10- use bitcoin :: { block :: Header , blockdata :: block :: BlockHash , Transaction } ;
10+ use bitcoin:: { block :: Header , blockdata :: block :: BlockHash , Network , Transaction } ;
11+ use bitcoincore_rpc :: json :: GetBlockchainInfoResult ;
1112use bitcoincore_rpc:: { bitcoincore_rpc_json:: BlockStatsFields , RpcApi } ;
1213use chrono:: NaiveDate ;
1314use flate2:: read:: GzDecoder ;
@@ -23,13 +24,15 @@ use std::path::Path;
2324use std:: sync:: Once ;
2425use std:: { cmp:: Ordering , collections:: HashMap } ;
2526use trustchain_core:: key_manager:: { KeyManager , KeyType } ;
27+ use trustchain_core:: resolver:: TrustchainResolver ;
2628use trustchain_core:: TRUSTCHAIN_DATA ;
2729use trustchain_core:: { utils:: get_did_suffix, verifier:: VerifierError } ;
2830
2931use crate :: {
30- TrustchainBitcoinError , TrustchainIpfsError , TrustchainMongodbError , BITS_KEY ,
31- HASH_PREV_BLOCK_KEY , MERKLE_ROOT_KEY , MONGO_COLLECTION_OPERATIONS , MONGO_CREATE_OPERATION ,
32- MONGO_FILTER_DID_SUFFIX , MONGO_FILTER_TYPE , NONCE_KEY , TIMESTAMP_KEY , VERSION_KEY ,
32+ trustchain_resolver, TrustchainBitcoinError , TrustchainIpfsError , TrustchainMongodbError ,
33+ BITS_KEY , HASH_PREV_BLOCK_KEY , MERKLE_ROOT_KEY , MONGO_COLLECTION_OPERATIONS ,
34+ MONGO_CREATE_OPERATION , MONGO_FILTER_DID_SUFFIX , MONGO_FILTER_TYPE , NONCE_KEY , TIMESTAMP_KEY ,
35+ VERSION_KEY ,
3336} ;
3437
3538const ION_METHOD_WITH_DELIMITER : & str = "ion:" ;
@@ -286,16 +289,23 @@ pub fn rpc_client() -> bitcoincore_rpc::Client {
286289 . unwrap ( )
287290}
288291
289- /// Gets the Bitcoin chain via the RPC API.
290- pub fn bitcoin_network (
292+ /// Gets Bitcoin blockchain info via the RPC API.
293+ pub fn blockchain_info (
291294 client : Option < & bitcoincore_rpc:: Client > ,
292- ) -> Result < Network , TrustchainBitcoinError > {
295+ ) -> Result < GetBlockchainInfoResult , TrustchainBitcoinError > {
293296 // If necessary, construct a Bitcoin RPC client to communicate with the ION Bitcoin node.
294297 if client. is_none ( ) {
295298 let rpc_client = rpc_client ( ) ;
296- return bitcoin_network ( Some ( & rpc_client) ) ;
299+ return blockchain_info ( Some ( & rpc_client) ) ;
297300 } ;
298- Ok ( client. unwrap ( ) . get_blockchain_info ( ) ?. chain )
301+ Ok ( client. unwrap ( ) . get_blockchain_info ( ) ?)
302+ }
303+
304+ /// Gets the Bitcoin chain via the RPC API.
305+ pub fn bitcoin_network (
306+ client : Option < & bitcoincore_rpc:: Client > ,
307+ ) -> Result < Network , TrustchainBitcoinError > {
308+ Ok ( blockchain_info ( client) ?. chain )
299309}
300310
301311/// Gets a Bitcoin block header via the RPC API.
@@ -522,6 +532,76 @@ pub fn block_height_range_on_date(
522532 Ok ( ( first_block, last_block) )
523533}
524534
535+ #[ derive( Debug ) ]
536+ pub enum BitcoindStatus {
537+ Ok ( Network ) ,
538+ Synching ( u64 , u64 ) ,
539+ UnexpectedNetwork ( Network ) ,
540+ UnsupportedNetwork ( Network ) ,
541+ Error ( TrustchainBitcoinError ) ,
542+ }
543+
544+ /// Returns the current status of bitcoind.
545+ pub async fn bitcoind_status ( ) -> BitcoindStatus {
546+ let info = blockchain_info ( None ) ;
547+ if info. is_err ( ) {
548+ return BitcoindStatus :: Error ( info. err ( ) . unwrap ( ) ) ;
549+ }
550+ let info = info. unwrap ( ) ;
551+ if info. blocks != info. headers {
552+ return BitcoindStatus :: Synching ( info. blocks , info. headers ) ;
553+ }
554+ let ion_core_config = & ion_config ( ) . mongo_database_ion_core ;
555+ match info. chain {
556+ Network :: Bitcoin => {
557+ if ion_core_config. contains ( "testnet" ) {
558+ return BitcoindStatus :: UnexpectedNetwork ( info. chain ) ;
559+ }
560+ BitcoindStatus :: Ok ( Network :: Bitcoin )
561+ }
562+ Network :: Testnet => {
563+ if ion_core_config. contains ( "mainnet" ) {
564+ return BitcoindStatus :: UnexpectedNetwork ( info. chain ) ;
565+ }
566+ BitcoindStatus :: Ok ( Network :: Testnet )
567+ }
568+ Network :: Testnet4 => {
569+ if ion_core_config. contains ( "mainnet" ) {
570+ return BitcoindStatus :: UnexpectedNetwork ( info. chain ) ;
571+ }
572+ BitcoindStatus :: Ok ( Network :: Testnet4 )
573+ }
574+ _ => BitcoindStatus :: UnsupportedNetwork ( info. chain ) ,
575+ }
576+ }
577+
578+ /// Returns true if the IPFS daemon is running on the expected port.
579+ pub async fn ipfs_ok ( ) -> bool {
580+ query_ipfs ( SAMPLE_CID , & IpfsClient :: default ( ) ) . await . is_ok ( )
581+ }
582+
583+ /// Returns true if the MongoDB daemon is running on the expected port.
584+ pub async fn mongodb_ok ( network : & Network ) -> bool {
585+ if let Ok ( sample_did) = sample_did ( network) {
586+ query_mongodb ( get_did_suffix ( & sample_did) ) . await . is_ok ( )
587+ } else {
588+ // If the given Bitcoin network is unsupported, return false.
589+ false
590+ }
591+ }
592+
593+ /// Returns true if the ION Core microservice is running on the expected port.
594+ pub async fn ion_ok ( network : & Network , ion_port : u16 ) -> bool {
595+ let resolver = trustchain_resolver ( & format ! ( "http://localhost:{}/" , ion_port) ) ;
596+ if let Ok ( sample_did) = sample_did ( network) {
597+ let result = resolver. resolve_as_result ( & sample_did) . await ;
598+ result. is_ok ( )
599+ } else {
600+ // If the given Bitcoin network is unsupported, return false.
601+ false
602+ }
603+ }
604+
525605#[ cfg( test) ]
526606mod tests {
527607 use super :: * ;
@@ -1104,4 +1184,10 @@ mod tests {
11041184 }
11051185 }
11061186 }
1187+
1188+ #[ tokio:: test]
1189+ #[ ignore = "Integration test requires Bitcoin" ]
1190+ async fn test_bitcoind_status ( ) {
1191+ let _ = bitcoind_status ( ) . await ;
1192+ }
11071193}
0 commit comments