-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The problem with blasting transactions to multiple peers is that it increases the chance of landing on a surveillance node. This isn't a problem when using Tor; it is mainly a problem for clearnet connections. This change ensures that we broadcast to just one peer and then wait for a roundtrip confirmation in the form of an `inventory` message from other connected peers. This is marginally slower than the blasting strategy but it increases both privacy for clearnet connections and gives us certainty in the form of verifying that our transaction has propagated through the network. The selected broadcast peer is given 10 seconds for our transaction(s) to appear on the network, after which we rotate the peer. Other changes: - bump version to 0.4.0 for both lib and bin - redo the `Report` struct to better reflect completion outcomes - deprecate the `--tesnet` switch in bin in favor of `--network` - remove the `send_unsolicited` option (unnecessary)
- Loading branch information
1 parent
1368a3d
commit 880ccb8
Showing
12 changed files
with
218 additions
and
150 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[package] | ||
name = "pushtx-cli" | ||
version = "0.3.0" | ||
version = "0.4.0" | ||
edition = "2021" | ||
authors = ["Alfred Hodler <[email protected]>"] | ||
license = "MIT" | ||
|
@@ -18,5 +18,5 @@ anyhow = "1.0.86" | |
clap = { version = "4.5.4", features = ["derive"] } | ||
env_logger = { version = "0.11.3", default-features = false } | ||
log = "0.4.20" | ||
pushtx = { version = "0.3.0", path = "../pushtx" } | ||
pushtx = { version = "0.4.0", path = "../pushtx" } | ||
thiserror = "1.0.61" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
use pushtx::*; | ||
|
||
use core::panic; | ||
use std::collections::HashSet; | ||
use std::io::Read; | ||
use std::path::PathBuf; | ||
|
||
|
@@ -17,20 +18,22 @@ use clap::Parser; | |
/// | ||
/// More verbose (debug) output can be enabled by specifying the | ||
/// -v or --verbose switch up to three times. | ||
/// | ||
/// Copyright (c) 2024 Alfred Hodler <[email protected]> | ||
#[derive(Parser)] | ||
#[command(version, about, long_about, verbatim_doc_comment, name = "pushtx")] | ||
struct Cli { | ||
/// Tor mode. Default is `try`. | ||
#[arg(short = 'm', long)] | ||
tor_mode: Option<TorMode>, | ||
/// Tor mode. | ||
#[arg(short = 'm', long, default_value_t = TorMode::Try)] | ||
tor_mode: TorMode, | ||
|
||
/// Dry-run mode. Performs the whole process except the sending part. | ||
#[arg(short, long)] | ||
dry_run: bool, | ||
|
||
/// Connect to testnet instead of mainnet. | ||
#[arg(short, long)] | ||
testnet: bool, | ||
/// The network to use. | ||
#[arg(short, long, default_value_t = Network::Mainnet)] | ||
network: Network, | ||
|
||
/// Zero or one paths to a file containing line-delimited hex encoded transactions | ||
/// | ||
|
@@ -104,16 +107,13 @@ fn main() -> anyhow::Result<()> { | |
Err(err) => Err(err), | ||
}?; | ||
|
||
let txids: HashSet<_> = txs.iter().map(|tx| tx.txid()).collect(); | ||
|
||
let receiver = broadcast( | ||
txs, | ||
Opts { | ||
use_tor: cli.tor_mode.unwrap_or_default().into(), | ||
network: if cli.testnet { | ||
Network::Testnet | ||
} else { | ||
Network::Mainnet | ||
}, | ||
send_unsolicited: true, | ||
use_tor: cli.tor_mode.into(), | ||
network: cli.network.into(), | ||
dry_run: cli.dry_run, | ||
..Default::default() | ||
}, | ||
|
@@ -124,23 +124,31 @@ fn main() -> anyhow::Result<()> { | |
Ok(Info::ResolvingPeers) => println!("* Resolving peers from DNS..."), | ||
Ok(Info::ResolvedPeers(n)) => println!("* Resolved {n} peers"), | ||
Ok(Info::ConnectingToNetwork { tor_status }) => { | ||
let network = if cli.testnet { "testnet" } else { "mainnet" }; | ||
println!("* Connecting to the P2P network ({network})..."); | ||
println!("* Connecting to the P2P network ({})...", cli.network); | ||
match tor_status { | ||
Some(proxy) => println!(" - using Tor proxy found at {proxy}"), | ||
None => println!(" - not using Tor"), | ||
} | ||
} | ||
Ok(Info::Broadcast { peer }) => println!("* Successful broadcast to peer {}", peer), | ||
Ok(Info::Done(Ok(Report { | ||
broadcasts, | ||
rejects, | ||
}))) => { | ||
println!("* Done! Broadcast to {broadcasts} peers with {rejects} rejections"); | ||
break Ok(()); | ||
Ok(Info::Broadcast { peer }) => println!("* Broadcast to peer {}", peer), | ||
Ok(Info::Done(Ok(Report { success, rejects }))) => { | ||
let difference: Vec<_> = txids.difference(&success).collect(); | ||
if difference.is_empty() { | ||
println!("* Done! Broadcast successful"); | ||
break Ok(()); | ||
} else { | ||
println!("* Failed to broadcast one or more transactions"); | ||
for missing in difference { | ||
println!(" - failed: {missing}"); | ||
} | ||
for (r_txid, r_reason) in rejects { | ||
println!(" - reject: {r_txid}: {r_reason}"); | ||
} | ||
break Err(Error::Partial.into()); | ||
} | ||
} | ||
Ok(Info::Done(Err(error))) => { | ||
break Err(Error::FailedToBroadcast(error).into()); | ||
break Err(Error::Broadcast(error).into()); | ||
} | ||
Err(_) => panic!("worker thread disconnected"), | ||
} | ||
|
@@ -156,14 +164,15 @@ enum Error { | |
#[error("Empty transaction set, did you pass at least one transaction?")] | ||
EmptyTxSet, | ||
#[error("Failed to broadcast: {0}")] | ||
FailedToBroadcast(pushtx::Error), | ||
Broadcast(pushtx::Error), | ||
#[error("Failed to broadcast one or more transactions")] | ||
Partial, | ||
} | ||
|
||
/// Determines how to use Tor. | ||
#[derive(Debug, Default, Clone, clap::ValueEnum)] | ||
#[derive(Debug, Clone, clap::ValueEnum)] | ||
pub enum TorMode { | ||
/// Use Tor if available. If not available, connect through clearnet. | ||
#[default] | ||
Try, | ||
/// Do not use Tor even if available and running. | ||
No, | ||
|
@@ -180,3 +189,43 @@ impl From<TorMode> for pushtx::TorMode { | |
} | ||
} | ||
} | ||
|
||
impl std::fmt::Display for TorMode { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
let name = match self { | ||
TorMode::Try => "try", | ||
TorMode::No => "no", | ||
TorMode::Must => "must", | ||
}; | ||
write!(f, "{}", name) | ||
} | ||
} | ||
|
||
/// The Bitcoin network to connect to. | ||
#[derive(Debug, Clone, Copy, clap::ValueEnum)] | ||
pub enum Network { | ||
Mainnet, | ||
Testnet, | ||
Signet, | ||
} | ||
|
||
impl From<Network> for pushtx::Network { | ||
fn from(value: Network) -> Self { | ||
match value { | ||
Network::Mainnet => Self::Mainnet, | ||
Network::Testnet => Self::Testnet, | ||
Network::Signet => Self::Signet, | ||
} | ||
} | ||
} | ||
|
||
impl std::fmt::Display for Network { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
let name = match self { | ||
Network::Mainnet => "mainnet", | ||
Network::Testnet => "testnet", | ||
Network::Signet => "signet", | ||
}; | ||
write!(f, "{}", name) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[package] | ||
name = "pushtx" | ||
version = "0.3.0" | ||
version = "0.4.0" | ||
edition = "2021" | ||
authors = ["Alfred Hodler <[email protected]>"] | ||
license = "MIT" | ||
|
@@ -18,5 +18,4 @@ fastrand = "2.0.2" | |
hex = "0.4.3" | ||
log = "0.4.20" | ||
peerlink = { version = "0.8.0", features = ["socks"] } | ||
port_check = "0.2.1" | ||
sha3 = "0.10.8" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.