Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ jobs:
workspaces: ". -> target/dev"
- name: tmpfs headroom
run: df -h /dev/shm && free -m
- name: rbitcoin-node signet datadir regressions
run: |
cargo test -p rbitcoin-node custom_signet_datadir_appends_challenge -- --nocapture --test-threads=1
cargo test -p rbitcoin-node default_signet_datadir_uses_builtin_challenge -- --nocapture --test-threads=1
- name: cargo test
run: cargo test --workspace

Expand Down
2 changes: 1 addition & 1 deletion crates/rbitcoin-node/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ where
"rbitcoin-node {} on {} datadir={}",
env!("CARGO_PKG_VERSION"),
handle.network_name(),
handle.config.datadir.path().display()
handle.config.datadir_path().display()
);
if std::env::var_os("RBITCOIN_TEST_DROP_STORE").is_some() {
let _ = std::fs::remove_dir_all(handle.config.store_path());
Expand Down
44 changes: 35 additions & 9 deletions crates/rbitcoin-node/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::error::NodeError;
use bitcoin::hex::FromHex;
use bitcoin::hex::{DisplayHex, FromHex};
use bitcoin::ScriptBuf;
use rbitcoin_consensus::{default_milestone_anchor, ChainParams, Milestone};
use rbitcoin_esplora::EsploraListen;
Expand Down Expand Up @@ -476,6 +476,32 @@ impl NodeConfig {
self
}

/// Datadir resolved for the active chain.
///
/// Custom signet keeps its own `signet/<challenge>` tree so peers and
/// store state do not collide with other signet challenges. Plain signet
/// uses the network default challenge.
pub fn datadir_path(&self) -> PathBuf {
let root = self.datadir.path();
if self.network != Network::Signet {
return root.to_path_buf();
}
let challenge = self.signet_challenge.as_ref().map_or_else(
|| {
rbitcoin_consensus::ChainParams::signet()
.signet_challenge
.expect("default signet challenge")
},
Clone::clone,
);
let challenge = challenge.as_bytes().to_lower_hex_string();
let custom = Path::new("signet").join(&challenge);
if root.ends_with(&custom) {
return root.to_path_buf();
}
root.join("signet").join(challenge)
}

pub fn with_p2p_listen(mut self, addr: SocketAddr) -> Self {
self.listen.p2p = P2pListen::Socket(addr);
self
Expand All @@ -488,7 +514,7 @@ impl NodeConfig {
}

pub fn store_path(&self) -> PathBuf {
self.datadir.path().join("store")
self.datadir_path().join("store")
}

/// Cold store directory (`{datadir-cold}/store`) when `--datadir-cold` is set.
Expand All @@ -509,7 +535,7 @@ impl NodeConfig {

/// Durable mempool directory (`{datadir}/mempool/`).
pub fn mempool_path(&self) -> PathBuf {
self.datadir.path().join("mempool")
self.datadir_path().join("mempool")
}

pub fn milestone(&self) -> Milestone {
Expand Down Expand Up @@ -758,7 +784,7 @@ impl NodeConfig {
self.rpc
.token_file
.clone()
.unwrap_or_else(|| rbitcoin_rpc::default_token_path(self.datadir.path()))
.unwrap_or_else(|| rbitcoin_rpc::default_token_path(&self.datadir_path()))
}

/// Configured Core cookie file for TCP HTTP Basic authentication.
Expand All @@ -771,7 +797,7 @@ impl NodeConfig {
self.rpc
.socket_path
.clone()
.unwrap_or_else(|| rbitcoin_rpc::default_socket_path(self.datadir.path()))
.unwrap_or_else(|| rbitcoin_rpc::default_socket_path(&self.datadir_path()))
}

/// Owner-only mode for a directory this process just created.
Expand All @@ -796,14 +822,14 @@ impl NodeConfig {
/// Create `{datadir}` and standard subdirs (`store`, `mempool`) if missing.
pub fn ensure_datadir(&self) -> Result<(), NodeError> {
self.validate()?;
let root = self.datadir.path();
let root = self.datadir_path();
let created_root = !root.exists();
std::fs::create_dir_all(root).map_err(|source| NodeError::Datadir {
std::fs::create_dir_all(&root).map_err(|source| NodeError::Datadir {
path: self.datadir.path.clone(),
source,
})?;
if created_root {
Self::restrict_new_dir(root)?;
Self::restrict_new_dir(&root)?;
}
if root.exists() && !root.is_dir() {
return Err(NodeError::Config(format!(
Expand Down Expand Up @@ -840,7 +866,7 @@ impl NodeConfig {
}
}
if created_root {
rbitcoin_log::info!("node: created datadir {}", self.datadir.path().display());
rbitcoin_log::info!("node: created datadir {}", self.datadir_path().display());
}
Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion crates/rbitcoin-node/src/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub fn lock_busy_msg(dir: &Path) -> String {
/// Exclusive-lock the process datadir.
pub fn lock_node_dirs(config: &NodeConfig) -> Result<DirLocks, NodeError> {
Ok(DirLocks {
_files: vec![lock_dir(config.datadir.path())?],
_files: vec![lock_dir(&config.datadir_path())?],
})
}

Expand Down
72 changes: 57 additions & 15 deletions crates/rbitcoin-node/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ pub async fn run_p2p(config: NodeConfig) -> Result<(), NodeError> {
"rbitcoin-node starting version={} network={} datadir={}{} tip={start_tip} io={}",
env!("CARGO_PKG_VERSION"),
config.network.as_str(),
config.datadir.path().display(),
config.datadir_path().display(),
config
.datadir
.cold
Expand Down Expand Up @@ -462,7 +462,7 @@ pub async fn run_p2p(config: NodeConfig) -> Result<(), NodeError> {
.expect("validate requires --tor-control with --listen-onion");
let virt = config.network.default_p2p_port();
let hs = ctl
.add_p2p_onion(config.datadir.path(), node.local_addr, virt)
.add_p2p_onion(&config.datadir_path(), node.local_addr, virt)
.await?;
info!("p2p onion {}.onion:{}", hs.service_id, virt);
node.peers
Expand All @@ -473,7 +473,7 @@ pub async fn run_p2p(config: NodeConfig) -> Result<(), NodeError> {
}
let mut i2p_sam = if let Some(addr) = config.listen.i2p_sam {
let s = if config.listen.i2p_accept_incoming {
let dest = config.datadir.path().join("i2p").join("p2p.priv");
let dest = config.datadir_path().join("i2p").join("p2p.priv");
rbitcoin_net::I2pSam::connect_persistent(addr, &dest).await
} else {
rbitcoin_net::I2pSam::connect(addr).await
Expand Down Expand Up @@ -516,7 +516,7 @@ pub async fn run_p2p(config: NodeConfig) -> Result<(), NodeError> {
} else {
None
};
let peers_path = config.datadir.path().join("peers");
let peers_path = config.datadir_path().join("peers");
let mut addrman = match AddrMan::load(&peers_path) {
Ok(am) => {
if !am.is_empty() {
Expand All @@ -536,7 +536,7 @@ pub async fn run_p2p(config: NodeConfig) -> Result<(), NodeError> {
AddrMan::new()
}
};
let asmap = load_asmap(config.datadir.path(), config.asmap.as_deref());
let asmap = load_asmap(&config.datadir_path(), config.asmap.as_deref());
addrman.set_asmap(asmap.clone());
addrman.set_only_net(config.listen.only_net.clone());
addrman.set_cjdns_reachable(config.listen.cjdns_reachable);
Expand Down Expand Up @@ -574,7 +574,7 @@ pub async fn run_p2p(config: NodeConfig) -> Result<(), NodeError> {
&& !config.listen.has_pinned_connect()
&& addrman.is_empty()
{
warn!("custom signet has no peers; use --connect ADDR or reuse a datadir with known peers");
info!("custom signet has no peers yet; keep listening or add --connect ADDR");
}
let shared_peers = std::sync::Arc::new(std::sync::Mutex::new(addrman.clone()));
node.peers.set_addrman(std::sync::Arc::clone(&shared_peers));
Expand Down Expand Up @@ -808,7 +808,7 @@ pub async fn run_p2p(config: NodeConfig) -> Result<(), NodeError> {
.await;
if let (Some(ctl), Some(h)) = (tor_ctl.as_mut(), electrum_handles.first()) {
let hs = ctl
.add_electrum_onion(config.datadir.path(), h.local_addr)
.add_electrum_onion(&config.datadir_path(), h.local_addr)
.await?;
info!(
"electrum onion {}.onion:{}",
Expand All @@ -832,7 +832,7 @@ pub async fn run_p2p(config: NodeConfig) -> Result<(), NodeError> {
if config.esplora_onion {
if let (Some(ctl), Some(h)) = (tor_ctl.as_mut(), esplora_handles.first()) {
let hs = ctl
.add_esplora_onion(config.datadir.path(), h.local_addr)
.add_esplora_onion(&config.datadir_path(), h.local_addr)
.await?;
info!(
"esplora onion http://{}.onion:{} (/ws same port)",
Expand All @@ -851,7 +851,7 @@ pub async fn run_p2p(config: NodeConfig) -> Result<(), NodeError> {
i2p_wallet.push(
start_i2p_named_forward(
addr,
config.datadir.path(),
&config.datadir_path(),
"electrum",
h.local_addr.port(),
)
Expand All @@ -862,7 +862,7 @@ pub async fn run_p2p(config: NodeConfig) -> Result<(), NodeError> {
i2p_wallet.push(
start_i2p_named_forward(
addr,
config.datadir.path(),
&config.datadir_path(),
"esplora",
h.local_addr.port(),
)
Expand Down Expand Up @@ -938,7 +938,10 @@ pub async fn run_p2p(config: NodeConfig) -> Result<(), NodeError> {
}
}

if tip_follow_ready && config.max_run_secs != Some(0) && !shutdown.requested() {
if (tip_follow_ready || config.network == Network::Signet)
&& config.max_run_secs != Some(0)
&& !shutdown.requested()
{
let unbound = [
(
"rpc",
Expand Down Expand Up @@ -1017,7 +1020,7 @@ pub async fn run_p2p(config: NodeConfig) -> Result<(), NodeError> {
.await;
if let (Some(ctl), Some(h)) = (tor_ctl.as_mut(), handles.first()) {
let hs = ctl
.add_electrum_onion(config.datadir.path(), h.local_addr)
.add_electrum_onion(&config.datadir_path(), h.local_addr)
.await?;
info!(
"electrum onion {}.onion:{}",
Expand All @@ -1037,7 +1040,7 @@ pub async fn run_p2p(config: NodeConfig) -> Result<(), NodeError> {
i2p_wallet.push(
start_i2p_named_forward(
addr,
config.datadir.path(),
&config.datadir_path(),
"electrum",
h.local_addr.port(),
)
Expand All @@ -1063,7 +1066,7 @@ pub async fn run_p2p(config: NodeConfig) -> Result<(), NodeError> {
if config.esplora_onion {
if let (Some(ctl), Some(h)) = (tor_ctl.as_mut(), handles.first()) {
let hs = ctl
.add_esplora_onion(config.datadir.path(), h.local_addr)
.add_esplora_onion(&config.datadir_path(), h.local_addr)
.await?;
info!(
"esplora onion http://{}.onion:{} (/ws same port)",
Expand All @@ -1082,7 +1085,7 @@ pub async fn run_p2p(config: NodeConfig) -> Result<(), NodeError> {
i2p_wallet.push(
start_i2p_named_forward(
addr,
config.datadir.path(),
&config.datadir_path(),
"esplora",
h.local_addr.port(),
)
Expand Down Expand Up @@ -2253,6 +2256,7 @@ fn resolve_seednode(raw: &str, network: Network) -> Result<SocketAddr, String> {
#[cfg(test)]
mod tests {
use super::*;
use bitcoin::hex::DisplayHex;
use std::time::{SystemTime, UNIX_EPOCH};
fn tiny_regtest(dir: impl AsRef<std::path::Path>) -> NodeConfig {
NodeConfig::default()
Expand Down Expand Up @@ -2701,6 +2705,44 @@ mod tests {
let _ = std::fs::remove_dir_all(&dir);
}

#[test]
fn custom_signet_datadir_appends_challenge() {
let mut cfg = NodeConfig::default()
.with_datadir("/tmp/rbitcoin-custom-signet")
.with_network(rbitcoin_primitives::Network::Signet);
cfg.signet_challenge = Some(bitcoin::ScriptBuf::from_bytes(vec![0x51]));
assert_eq!(
cfg.datadir_path(),
std::path::Path::new("/tmp/rbitcoin-custom-signet/signet/51")
);

let already = NodeConfig::default()
.with_datadir("/tmp/rbitcoin-custom-signet/signet/51")
.with_network(rbitcoin_primitives::Network::Signet);
let mut already = already;
already.signet_challenge = Some(bitcoin::ScriptBuf::from_bytes(vec![0x51]));
assert_eq!(
already.datadir_path(),
std::path::Path::new("/tmp/rbitcoin-custom-signet/signet/51")
);
}

#[test]
fn default_signet_datadir_uses_builtin_challenge() {
let cfg = NodeConfig::default()
.with_datadir("/tmp/rbitcoin-default-signet")
.with_network(rbitcoin_primitives::Network::Signet);
let challenge = rbitcoin_consensus::ChainParams::signet()
.signet_challenge
.unwrap()
.as_bytes()
.to_lower_hex_string();
assert_eq!(
cfg.datadir_path(),
std::path::Path::new(&format!("/tmp/rbitcoin-default-signet/signet/{challenge}"))
);
}

#[tokio::test]
async fn cancelled_completes_after_request() {
let sd = Shutdown::new();
Expand Down