Skip to content

Commit 2999e05

Browse files
committed
joinstr: implem Joinstr::state() for dump a serializable state
1 parent aea2792 commit 2999e05

File tree

2 files changed

+113
-37
lines changed

2 files changed

+113
-37
lines changed

rust/joinstr/src/electrum.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,14 @@ impl Client {
638638
self.index.remove(&req_id);
639639
Err(Error::WrongResponse)
640640
}
641+
642+
/// Returns the URL of the electrum client.
643+
///
644+
/// # Returns
645+
/// A `String` containing the URL of the electrum server.
646+
pub fn url(&self) -> String {
647+
self.url.clone()
648+
}
641649
}
642650

643651
impl BitcoinBackend for Client {

rust/joinstr/src/joinstr/mod.rs

Lines changed: 105 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
mod error;
22
use backoff::Backoff;
33
pub use error::Error;
4+
use serde::{Deserialize, Serialize};
45

56
use std::{
67
collections::HashSet,
@@ -11,6 +12,7 @@ use std::{
1112

1213
use miniscript::bitcoin::{Amount, Network};
1314
use simple_nostr_client::nostr::{
15+
self,
1416
bitcoin::{address::NetworkUnchecked, Address},
1517
hashes::{sha256, Hash, HashEngine},
1618
Keys, PublicKey,
@@ -70,49 +72,66 @@ pub struct Status {
7072
pub struct JoinstrInner<'a> {
7173
role: Role,
7274
step: Step,
73-
registered_peers: usize,
74-
registered_outputs: usize,
75-
registered_inputs: usize,
7675
confirmations: usize,
7776
error: Option<String>,
7877
pub client: NostrClient,
7978
pub pool: Option<Pool>,
8079
pub denomination: Option<Amount>,
81-
pub peers: Option<usize>,
80+
pub peers_count: Option<usize>,
8281
pub timeout: Option<Timeline>,
83-
pub relays: Vec<String>,
82+
pub relay: Option<String>,
8483
pub fee: Option<Fee>,
8584
pub network: Network,
8685
pub coinjoin: Option<CoinJoin<'a, crate::electrum::Client>>,
8786
pub electrum_client: Option<crate::electrum::Client>,
8887
input: Option<Coin>,
8988
output: Option<Address>,
9089
final_tx: Option<miniscript::bitcoin::Transaction>,
90+
// requests history
91+
peers: Vec<nostr::PublicKey>,
92+
outputs: Vec<miniscript::bitcoin::Address>,
93+
inputs: Vec<InputDataSigned>,
94+
}
95+
96+
#[derive(Debug, Clone, Serialize, Deserialize)]
97+
pub struct State {
98+
pool_secret_key: String, /* nostr::Keys*/
99+
relay: String,
100+
electrum: Option<String>,
101+
pool: Pool,
102+
input: Option<Coin>,
103+
output: Option<Address<NetworkUnchecked>>,
104+
network: bitcoin::Network,
105+
final_tx: Option<bitcoin::Transaction>,
106+
// requests history
107+
peers: Vec<nostr::PublicKey>,
108+
outputs: Vec<bitcoin::Address<NetworkUnchecked>>,
109+
inputs: Vec<serde_json::Value /* InputDataSigned*/>,
91110
}
92111

93112
impl Default for JoinstrInner<'_> {
94113
fn default() -> Self {
95114
Self {
96115
role: Default::default(),
97116
step: Default::default(),
98-
registered_peers: 0,
99-
registered_outputs: 0,
100-
registered_inputs: 0,
101117
confirmations: 0,
102118
error: None,
103119
client: Default::default(),
104120
pool: Default::default(),
105121
denomination: Default::default(),
106-
peers: Default::default(),
122+
peers_count: Default::default(),
107123
timeout: Default::default(),
108-
relays: Default::default(),
124+
relay: Default::default(),
109125
fee: Default::default(),
110126
network: Network::Bitcoin,
111127
coinjoin: None,
112128
electrum_client: None,
113129
input: None,
114130
output: None,
115131
final_tx: None,
132+
peers: Default::default(),
133+
outputs: Default::default(),
134+
inputs: Default::default(),
116135
}
117136
}
118137
}
@@ -132,10 +151,10 @@ impl Joinstr<'_> {
132151
fn new(keys: Keys, relay: String, name: &str) -> Result<Self, Error> {
133152
let mut client = NostrClient::new(name).relay(relay.clone())?.keys(keys)?;
134153
client.connect_nostr()?;
135-
let relays = vec![relay];
154+
let relay = Some(relay);
136155
let inner = Arc::new(Mutex::new(JoinstrInner {
137156
client,
138-
relays,
157+
relay,
139158
..Default::default()
140159
}));
141160
Ok(Joinstr { inner })
@@ -331,8 +350,8 @@ impl Joinstr<'_> {
331350
}
332351
let mut inner = self.inner.lock().expect("poisoned");
333352
inner.pool_not_exists()?;
334-
if inner.peers.is_none() {
335-
inner.peers = Some(peers);
353+
if inner.peers_count.is_none() {
354+
inner.peers_count = Some(peers);
336355
drop(inner);
337356
Ok(self)
338357
} else {
@@ -360,7 +379,7 @@ impl Joinstr<'_> {
360379
inner.pool_not_exists()?;
361380
// TODO: check the address is valid
362381
let url: String = url.into();
363-
inner.relays.push(url);
382+
inner.relay = Some(url);
364383
drop(inner);
365384
Ok(self)
366385
}
@@ -520,7 +539,7 @@ impl Joinstr<'_> {
520539
inner.client.send_pool_message(&npub, response)?;
521540
}
522541
peers.insert(npub);
523-
inner.registered_peers += 1;
542+
inner.peers.push(npub);
524543
log::debug!(
525544
"Coordinator({}).register_outputs(): receive Join({}) request. \n peers: {}",
526545
inner.client.name,
@@ -588,10 +607,11 @@ impl Joinstr<'_> {
588607
inner.client.name,
589608
o
590609
);
591-
let outputs = vec![o];
610+
let outputs = vec![o.clone()];
592611
inner.receive_outputs(outputs, &mut coinjoin)?;
593612
// TODO: we must error if outputs > peers
594-
inner.registered_outputs += 1;
613+
// TODO: check address network
614+
inner.outputs.push(o.assume_checked());
595615
}
596616
// FIXME: here it can be some cases where, because network timing, we can
597617
// receive a signed input before the output registration round ended, we should
@@ -709,14 +729,22 @@ impl Joinstr<'_> {
709729
}
710730
}
711731

