diff --git a/client/src/bin/spaced.rs b/client/src/bin/spaced.rs index 1fa96c6..0abe704 100644 --- a/client/src/bin/spaced.rs +++ b/client/src/bin/spaced.rs @@ -129,7 +129,8 @@ impl Composer { } async fn run(&mut self) -> anyhow::Result<()> { - let spaced = Args::configure().await?; + let shutdown_receiver = self.shutdown.subscribe(); + let spaced = Args::configure(shutdown_receiver).await?; self.setup_rpc_services(&spaced).await; self.setup_sync_service(spaced).await; diff --git a/client/src/config.rs b/client/src/config.rs index 77be6f8..5bda884 100644 --- a/client/src/config.rs +++ b/client/src/config.rs @@ -88,7 +88,6 @@ pub struct Args { #[serde(rename_all = "lowercase")] pub enum ExtendedNetwork { Mainnet, - MainnetAlpha, Testnet, Testnet4, Signet, @@ -98,7 +97,7 @@ pub enum ExtendedNetwork { impl ExtendedNetwork { pub fn fallback_network(&self) -> Network { match self { - ExtendedNetwork::Mainnet | ExtendedNetwork::MainnetAlpha => Network::Bitcoin, + ExtendedNetwork::Mainnet => Network::Bitcoin, ExtendedNetwork::Testnet => Network::Testnet, ExtendedNetwork::Signet => Network::Signet, ExtendedNetwork::Regtest => Network::Regtest, @@ -110,7 +109,7 @@ impl ExtendedNetwork { impl Args { /// Configures spaced node by processing command line arguments /// and configuration files - pub async fn configure() -> anyhow::Result { + pub async fn configure(shutdown: tokio::sync::broadcast::Receiver<()>) -> anyhow::Result { let mut args = Args::merge_args_config(None); let default_dirs = get_default_node_dirs(); @@ -170,7 +169,7 @@ impl Args { !args.bitcoin_rpc_light ); - let genesis = Spaced::genesis(&rpc, args.chain).await?; + let genesis = Spaced::genesis(&rpc, args.chain, shutdown).await?; fs::create_dir_all(data_dir.clone())?; @@ -282,7 +281,7 @@ pub fn safe_exit(code: i32) -> ! { pub fn default_bitcoin_rpc_url(network: &ExtendedNetwork) -> &'static str { match network { - ExtendedNetwork::Mainnet | ExtendedNetwork::MainnetAlpha => "http://127.0.0.1:8332", + ExtendedNetwork::Mainnet => "http://127.0.0.1:8332", ExtendedNetwork::Testnet4 => "http://127.0.0.1:48332", ExtendedNetwork::Signet => "http://127.0.0.1:38332", ExtendedNetwork::Testnet => "http://127.0.0.1:18332", @@ -380,7 +379,6 @@ impl Display for ExtendedNetwork { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let str = match self { ExtendedNetwork::Mainnet => "mainnet".to_string(), - ExtendedNetwork::MainnetAlpha => "mainnet-alpha".to_string(), ExtendedNetwork::Testnet => "testnet".to_string(), ExtendedNetwork::Testnet4 => "testnet4".to_string(), ExtendedNetwork::Signet => "signet".to_string(), @@ -393,7 +391,6 @@ impl Display for ExtendedNetwork { pub fn default_spaces_rpc_port(chain: &ExtendedNetwork) -> u16 { match chain { ExtendedNetwork::Mainnet => 7225, - ExtendedNetwork::MainnetAlpha => 7225, ExtendedNetwork::Testnet4 => 7224, ExtendedNetwork::Testnet => 7223, ExtendedNetwork::Signet => 7221, diff --git a/client/src/source.rs b/client/src/source.rs index cbabe44..228d408 100644 --- a/client/src/source.rs +++ b/client/src/source.rs @@ -896,15 +896,22 @@ impl BlockSource for BitcoinBlockSource { #[serde(rename = "bestblockhash")] pub best_block_hash: BlockHash, } - let info: Info = self + let mut info: Info = self .rpc .send_json_blocking(&self.client, &self.rpc.get_blockchain_info())?; + // TODO: update this check once testnet4 is part of the [Network] type. + // use network names from bitcoin core + // https://github.com/bitcoin/bitcoin/blob/master/src/util/chaintype.cpp let expected_chain = match expected_chain { Network::Bitcoin => "main", Network::Regtest => "regtest", + Network::Signet => "signet", _ => "test" }; + if info.chain.starts_with("test") { + info.chain = "test".to_string() + } if info.chain != expected_chain { warn!("Invalid chain from connected rpc node - expected {}, got {}", expected_chain, info.chain); return Ok(None); diff --git a/client/src/sync.rs b/client/src/sync.rs index 0d05a5d..9afd773 100644 --- a/client/src/sync.rs +++ b/client/src/sync.rs @@ -257,34 +257,83 @@ impl Spaced { pub async fn genesis( rpc: &BitcoinRpc, network: ExtendedNetwork, + mut shutdown: tokio::sync::broadcast::Receiver<()> ) -> anyhow::Result { let mut anchor = match network { ExtendedNetwork::Testnet => ChainAnchor::TESTNET(), ExtendedNetwork::Testnet4 => ChainAnchor::TESTNET4(), ExtendedNetwork::Regtest => ChainAnchor::REGTEST(), ExtendedNetwork::Mainnet => ChainAnchor::MAINNET(), - ExtendedNetwork::MainnetAlpha => ChainAnchor::MAINNET_ALPHA(), _ => panic!("unsupported network"), }; - if anchor.hash == BlockHash::all_zeros() { - let client = reqwest::Client::new(); - anchor.hash = match rpc - .send_json(&client, &rpc.get_block_hash(anchor.height)) - .await - { - Ok(hash) => hash, - Err(e) => { - return Err(anyhow!( - "Could not retrieve activation block at height {}: {}", - anchor.height, - e + + // Wait for the RPC node to be ready + let mut attempts = 0; + let mut last_error = BitcoinRpcError::Other("Unknown error".to_string()); + loop { + if shutdown.try_recv().is_ok() { + return Err(anyhow!("Fetching activation height terminated: shutdown requested")) + } + if attempts > 5 { + return Err(anyhow!( + "Could not retrieve activation height: {}", + last_error )); + } + + let rpc_task = rpc.clone(); + let net_task = network.fallback_network(); + let best_chain = tokio::task::spawn_blocking(move || { + let source = BitcoinBlockSource::new(rpc_task); + source.get_best_chain(Some(anchor.height), net_task) + }).await.expect("join"); + + match best_chain { + Ok(Some(tip)) => { + info!("Connect to RPC node (tip: {})", tip.height); + if anchor.hash != BlockHash::all_zeros() { + break; + } + + // Pull the activation block hash + let client = reqwest::Client::new(); + anchor.hash = match rpc + .send_json(&client, &rpc.get_block_hash(anchor.height)) + .await + { + Ok(hash) => hash, + Err(e) => { + warn!("Fetching height {}:{}, retrying in 1s ...", anchor.height, e); + last_error = e; + match &last_error { + BitcoinRpcError::Rpc(_) => {} + _ => attempts += 1, + } + continue; + } + }; + + break; + } + Ok(None) => { + warn!("Connected RPC node is still syncing, waiting 5s ..."); + tokio::time::sleep(Duration::from_secs(5)).await; + } + Err(e) => { + warn!("Error fetching blockchain info: {}, retrying in 1s ...", e); + last_error = e; + tokio::time::sleep(Duration::from_secs(1)).await; + match &last_error { + BitcoinRpcError::Rpc(_) => {} + _ => attempts += 1, + } } } } + Ok(anchor) } } diff --git a/protocol/src/constants.rs b/protocol/src/constants.rs index 9223dd6..4dd330e 100644 --- a/protocol/src/constants.rs +++ b/protocol/src/constants.rs @@ -64,11 +64,6 @@ impl ChainAnchor { height: 871_222, }; - pub const MAINNET_ALPHA: fn() -> Self = || ChainAnchor { - hash: BlockHash::all_zeros(), - height: 870_000, - }; - // Testnet4 activation block pub const TESTNET4: fn() -> Self = || Self { hash: BlockHash::all_zeros(),