Merge branch 'master' into rpc-server

This commit is contained in:
binarybaron 2023-08-09 22:52:14 +02:00
commit 75bc64d248
34 changed files with 942 additions and 800 deletions

View file

@ -1,6 +1,6 @@
[package]
name = "swap"
version = "0.12.1"
version = "0.12.2"
authors = [ "The COMIT guys <hello@comit.network>" ]
edition = "2021"
description = "XMR/BTC trustless atomic swaps."
@ -14,16 +14,16 @@ async-compression = { version = "0.3", features = [ "bzip2", "tokio" ] }
async-trait = "0.1"
atty = "0.2"
backoff = { version = "0.4", features = [ "tokio" ] }
base64 = "0.20"
bdk = "0.25"
base64 = "0.21"
bdk = "0.28"
big-bytes = "1"
bitcoin = { version = "0.29", features = [ "rand", "serde" ] }
bmrng = "0.5"
comfy-table = "6.1"
config = { version = "0.13", default-features = false, features = [ "toml" ] }
conquer-once = "0.3"
conquer-once = "0.4"
curve25519-dalek = { package = "curve25519-dalek-ng", version = "4" }
data-encoding = "2.3"
data-encoding = "2.4"
dialoguer = "0.10"
directories-next = "2"
ecdsa_fun = { git = "https://github.com/LLFourn/secp256kfun", default-features = false, features = [ "libsecp_compat", "serde", "adaptor" ] }
@ -66,7 +66,7 @@ tracing-appender = "0.2"
tracing-futures = { version = "0.2", features = [ "std-future", "futures-03" ] }
tracing-subscriber = { version = "0.3", default-features = false, features = [ "fmt", "ansi", "env-filter", "time", "tracing-log", "json" ] }
url = { version = "2", features = [ "serde" ] }
uuid = { version = "1.2", features = [ "serde", "v4" ] }
uuid = { version = "1.4", features = [ "serde", "v4" ] }
void = "1"
[target.'cfg(not(windows))'.dependencies]
@ -86,11 +86,11 @@ port_check = "0.1"
proptest = "1"
sequential-test = "0.2.4"
serde_cbor = "0.11"
serial_test = "0.10"
serial_test = "2.0"
spectral = "0.6"
tempfile = "3"
testcontainers = "0.12"
[build-dependencies]
anyhow = "1"
vergen = { version = "7", default-features = false, features = [ "git", "build" ] }
vergen = { version = "7.5", default-features = false, features = [ "git", "build" ] }

View file

@ -8,6 +8,7 @@ pub mod tracing;
pub use event_loop::{EventLoop, EventLoopHandle, FixedRate, KrakenRate, LatestRate};
pub use network::behaviour::{Behaviour, OutEvent};
pub use network::rendezvous::RendezvousNode;
pub use network::transport;
pub use rate::Rate;
pub use recovery::cancel::cancel;
@ -18,4 +19,4 @@ pub use recovery::safely_abort::safely_abort;
pub use recovery::{cancel, refund};
#[cfg(test)]
pub use network::rendezous;
pub use network::rendezvous;

View file