712-
/// Returns the current state of the [`Joinstr`] instance.
732+
/// Returns the current status of the [`Joinstr`] instance.
713733
///
714734
/// # Returns
715-
/// A [`State`] struct containing the current state information.
735+
/// A [`Status`] struct containing the current state information.
716736
pub fn status(&self) -> Status {
717737
self.inner.lock().expect("poisoned").status()
718738
}
719739

740+
/// Returns the current serializable state of the [`Joinstr`] instance.
741+
///
742+
/// # Returns
743+
/// A [`State`] struct containing the current state information.
744+
pub fn state(&self) -> Option<State> {
745+
self.inner.lock().expect("poisoned").state()
746+
}
747+
720748
/// Start a coinjoin process, followings steps will be processed:
721749
/// - if no `pool` arg is passed, a new pool will be initiated.
722750
/// - if a `pool` arg is passed, it will join the pool
@@ -875,9 +903,9 @@ impl<'a> JoinstrInner<'a> {
875903
fn is_ready(&self) -> Result<(), Error> {
876904
if self.pool.is_none()
877905
&& self.denomination.is_some()
878-
&& self.peers.is_some()
906+
&& self.peers_count.is_some()
879907
&& self.timeout.is_some()
880-
&& !self.relays.is_empty()
908+
&& self.relay.is_some()
881909
&& self.fee.is_some()
882910
{
883911
Ok(())
@@ -888,13 +916,13 @@ impl<'a> JoinstrInner<'a> {
888916
if self.denomination.is_none() {
889917
log::error!("Coordinator.is_ready(): denomination is missing!")
890918
}
891-
if self.peers.is_none() {
919+
if self.peers_count.is_none() {
892920
log::error!("Coordinator.is_ready(): peers is missing!")
893921
}
894922
if self.timeout.is_none() {
895923
log::error!("Coordinator.is_ready(): timeout is missing!")
896924
}
897-
if self.relays.is_empty() {
925+
if self.relay.is_none() {
898926
log::error!("Coordinator.is_ready(): no relay specified!")
899927
}
900928
if self.fee.is_none() {
@@ -924,14 +952,14 @@ impl<'a> JoinstrInner<'a> {
924952
}),
925953
tor: Some(Tor { enable: false }),
926954
};
927-
if self.relays.is_empty() {
955+
if self.relay.is_none() {
928956
return Err(Error::RelaysMissing);
929957
};
930958
let payload = PoolPayload {
931959
denomination: self.denomination.ok_or(Error::DenominationMissing)?,
932-
peers: self.peers.ok_or(Error::PeerMissing)?,
960+
peers: self.peers_count.ok_or(Error::PeerMissing)?,
933961
timeout: self.timeout.ok_or(Error::TimeoutMissing)?,
934-
relays: self.relays.clone(),
962+
relays: self.relay.clone().map(|r| vec![r]).unwrap_or_default(),
935963
fee: self.fee.clone().ok_or(Error::FeeMissing)?,
936964
transport,
937965
};
@@ -1020,7 +1048,7 @@ impl<'a> JoinstrInner<'a> {
10201048
self.pool_exists()?;
10211049
let npub = self.pool_as_ref()?.public_key;
10221050
self.client.send_pool_message(&npub, msg)?;
1023-
self.registered_outputs += 1;
1051+
self.outputs.push(address.clone());
10241052
// TODO: handle re-send if fails
10251053
Ok(())
10261054
} else {
@@ -1086,11 +1114,11 @@ impl<'a> JoinstrInner<'a> {
10861114
let signed_input = signer
10871115
.sign_input(&unsigned, input)
10881116
.map_err(Error::SigningFail)?;
1089-
let msg = PoolMessage::Input(signed_input);
1117+
let msg = PoolMessage::Input(signed_input.clone());
10901118
self.pool_exists()?;
10911119
let npub = self.pool_as_ref()?.public_key;
10921120
self.client.send_pool_message(&npub, msg)?;
1093-
self.registered_inputs += 1;
1121+
self.inputs.push(signed_input);
10941122
// TODO: handle re-send if fails
10951123
Ok(())
10961124
} else {
@@ -1112,14 +1140,14 @@ impl<'a> JoinstrInner<'a> {
11121140
);
11131141
// Register inputs
11141142
if let Some(coinjoin) = self.coinjoin.as_mut() {
1115-
if let Err(e) = coinjoin.add_input(input) {
1143+
if let Err(e) = coinjoin.add_input(input.clone()) {
11161144
log::error!(
11171145
"Coordinator({}).register_input(): fail to add input: {:?}",
11181146
self.client.name,
11191147
e
11201148
);
11211149
} else {
1122-
self.registered_inputs += 1;
1150+
self.inputs.push(input);
11231151
}
11241152
}
11251153
Ok(())
@@ -1219,19 +1247,59 @@ impl<'a> JoinstrInner<'a> {
12191247
}
12201248
}
12211249

1222-
/// Returns the current state of the [`JoinstrInner`] instance.
1250+
/// Returns the current status of the [`JoinstrInner`] instance.
12231251
///
12241252
/// # Returns
1225-
/// A [`State`] struct containing the current state information.
1253+
/// A [`Status`] struct containing the current status information.
12261254
pub fn status(&self) -> Status {
12271255
Status {
12281256
role: self.role,
12291257
step: self.step,
1230-
registered_peers: self.registered_peers,
1231-
registered_outputs: self.registered_outputs,
1232-
registered_inputs: self.registered_inputs,
1258+
registered_peers: self.peers.len(),
1259+
registered_outputs: self.outputs.len(),
1260+
registered_inputs: self.inputs.len(),
12331261
confirmations: self.confirmations,
12341262
error: self.error.clone(),
12351263
}
12361264
}
1265+
1266+
/// Returns the current serializable state of the [`JoinstrInner`] instance.
1267+
///
1268+
/// # Returns
1269+
/// A [`State`] struct containing the current state information.
1270+
pub fn state(&self) -> Option<State> {
1271+
let keys = if let Ok(keys) = self.client.get_keys() {
1272+
keys
1273+
} else {
1274+
return None;
1275+
};
1276+
let relay = if let Some(relay) = &self.relay {
1277+
relay.clone()
1278+
} else {
1279+
return None;
1280+
};
1281+
let electrum = self.electrum_client.as_ref().map(|c| c.url());
1282+
let pool = if let Some(pool) = &self.pool {
1283+
pool.clone()
1284+
} else {
1285+
return None;
1286+
};
1287+
Some(State {
1288+
pool_secret_key: keys.secret_key().to_secret_hex(),
1289+
relay,
1290+
electrum,
1291+
pool,
1292+
input: self.input.clone(),
1293+
output: self.output.clone().map(|a| a.as_unchecked().clone()),
1294+
network: self.network,
1295+
final_tx: self.final_tx.clone(),
1296+
peers: self.peers.clone(),
1297+
outputs: self
1298+
.outputs
1299+
.iter()
1300+
.map(|a| a.as_unchecked().clone())
1301+
.collect(),
1302+
inputs: self.inputs.iter().map(|i| i.to_json()).collect(),
1303+
})
1304+
}
12371305
}

0 commit comments

Comments
 (0)