@@ -12,15 +12,15 @@ use std::{
1212use base64:: Engine ;
1313use bitcoin:: { Block , BlockHash , Txid } ;
1414use hex:: FromHexError ;
15- use log:: error;
15+ use log:: { error, warn } ;
1616use reqwest:: StatusCode ;
1717use serde:: { de:: DeserializeOwned , Deserialize , Serialize } ;
1818use serde_json:: Value ;
1919use spaces_protocol:: constants:: ChainAnchor ;
2020use spaces_wallet:: { bitcoin, bitcoin:: Transaction } ;
2121use threadpool:: ThreadPool ;
2222use tokio:: time:: Instant ;
23-
23+ use spaces_protocol :: bitcoin :: Network ;
2424use crate :: { client:: BlockSource , std_wait} ;
2525
2626const BITCOIN_RPC_IN_WARMUP : i32 = -28 ; // Client still warming up
@@ -34,9 +34,11 @@ pub struct BitcoinRpc {
3434 id : Arc < AtomicU64 > ,
3535 auth_token : Option < String > ,
3636 url : String ,
37+ legacy : bool
3738}
3839
3940pub struct BlockFetcher {
41+ chain : Network ,
4042 src : BitcoinBlockSource ,
4143 job_id : Arc < AtomicUsize > ,
4244 sender : std:: sync:: mpsc:: SyncSender < BlockEvent > ,
@@ -121,18 +123,23 @@ trait ErrorForRpcBlocking {
121123}
122124
123125impl BitcoinRpc {
124- pub fn new ( url : & str , auth : BitcoinRpcAuth ) -> Self {
126+ pub fn new ( url : & str , auth : BitcoinRpcAuth , legacy : bool ) -> Self {
125127 Self {
126128 id : Default :: default ( ) ,
127129 auth_token : auth. to_token ( ) ,
128130 url : url. to_string ( ) ,
131+ legacy,
129132 }
130133 }
131134
132- pub fn make_request ( & self , method : & str , params : serde_json :: Value ) -> BitcoinRpcRequest {
135+ pub fn make_request ( & self , method : & str , params : Value ) -> BitcoinRpcRequest {
133136 let id = self . id . fetch_add ( 1 , Ordering :: Relaxed ) ;
134137 let body = serde_json:: json!( {
135- "jsonrpc" : "1.0" ,
138+ "jsonrpc" : if self . legacy {
139+ "1.0"
140+ } else {
141+ "2.0"
142+ } ,
136143 "id" : id. to_string( ) ,
137144 "method" : method,
138145 "params" : params,
@@ -381,12 +388,14 @@ impl BitcoinRpcAuth {
381388
382389impl BlockFetcher {
383390 pub fn new (
391+ chain : Network ,
384392 src : BitcoinBlockSource ,
385393 num_workers : usize ,
386394 ) -> ( Self , std:: sync:: mpsc:: Receiver < BlockEvent > ) {
387395 let ( tx, rx) = std:: sync:: mpsc:: sync_channel ( 12 ) ;
388396 (
389397 Self {
398+ chain,
390399 src,
391400 job_id : Arc :: new ( AtomicUsize :: new ( 0 ) ) ,
392401 sender : tx,
@@ -401,10 +410,15 @@ impl BlockFetcher {
401410 }
402411
403412 fn should_sync (
413+ expected_chain : Network ,
404414 source : & BitcoinBlockSource ,
405415 start : ChainAnchor ,
406416 ) -> Result < Option < ChainAnchor > , BlockFetchError > {
407- let tip = source. get_best_chain ( ) ?;
417+ let tip = match source. get_best_chain ( Some ( start. height ) , expected_chain) ? {
418+ Some ( tip) => tip,
419+ None => return Ok ( None ) ,
420+ } ;
421+
408422 if start. height > tip. height {
409423 return Err ( BlockFetchError :: BlockMismatch ) ;
410424 }
@@ -437,6 +451,7 @@ impl BlockFetcher {
437451 let current_task = self . job_id . clone ( ) ;
438452 let task_sender = self . sender . clone ( ) ;
439453 let num_workers = self . num_workers ;
454+ let chain = self . chain ;
440455
441456 _ = std:: thread:: spawn ( move || {
442457 let mut last_check = Instant :: now ( ) - Duration :: from_secs ( 2 ) ;
@@ -451,7 +466,7 @@ impl BlockFetcher {
451466 }
452467 last_check = Instant :: now ( ) ;
453468
454- let tip = match BlockFetcher :: should_sync ( & task_src, checkpoint) {
469+ let tip = match BlockFetcher :: should_sync ( chain , & task_src, checkpoint) {
455470 Ok ( t) => t,
456471 Err ( e) => {
457472 _ = task_sender. send ( BlockEvent :: Error ( e) ) ;
@@ -872,21 +887,48 @@ impl BlockSource for BitcoinBlockSource {
872887 . send_json_blocking ( & self . client , & self . rpc . get_block_count ( ) ) ?)
873888 }
874889
875- fn get_best_chain ( & self ) -> Result < ChainAnchor , BitcoinRpcError > {
890+ fn get_best_chain ( & self , tip : Option < u32 > , expected_chain : Network ) -> Result < Option < ChainAnchor > , BitcoinRpcError > {
876891 #[ derive( Deserialize ) ]
877892 struct Info {
878- #[ serde( rename = "blocks" ) ]
879- height : u64 ,
893+ pub chain : String ,
894+ pub blocks : u32 ,
895+ pub headers : u32 ,
880896 #[ serde( rename = "bestblockhash" ) ]
881- hash : BlockHash ,
897+ pub best_block_hash : BlockHash ,
882898 }
883899 let info: Info = self
884900 . rpc
885901 . send_json_blocking ( & self . client , & self . rpc . get_blockchain_info ( ) ) ?;
886902
887- Ok ( ChainAnchor {
888- hash : info. hash ,
889- height : info. height as _ ,
890- } )
903+ let expected_chain = match expected_chain {
904+ Network :: Bitcoin => "main" ,
905+ Network :: Regtest => "regtest" ,
906+ _ => "test"
907+ } ;
908+ if info. chain != expected_chain {
909+ warn ! ( "Invalid chain from connected rpc node - expected {}, got {}" , expected_chain, info. chain) ;
910+ return Ok ( None ) ;
911+ }
912+
913+ let synced = info. headers == info. blocks ;
914+ let best_chain = if !synced {
915+ let block_hash = self . get_block_hash ( info. blocks ) ?;
916+ ChainAnchor {
917+ hash : block_hash,
918+ height : info. blocks ,
919+ }
920+ } else {
921+ ChainAnchor {
922+ hash : info. best_block_hash ,
923+ height : info. headers ,
924+ }
925+ } ;
926+
927+ // If the source is still syncing, and we have a higher tip, wait.
928+ if !synced && tip. is_some_and ( |tip| tip > info. blocks ) {
929+ return Ok ( None ) ;
930+ }
931+
932+ Ok ( Some ( best_chain) )
891933 }
892934}
0 commit comments