@ -134,8 +134,8 @@ pub struct Data {
pub struct Network {
#[serde(deserialize_with = "addr_list::deserialize")]
pub listen: Vec<Multiaddr>,
#[serde(default)]
pub rendezvous_point: Option<Multiaddr>,
#[serde(default, deserialize_with = "addr_list::deserialize")]
pub rendezvous_point: Vec<Multiaddr>,
#[serde(default, deserialize_with = "addr_list::deserialize")]
pub external_addresses: Vec<Multiaddr>,
}
@ -156,7 +156,7 @@ mod addr_list {
let list: Result<Vec<_>, _> = s
.split(',')
.filter(|s| !s.is_empty())
.map(|s| s.parse().map_err(de::Error::custom))
.map(|s| s.trim().parse().map_err(de::Error::custom))
.collect();
Ok(list?)
}
@ -165,7 +165,7 @@ mod addr_list {
.iter()
.map(|v| {
if let Value::String(s) = v {
s.parse().map_err(de::Error::custom)
s.trim().parse().map_err(de::Error::custom)
} else {
Err(de::Error::custom("expected a string"))
}
@ -347,10 +347,27 @@ pub fn query_user_for_initial_config(testnet: bool) -> Result<Config> {
}
let ask_spread = Decimal::from_f64(ask_spread).context("Unable to parse spread")?;
let rendezvous_point = Input::<Multiaddr>::with_theme(&ColorfulTheme::default())
.with_prompt("Do you want to advertise your ASB instance with a rendezvous node? Enter an empty string if not.")
.allow_empty(true)
.interact_text()?;
let mut number = 1;
let mut done = false;
let mut rendezvous_points = Vec::new();
println!("ASB can register with multiple rendezvous nodes for discoverability. This can also be edited in the config file later.");
while !done {
let prompt = format!(
"Enter the address for rendezvous node ({number}). Or just hit Enter to continue."
);
let rendezvous_addr = Input::<Multiaddr>::with_theme(&ColorfulTheme::default())
.with_prompt(prompt)
.allow_empty(true)
.interact_text()?;
if rendezvous_addr.is_empty() {
done = true;
} else if rendezvous_points.contains(&rendezvous_addr) {
println!("That rendezvous address is already in the list.");
} else {
rendezvous_points.push(rendezvous_addr);
number += 1;
}
}
println!();
@ -358,11 +375,7 @@ pub fn query_user_for_initial_config(testnet: bool) -> Result<Config> {
data: Data { dir: data_dir },
network: Network {
listen: listen_addresses,
rendezvous_point: if rendezvous_point.is_empty() {
None
} else {
Some(rendezvous_point)
},
rendezvous_point: rendezvous_points, // keeping the singular key name for backcompat
external_addresses: vec![],
},
bitcoin: Bitcoin {
@ -417,7 +430,7 @@ mod tests {
},
network: Network {
listen: vec![defaults.listen_address_tcp, defaults.listen_address_ws],
rendezvous_point: None,
rendezvous_point: vec![],
external_addresses: vec![],
},
monero: Monero {
@ -461,7 +474,7 @@ mod tests {
},
network: Network {
listen: vec![defaults.listen_address_tcp, defaults.listen_address_ws],
rendezvous_point: None,
rendezvous_point: vec![],
external_addresses: vec![],
},
monero: Monero {
@ -515,7 +528,7 @@ mod tests {
},
network: Network {
listen,
rendezvous_point: None,
rendezvous_point: vec![],
external_addresses,
},
monero: Monero {

View file

@ -253,8 +253,8 @@ where
channel
}.boxed());
}
SwarmEvent::Behaviour(OutEvent::Rendezvous(libp2p::rendezvous::client::Event::Registered { .. })) => {
tracing::info!("Successfully registered with rendezvous node");
SwarmEvent::Behaviour(OutEvent::Rendezvous(libp2p::rendezvous::client::Event::Registered { rendezvous_node, ttl, namespace })) => {
tracing::info!("Successfully registered with rendezvous node: {} with namespace: {} and TTL: {:?}", rendezvous_node, namespace, ttl);
}
SwarmEvent::Behaviour(OutEvent::Rendezvous(libp2p::rendezvous::client::Event::RegisterFailed(error))) => {
tracing::error!("Registration with rendezvous node failed: {:?}", error);

View file

@ -44,7 +44,9 @@ pub mod transport {
}
pub mod behaviour {
use super::*;
use libp2p::swarm::behaviour::toggle::Toggle;
use super::{rendezvous::RendezvousNode, *};
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
@ -108,7 +110,7 @@ pub mod behaviour {
where
LR: LatestRate + Send + 'static,
{
pub rendezvous: libp2p::swarm::behaviour::toggle::Toggle<rendezous::Behaviour>,
pub rendezvous: Toggle<rendezvous::Behaviour>,
pub quote: quote::Behaviour,
pub swap_setup: alice::Behaviour<LR>,
pub transfer_proof: transfer_proof::Behaviour,
@ -132,25 +134,22 @@ pub mod behaviour {
resume_only: bool,
env_config: env::Config,
identify_params: (identity::Keypair, XmrBtcNamespace),
rendezvous_params: Option<(identity::Keypair, PeerId, Multiaddr, XmrBtcNamespace)>,
rendezvous_nodes: Vec<RendezvousNode>,
) -> Self {
let agentVersion = format!("asb/{} ({})", env!("CARGO_PKG_VERSION"), identify_params.1);
let protocolVersion = "/comit/xmr/btc/1.0.0".to_string();
let identifyConfig = IdentifyConfig::new(protocolVersion, identify_params.0.public())
.with_agent_version(agentVersion);
let (identity, namespace) = identify_params;
let agent_version = format!("asb/{} ({})", env!("CARGO_PKG_VERSION"), namespace);
let protocol_version = "/comit/xmr/btc/1.0.0".to_string();
let identifyConfig = IdentifyConfig::new(protocol_version, identity.public())
.with_agent_version(agent_version);
let behaviour = if rendezvous_nodes.is_empty() {
None
} else {
Some(rendezvous::Behaviour::new(identity, rendezvous_nodes))
};
Self {
rendezvous: libp2p::swarm::behaviour::toggle::Toggle::from(rendezvous_params.map(
|(identity, rendezvous_peer_id, rendezvous_address, namespace)| {
rendezous::Behaviour::new(
identity,
rendezvous_peer_id,
rendezvous_address,
namespace,
None, // use default ttl on rendezvous point
)
},
)),
rendezvous: Toggle::from(behaviour),
quote: quote::asb(),
swap_setup: alice::Behaviour::new(
min_buy,
@ -186,13 +185,14 @@ pub mod behaviour {
}
}
pub mod rendezous {
pub mod rendezvous {
use super::*;
use libp2p::swarm::dial_opts::DialOpts;
use libp2p::swarm::DialError;
use std::collections::VecDeque;
use std::pin::Pin;
#[derive(PartialEq)]
#[derive(Clone, PartialEq)]
enum ConnectionStatus {
Disconnected,
Dialling,
@ -209,39 +209,59 @@ pub mod rendezous {
pub struct Behaviour {
inner: libp2p::rendezvous::client::Behaviour,
rendezvous_point: Multiaddr,
rendezvous_peer_id: PeerId,
namespace: XmrBtcNamespace,
registration_status: RegistrationStatus,
connection_status: ConnectionStatus,
registration_ttl: Option<u64>,
rendezvous_nodes: Vec<RendezvousNode>,
to_dial: VecDeque<PeerId>,
}
impl Behaviour {
pub struct RendezvousNode {
pub address: Multiaddr,
connection_status: ConnectionStatus,
pub peer_id: PeerId,
registration_status: RegistrationStatus,
pub registration_ttl: Option<u64>,
pub namespace: XmrBtcNamespace,
}
impl RendezvousNode {
pub fn new(
identity: identity::Keypair,
rendezvous_peer_id: PeerId,
rendezvous_address: Multiaddr,
address: &Multiaddr,
peer_id: PeerId,
namespace: XmrBtcNamespace,
registration_ttl: Option<u64>,
) -> Self {
Self {
inner: libp2p::rendezvous::client::Behaviour::new(identity),
rendezvous_point: rendezvous_address,
rendezvous_peer_id,
namespace,
registration_status: RegistrationStatus::RegisterOnNextConnection,
address: address.to_owned(),
connection_status: ConnectionStatus::Disconnected,
namespace,
peer_id,
registration_status: RegistrationStatus::RegisterOnNextConnection,
registration_ttl,
}
}
fn register(&mut self) {
self.inner.register(
self.namespace.into(),
self.rendezvous_peer_id,
self.registration_ttl,
);
fn set_connection(&mut self, status: ConnectionStatus) {
self.connection_status = status;
}
fn set_registration(&mut self, status: RegistrationStatus) {
self.registration_status = status;
}
}
impl Behaviour {
pub fn new(identity: identity::Keypair, rendezvous_nodes: Vec<RendezvousNode>) -> Self {
Self {
inner: libp2p::rendezvous::client::Behaviour::new(identity),
rendezvous_nodes,
to_dial: VecDeque::new(),
}
}
/// Calls the rendezvous register method of the node at node_index in the Vec of rendezvous nodes
fn register(&mut self, node_index: usize) {
let node = &self.rendezvous_nodes[node_index];
self.inner
.register(node.namespace.into(), node.peer_id, node.registration_ttl);
}
}
@ -255,31 +275,37 @@ pub mod rendezous {
}
fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec<Multiaddr> {
if peer_id == &self.rendezvous_peer_id {
return vec![self.rendezvous_point.clone()];
for node in self.rendezvous_nodes.iter() {
if peer_id == &node.peer_id {
return vec![node.address.clone()];
}
}
vec![]
}
fn inject_connected(&mut self, peer_id: &PeerId) {
if peer_id == &self.rendezvous_peer_id {
self.connection_status = ConnectionStatus::Connected;
match &self.registration_status {
RegistrationStatus::RegisterOnNextConnection => {
self.register();
self.registration_status = RegistrationStatus::Pending;
for i in 0..self.rendezvous_nodes.len() {
if peer_id == &self.rendezvous_nodes[i].peer_id {
self.rendezvous_nodes[i].set_connection(ConnectionStatus::Connected);
match &self.rendezvous_nodes[i].registration_status {
RegistrationStatus::RegisterOnNextConnection => {
self.register(i);
self.rendezvous_nodes[i].set_registration(RegistrationStatus::Pending);
}
RegistrationStatus::Registered { .. } => {}
RegistrationStatus::Pending => {}
}
RegistrationStatus::Registered { .. } => {}
RegistrationStatus::Pending => {}
}
}
}
fn inject_disconnected(&mut self, peer_id: &PeerId) {
if peer_id == &self.rendezvous_peer_id {
self.connection_status = ConnectionStatus::Disconnected;
for i in 0..self.rendezvous_nodes.len() {
let mut node = &mut self.rendezvous_nodes[i];
if peer_id == &node.peer_id {
node.connection_status = ConnectionStatus::Disconnected;
}
}
}
@ -298,9 +324,12 @@ pub mod rendezous {
_handler: Self::ProtocolsHandler,
_error: &DialError,
) {
if let Some(id) = peer_id {
if id == self.rendezvous_peer_id {
self.connection_status = ConnectionStatus::Disconnected;
for i in 0..self.rendezvous_nodes.len() {
let mut node = &mut self.rendezvous_nodes[i];
if let Some(id) = peer_id {
if id == node.peer_id {
node.connection_status = ConnectionStatus::Disconnected;
}
}
}
}
@ -311,62 +340,73 @@ pub mod rendezous {
cx: &mut std::task::Context<'_>,
params: &mut impl PollParameters,
) -> Poll<NetworkBehaviourAction<Self::OutEvent, Self::ProtocolsHandler>> {
match &mut self.registration_status {
RegistrationStatus::RegisterOnNextConnection => match self.connection_status {
ConnectionStatus::Disconnected => {
self.connection_status = ConnectionStatus::Dialling;
if let Some(peer_id) = self.to_dial.pop_front() {
return Poll::Ready(NetworkBehaviourAction::Dial {
opts: DialOpts::peer_id(peer_id)
.condition(PeerCondition::Disconnected)
.build(),
return Poll::Ready(NetworkBehaviourAction::Dial {
opts: DialOpts::peer_id(self.rendezvous_peer_id)
.condition(PeerCondition::Disconnected)
.build(),
handler: Self::ProtocolsHandler::new(Duration::from_secs(30)),
});
}
ConnectionStatus::Dialling => {}
ConnectionStatus::Connected => {
self.registration_status = RegistrationStatus::Pending;
self.register();
}
},
RegistrationStatus::Registered { re_register_in } => {
if let Poll::Ready(()) = re_register_in.poll_unpin(cx) {
match self.connection_status {
ConnectionStatus::Connected => {
self.registration_status = RegistrationStatus::Pending;
self.register();
handler: Self::ProtocolsHandler::new(Duration::from_secs(30)),
});
}
// check the status of each rendezvous node
for i in 0..self.rendezvous_nodes.len() {
let connection_status = self.rendezvous_nodes[i].connection_status.clone();
match &mut self.rendezvous_nodes[i].registration_status {
RegistrationStatus::RegisterOnNextConnection => match connection_status {
ConnectionStatus::Disconnected => {
self.rendezvous_nodes[i].set_connection(ConnectionStatus::Dialling);
self.to_dial.push_back(self.rendezvous_nodes[i].peer_id);
}
ConnectionStatus::Dialling => {}
ConnectionStatus::Connected => {
self.rendezvous_nodes[i].set_registration(RegistrationStatus::Pending);
self.register(i);
}
},
RegistrationStatus::Registered { re_register_in } => {
if let Poll::Ready(()) = re_register_in.poll_unpin(cx) {
match connection_status {
ConnectionStatus::Connected => {
self.rendezvous_nodes[i]
.set_registration(RegistrationStatus::Pending);
self.register(i);
}
ConnectionStatus::Disconnected => {
self.rendezvous_nodes[i].set_registration(
RegistrationStatus::RegisterOnNextConnection,
);
self.to_dial.push_back(self.rendezvous_nodes[i].peer_id);
}
ConnectionStatus::Dialling => {}
}
ConnectionStatus::Disconnected => {
self.registration_status =
RegistrationStatus::RegisterOnNextConnection;
return Poll::Ready(NetworkBehaviourAction::Dial {
opts: DialOpts::peer_id(self.rendezvous_peer_id)
.condition(PeerCondition::Disconnected)
.build(),
handler: Self::ProtocolsHandler::new(Duration::from_secs(30)),
});
}
ConnectionStatus::Dialling => {}
}
}
RegistrationStatus::Pending => {}
}
RegistrationStatus::Pending => {}
}
let inner_poll = self.inner.poll(cx, params);
// reset the timer if we successfully registered
// reset the timer for the specific rendezvous node if we successfully registered
if let Poll::Ready(NetworkBehaviourAction::GenerateEvent(
libp2p::rendezvous::client::Event::Registered { ttl, .. },
libp2p::rendezvous::client::Event::Registered {
ttl,
rendezvous_node,
..
},
)) = &inner_poll
{
let half_of_ttl = Duration::from_secs(*ttl) / 2;
self.registration_status = RegistrationStatus::Registered {
re_register_in: Box::pin(tokio::time::sleep(half_of_ttl)),
};
if let Some(i) = self
.rendezvous_nodes
.iter()
.position(|n| &n.peer_id == rendezvous_node)
{
let half_of_ttl = Duration::from_secs(*ttl) / 2;
let re_register_in = Box::pin(tokio::time::sleep(half_of_ttl));
let status = RegistrationStatus::Registered { re_register_in };
self.rendezvous_nodes[i].set_registration(status);
}
}
inner_poll
@ -380,6 +420,7 @@ pub mod rendezous {
use futures::StreamExt;
use libp2p::rendezvous;
use libp2p::swarm::SwarmEvent;
use std::collections::HashMap;
#[tokio::test]
async fn given_no_initial_connection_when_constructed_asb_connects_and_registers_with_rendezvous_node(
@ -387,16 +428,16 @@ pub mod rendezous {
let mut rendezvous_node = new_swarm(|_, _| {
rendezvous::server::Behaviour::new(rendezvous::server::Config::default())
});
let rendezvous_address = rendezvous_node.listen_on_random_memory_address().await;
let address = rendezvous_node.listen_on_random_memory_address().await;
let rendezvous_point = RendezvousNode::new(
&address,
rendezvous_node.local_peer_id().to_owned(),
XmrBtcNamespace::Testnet,
None,
);
let mut asb = new_swarm(|_, identity| {
rendezous::Behaviour::new(
identity,
*rendezvous_node.local_peer_id(),
rendezvous_address,
XmrBtcNamespace::Testnet,
None,
)
super::rendezvous::Behaviour::new(identity, vec![rendezvous_point])
});
asb.listen_on_random_memory_address().await; // this adds an external address
@ -428,16 +469,16 @@ pub mod rendezous {
rendezvous::server::Config::default().with_min_ttl(2),
)
});
let rendezvous_address = rendezvous_node.listen_on_random_memory_address().await;
let address = rendezvous_node.listen_on_random_memory_address().await;
let rendezvous_point = RendezvousNode::new(
&address,
rendezvous_node.local_peer_id().to_owned(),
XmrBtcNamespace::Testnet,
Some(5),
);
let mut asb = new_swarm(|_, identity| {
rendezous::Behaviour::new(
identity,
*rendezvous_node.local_peer_id(),
rendezvous_address,
XmrBtcNamespace::Testnet,
Some(5),
)
super::rendezvous::Behaviour::new(identity, vec![rendezvous_point])
});
asb.listen_on_random_memory_address().await; // this adds an external address
@ -467,5 +508,62 @@ pub mod rendezous {
.unwrap()
.unwrap();
}
#[tokio::test]
async fn asb_registers_multiple() {
let registration_ttl = Some(10);
let mut rendezvous_nodes = Vec::new();
let mut registrations = HashMap::new();
// register with 5 rendezvous nodes
for _ in 0..5 {
let mut rendezvous = new_swarm(|_, _| {
rendezvous::server::Behaviour::new(
rendezvous::server::Config::default().with_min_ttl(2),
)
});
let address = rendezvous.listen_on_random_memory_address().await;
let id = *rendezvous.local_peer_id();
registrations.insert(id, 0);
rendezvous_nodes.push(RendezvousNode::new(
&address,
*rendezvous.local_peer_id(),
XmrBtcNamespace::Testnet,
registration_ttl,
));
tokio::spawn(async move {
loop {
rendezvous.next().await;
}
});
}
let mut asb = new_swarm(|_, identity| {
super::rendezvous::Behaviour::new(identity, rendezvous_nodes)
});
asb.listen_on_random_memory_address().await; // this adds an external address
let handle = tokio::spawn(async move {
loop {
if let SwarmEvent::Behaviour(rendezvous::client::Event::Registered {
rendezvous_node,
..
}) = asb.select_next_some().await
{
registrations
.entry(rendezvous_node)
.and_modify(|counter| *counter += 1);
}
if registrations.iter().all(|(_, &count)| count >= 4) {
break;
}
}
});
tokio::time::timeout(Duration::from_secs(30), handle)
.await
.unwrap()
.unwrap();
}
}
}

View file

@ -102,6 +102,19 @@ async fn main() -> Result<()> {
match cmd {
Command::Start { resume_only } => {
// check and warn for duplicate rendezvous points
let mut rendezvous_addrs = config.network.rendezvous_point.clone();
let prev_len = rendezvous_addrs.len();
rendezvous_addrs.sort();
rendezvous_addrs.dedup();
let new_len = rendezvous_addrs.len();
if new_len < prev_len {
tracing::warn!(
"`rendezvous_point` config has {} duplicate entries, they are being ignored.",
prev_len - new_len
);
}
let monero_wallet = init_monero_wallet(&config, env_config).await?;
let monero_address = monero_wallet.get_main_address();
tracing::info!(%monero_address, "Monero wallet address");
@ -161,7 +174,7 @@ async fn main() -> Result<()> {
resume_only,
env_config,
namespace,
config.network.rendezvous_point,
&rendezvous_addrs,
)?;
for listen in config.network.listen.clone() {

View file

@ -210,14 +210,20 @@ impl TxCancel {
};
// The order in which these are inserted doesn't matter
satisfier.insert(A, ::bitcoin::EcdsaSig {
sig: sig_a.into(),
hash_ty: EcdsaSighashType::All,
});
satisfier.insert(B, ::bitcoin::EcdsaSig {
sig: sig_b.into(),
hash_ty: EcdsaSighashType::All,
});
satisfier.insert(
A,
::bitcoin::EcdsaSig {
sig: sig_a.into(),
hash_ty: EcdsaSighashType::All,
},
);
satisfier.insert(
B,
::bitcoin::EcdsaSig {
sig: sig_b.into(),
hash_ty: EcdsaSighashType::All,
},
);
satisfier
};

View file

@ -65,14 +65,20 @@ impl TxPunish {
let B = B.try_into()?;
// The order in which these are inserted doesn't matter
satisfier.insert(A, ::bitcoin::EcdsaSig {
sig: sig_a.into(),
hash_ty: EcdsaSighashType::All,
});
satisfier.insert(B, ::bitcoin::EcdsaSig {
sig: sig_b.into(),
hash_ty: EcdsaSighashType::All,
});
satisfier.insert(
A,
::bitcoin::EcdsaSig {
sig: sig_a.into(),
hash_ty: EcdsaSighashType::All,
},
);
satisfier.insert(
B,
::bitcoin::EcdsaSig {
sig: sig_b.into(),
hash_ty: EcdsaSighashType::All,
},
);
satisfier
};

View file

@ -87,14 +87,20 @@ impl TxRedeem {
};
// The order in which these are inserted doesn't matter
satisfier.insert(A, ::bitcoin::EcdsaSig {
sig: sig_a.into(),
hash_ty: EcdsaSighashType::All,
});
satisfier.insert(B, ::bitcoin::EcdsaSig {
sig: sig_b.into(),
hash_ty: EcdsaSighashType::All,
});
satisfier.insert(
A,
::bitcoin::EcdsaSig {
sig: sig_a.into(),
hash_ty: EcdsaSighashType::All,
},
);
satisfier.insert(
B,
::bitcoin::EcdsaSig {
sig: sig_b.into(),
hash_ty: EcdsaSighashType::All,
},
);
satisfier
};

View file

@ -70,14 +70,20 @@ impl TxRefund {
};
// The order in which these are inserted doesn't matter
satisfier.insert(A, ::bitcoin::EcdsaSig {
sig: sig_a.into(),
hash_ty: EcdsaSighashType::All,
});
satisfier.insert(B, ::bitcoin::EcdsaSig {
sig: sig_b.into(),
hash_ty: EcdsaSighashType::All,
});
satisfier.insert(
A,
::bitcoin::EcdsaSig {
sig: sig_a.into(),
hash_ty: EcdsaSighashType::All,
},
);
satisfier.insert(
B,
::bitcoin::EcdsaSig {
sig: sig_b.into(),
hash_ty: EcdsaSighashType::All,
},
);
satisfier
};

View file

@ -738,12 +738,15 @@ impl Client {
let client = bdk::electrum_client::Client::new(electrum_rpc_url.as_str())
.context("Failed to initialize Electrum RPC client")?;
let blockchain = ElectrumBlockchain::from(client);
let last_sync = Instant::now()
.checked_sub(interval)
.expect("no underflow since block time is only 600 secs");
Ok(Self {
electrum,
blockchain,
latest_block_height: BlockHeight::try_from(latest_block)?,
last_sync: Instant::now() - interval,
last_sync,
sync_interval: interval,
script_history: Default::default(),
subscriptions: Default::default(),

View file

@ -15,6 +15,7 @@ pub use list_sellers::{list_sellers, Seller, Status as SellerStatus};
mod tests {
use super::*;
use crate::asb;
use crate::asb::rendezvous::RendezvousNode;
use crate::cli::list_sellers::{Seller, Status};
use crate::network::quote;
use crate::network::quote::BidQuote;
@ -33,10 +34,8 @@ mod tests {
async fn list_sellers_should_report_all_registered_asbs_with_a_quote() {
let namespace = XmrBtcNamespace::Mainnet;
let (rendezvous_address, rendezvous_peer_id) = setup_rendezvous_point().await;
let expected_seller_1 =
setup_asb(rendezvous_peer_id, rendezvous_address.clone(), namespace).await;
let expected_seller_2 =
setup_asb(rendezvous_peer_id, rendezvous_address.clone(), namespace).await;
let expected_seller_1 = setup_asb(rendezvous_peer_id, &rendezvous_address, namespace).await;
let expected_seller_2 = setup_asb(rendezvous_peer_id, &rendezvous_address, namespace).await;
let list_sellers = list_sellers(
rendezvous_peer_id,
@ -72,7 +71,7 @@ mod tests {
async fn setup_asb(
rendezvous_peer_id: PeerId,
rendezvous_address: Multiaddr,
rendezvous_address: &Multiaddr,
namespace: XmrBtcNamespace,
) -> Seller {
let static_quote = BidQuote {
@ -81,18 +80,18 @@ mod tests {
max_quantity: bitcoin::Amount::from_sat(9001),
};
let mut asb = new_swarm(|_, identity| StaticQuoteAsbBehaviour {
rendezvous: asb::rendezous::Behaviour::new(
identity,
rendezvous_peer_id,
rendezvous_address,
namespace,
None,
),
ping: Default::default(),
quote: quote::asb(),
static_quote,
registered: false,
let mut asb = new_swarm(|_, identity| {
let rendezvous_node =
RendezvousNode::new(rendezvous_address, rendezvous_peer_id, namespace, None);
let rendezvous = asb::rendezvous::Behaviour::new(identity, vec![rendezvous_node]);
StaticQuoteAsbBehaviour {
rendezvous,
ping: Default::default(),
quote: quote::asb(),
static_quote,
registered: false,
}
});
let asb_address = asb.listen_on_tcp_localhost().await;
@ -121,7 +120,7 @@ mod tests {
#[derive(libp2p::NetworkBehaviour)]
#[behaviour(event_process = true)]
struct StaticQuoteAsbBehaviour {
rendezvous: asb::rendezous::Behaviour,
rendezvous: asb::rendezvous::Behaviour,
// Support `Ping` as a workaround until https://github.com/libp2p/rust-libp2p/issues/2109 is fixed.
ping: libp2p::ping::Ping,
quote: quote::Behaviour,

View file

@ -350,23 +350,26 @@ mod tests {
list.sort();
assert_eq!(list, vec![
Seller {
multiaddr: "/ip4/127.0.0.1/tcp/5678".parse().unwrap(),
status: Status::Online(BidQuote {
price: Default::default(),
min_quantity: Default::default(),
max_quantity: Default::default(),
})
},
Seller {
multiaddr: Multiaddr::empty(),
status: Status::Unreachable
},
Seller {
multiaddr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(),
status: Status::Unreachable
},
])
assert_eq!(
list,
vec![
Seller {
multiaddr: "/ip4/127.0.0.1/tcp/5678".parse().unwrap(),
status: Status::Online(BidQuote {
price: Default::default(),
min_quantity: Default::default(),
max_quantity: Default::default(),
})
},
Seller {
multiaddr: Multiaddr::empty(),
status: Status::Unreachable
},
Seller {
multiaddr: "/ip4/127.0.0.1/tcp/1234".parse().unwrap(),
status: Status::Unreachable
},
]
)
}
}

View file

@ -155,13 +155,16 @@ impl ProtocolsHandler for Handler {
let env_config = self.env_config;
let protocol = tokio::time::timeout(self.timeout, async move {
write_cbor_message(&mut substream, SpotPriceRequest {
btc: info.btc,
blockchain_network: BlockchainNetwork {
bitcoin: env_config.bitcoin_network,
monero: env_config.monero_network,
write_cbor_message(
&mut substream,
SpotPriceRequest {
btc: info.btc,
blockchain_network: BlockchainNetwork {
bitcoin: env_config.bitcoin_network,
monero: env_config.monero_network,
},
},
})
)
.await?;
let xmr = Result::from(read_cbor_message::<SpotPriceResponse>(&mut substream).await?)?;

View file

@ -1,9 +1,9 @@
use crate::asb::LatestRate;
use crate::asb::{LatestRate, RendezvousNode};
use crate::libp2p_ext::MultiAddrExt;
use crate::network::rendezvous::XmrBtcNamespace;
use crate::seed::Seed;
use crate::{asb, bitcoin, cli, env, tor};
use anyhow::{Context, Result};
use anyhow::Result;
use libp2p::swarm::{NetworkBehaviour, SwarmBuilder};
use libp2p::{identity, Multiaddr, Swarm};
use std::fmt::Debug;
@ -17,22 +17,23 @@ pub fn asb<LR>(
resume_only: bool,
env_config: env::Config,
namespace: XmrBtcNamespace,
rendezvous_point: Option<Multiaddr>,
rendezvous_addrs: &[Multiaddr],
) -> Result<Swarm<asb::Behaviour<LR>>>
where
LR: LatestRate + Send + 'static + Debug + Clone,
{
let identity = seed.derive_libp2p_identity();
let rendezvous_params = if let Some(address) = rendezvous_point {
let peer_id = address
.extract_peer_id()
.context("Rendezvous node address must contain peer ID")?;
let rendezvous_nodes = rendezvous_addrs
.iter()
.map(|addr| {
let peer_id = addr
.extract_peer_id()
.expect("Rendezvous node address must contain peer ID");
Some((identity.clone(), peer_id, address, namespace))
} else {
None
};
RendezvousNode::new(addr, peer_id, namespace, None)
})
.collect();
let behaviour = asb::Behaviour::new(
min_buy,
@ -41,7 +42,7 @@ where
resume_only,
env_config,
(identity.clone(), namespace),
rendezvous_params,
rendezvous_nodes,
);
let transport = asb::transport::new(&identity)?;

View file

@ -21,7 +21,7 @@ struct GlobalSpawnTokioExecutor;
impl Executor for GlobalSpawnTokioExecutor {
fn exec(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>) {
let _ = tokio::spawn(future);
tokio::spawn(future);
}
}

View file

@ -184,29 +184,32 @@ impl State0 {
let v = self.v_a + msg.v_b;
Ok((msg.swap_id, State1 {
a: self.a,
B: msg.B,
s_a: self.s_a,
S_a_monero: self.S_a_monero,
S_a_bitcoin: self.S_a_bitcoin,
S_b_monero: msg.S_b_monero,
S_b_bitcoin: msg.S_b_bitcoin,
v,
v_a: self.v_a,
dleq_proof_s_a: self.dleq_proof_s_a,
btc: self.btc,
xmr: self.xmr,
cancel_timelock: self.cancel_timelock,
punish_timelock: self.punish_timelock,
refund_address: msg.refund_address,
redeem_address: self.redeem_address,
punish_address: self.punish_address,
tx_redeem_fee: self.tx_redeem_fee,
tx_punish_fee: self.tx_punish_fee,
tx_refund_fee: msg.tx_refund_fee,
tx_cancel_fee: msg.tx_cancel_fee,
}))
Ok((
msg.swap_id,
State1 {
a: self.a,
B: msg.B,
s_a: self.s_a,
S_a_monero: self.S_a_monero,
S_a_bitcoin: self.S_a_bitcoin,
S_b_monero: msg.S_b_monero,
S_b_bitcoin: msg.S_b_bitcoin,
v,
v_a: self.v_a,
dleq_proof_s_a: self.dleq_proof_s_a,
btc: self.btc,
xmr: self.xmr,
cancel_timelock: self.cancel_timelock,
punish_timelock: self.punish_timelock,
refund_address: msg.refund_address,
redeem_address: self.redeem_address,
punish_address: self.punish_address,
tx_redeem_fee: self.tx_redeem_fee,
tx_punish_fee: self.tx_punish_fee,
tx_refund_fee: msg.tx_refund_fee,
tx_cancel_fee: msg.tx_cancel_fee,
},
))
}
}

View file

@ -187,6 +187,9 @@ mod tests {
#[test]
fn seed_from_pem_works() {
use base64::engine::general_purpose;
use base64::Engine;
let payload: &str = "syl9wSYaruvgxg9P5Q1qkZaq5YkM6GvXkxe+VYrL/XM=";
// 32 bytes base64 encoded.
@ -195,7 +198,7 @@ syl9wSYaruvgxg9P5Q1qkZaq5YkM6GvXkxe+VYrL/XM=
-----END SEED-----
";
let want = base64::decode(payload).unwrap();
let want = general_purpose::STANDARD.decode(payload).unwrap();
let pem = pem::parse(pem_string).unwrap();
let got = Seed::from_pem(pem).unwrap();

View file

@ -8,7 +8,7 @@ async fn ensure_same_swap_id_for_alice_and_bob() {
harness::setup_test(SlowCancelConfig, |mut ctx| async move {
let (bob_swap, _) = ctx.bob_swap().await;
let bob_swap_id = bob_swap.id;
let _ = tokio::spawn(bob::run(bob_swap));
tokio::spawn(bob::run(bob_swap));
// once Bob's swap is spawned we can retrieve Alice's swap and assert on the
// swap ID

View file

@ -158,13 +158,16 @@ async fn init_containers(cli: &Cli) -> (Monero, Containers<'_>) {
.await
.unwrap();
(monero, Containers {
bitcoind_url,
bitcoind,
monerod_container,
monero_wallet_rpc_containers,
electrs,
})
(
monero,
Containers {
bitcoind_url,
bitcoind,
monerod_container,
monero_wallet_rpc_containers,
electrs,
},
)
}
async fn init_bitcoind_container(
@ -245,7 +248,7 @@ async fn start_alice(
resume_only,
env_config,
XmrBtcNamespace::Testnet,
None,
&[],
)
.unwrap();
swarm.listen_on(listen_address).unwrap();
@ -925,7 +928,7 @@ async fn init_bitcoind(node_url: Url, spendable_quantity: u32) -> Result<Client>
bitcoind_client
.generatetoaddress(101 + spendable_quantity, reward_address.clone())
.await?;
let _ = tokio::spawn(mine(bitcoind_client.clone(), reward_address));
tokio::spawn(mine(bitcoind_client.clone(), reward_address));
Ok(bitcoind_client)
}