Skip to content
This repository was archived by the owner on Sep 12, 2023. It is now read-only.

Commit 53acc31

Browse files
bors[bot]holzeis
andauthored
Merge #3173
3173: feat(taker): import seed r=holzeis a=holzeis - allows to import a seed from a file. - only supports a 256 bytes seed file - umbrel seeds are actively rejected. - splits the identity from the wallet seed for taker and maker. - extended the wallet info event for a managed_wallet flag, indicating if the seed was externally provided or generated by ItchySats. - creates an backup file of the old seed file before overwriting it with the imported one. ![import-seed](https://user-images.githubusercontent.com/382048/195764624-a3cc825c-6d15-42d5-8260-d35de92a88a5.gif) resolves #2989 see also #3048 Co-authored-by: Richard Holzeis <richard@holzeis.me>
2 parents 4e025d7 + 583a3df commit 53acc31

File tree

18 files changed

+655
-81
lines changed

18 files changed

+655
-81
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- Electron App has now a tray icon for macOS 🚀
13+
- Split identity and wallet seed into two separate files.
14+
- Wallet seeds created by ItchySats can now be imported and exported for the taker.
1315

1416
## [0.7.0] - 2022-09-30
1517

crates/bdk-ext/src/lib.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use bdk::bitcoin::secp256k1::SecretKey;
66
use bdk::bitcoin::util::bip32::ExtendedPrivKey;
77
use bdk::bitcoin::Amount;
88
use bdk::bitcoin::Network;
9+
use bdk::database::BatchDatabase;
910
use bdk::BlockTime;
1011
use rand::CryptoRng;
1112
use rand::RngCore;
@@ -17,6 +18,20 @@ pub fn new_test_wallet(
1718
utxo_amount: Amount,
1819
num_utxos: u8,
1920
) -> Result<bdk::Wallet<bdk::database::MemoryDatabase>> {
21+
new_test_wallet_from_database(
22+
rng,
23+
utxo_amount,
24+
num_utxos,
25+
bdk::database::MemoryDatabase::new(),
26+
)
27+
}
28+
29+
pub fn new_test_wallet_from_database<DB: BatchDatabase>(
30+
rng: &mut (impl RngCore + CryptoRng),
31+
utxo_amount: Amount,
32+
num_utxos: u8,
33+
mut database: DB,
34+
) -> Result<bdk::Wallet<DB>> {
2035
use bdk::populate_test_db;
2136
use bdk::testutils;
2237

@@ -26,8 +41,6 @@ pub fn new_test_wallet(
2641
let key = ExtendedPrivKey::new_master(Network::Regtest, &seed)?;
2742
let descriptors = testutils!(@descriptors (&format!("wpkh({key}/*)")));
2843

29-
let mut database = bdk::database::MemoryDatabase::new();
30-
3144
for index in 0..num_utxos {
3245
populate_test_db!(
3346
&mut database,

crates/daemon-tests/src/mocks/wallet.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use anyhow::Result;
22
use async_trait::async_trait;
33
use bdk_ext::new_test_wallet;
4+
use daemon::bdk;
45
use daemon::bdk::bitcoin::util::psbt::PartiallySignedTransaction;
56
use daemon::bdk::bitcoin::Amount;
67
use daemon::bdk::bitcoin::Txid;
@@ -52,6 +53,9 @@ impl WalletActor {
5253
async fn handle(&mut self, msg: wallet::Sync) {
5354
self.mock.lock().await.sync(msg)
5455
}
56+
async fn handle(&mut self, msg: wallet::ImportSeed) -> Result<bdk::wallet::AddressInfo> {
57+
self.mock.lock().await.import_seed(msg)
58+
}
5559
}
5660

5761
#[automock]
@@ -71,6 +75,10 @@ pub trait Wallet {
7175
fn sync(&mut self, _msg: wallet::Sync) {
7276
unreachable!("mockall will reimplement this method")
7377
}
78+
79+
fn import_seed(&mut self, _msg: wallet::ImportSeed) -> Result<bdk::wallet::AddressInfo> {
80+
unreachable!("mockall will reimplement this method")
81+
}
7482
}
7583

7684
pub fn build_party_params(msg: wallet::BuildPartyParams) -> Result<PartyParams> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use daemon::bdk::bitcoin;
2+
use daemon::seed;
3+
use daemon::seed::RandomSeed;
4+
use daemon::seed::Seed;
5+
use daemon_tests::open_cfd;
6+
use daemon_tests::start_both;
7+
use daemon_tests::OpenCfdArgs;
8+
use otel_tests::otel_test;
9+
use rand::distributions::Alphanumeric;
10+
use rand::thread_rng;
11+
use rand::Rng;
12+
use std::env;
13+
use std::path::Path;
14+
15+
#[otel_test]
16+
async fn fail_to_import_seed_with_open_cfds() {
17+
let (mut maker, mut taker) = start_both().await;
18+
19+
let cfd_args = OpenCfdArgs::default();
20+
open_cfd(&mut taker, &mut maker, cfd_args.clone()).await;
21+
22+
let random_dir: String = thread_rng()
23+
.sample_iter(&Alphanumeric)
24+
.take(7)
25+
.map(char::from)
26+
.collect();
27+
let data_dir = env::temp_dir().join(Path::new(&random_dir));
28+
tokio::fs::create_dir_all(&data_dir)
29+
.await
30+
.expect("failed to create random temp folder");
31+
32+
taker
33+
.system
34+
.import_seed(
35+
RandomSeed::default().seed(),
36+
data_dir,
37+
bitcoin::Network::Testnet,
38+
seed::TAKER_WALLET_SEED_FILE,
39+
)
40+
.await
41+
.expect_err("import seed should be rejected as open CFDs exist");
42+
}

crates/daemon-tests/tests/main/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod collaborative_settlement;
22
mod connectivity;
3+
mod import_seed;
34
mod liquidation;
45
mod non_collaborative_settlement;
56
mod offer;

crates/daemon/src/lib.rs

+38-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![cfg_attr(not(test), warn(clippy::unwrap_used))]
22

33
use crate::bitcoin::util::psbt::PartiallySignedTransaction;
4+
use crate::bitcoin::Network;
45
use crate::bitcoin::Txid;
56
use crate::listen_protocols::TAKER_LISTEN_PROTOCOLS;
67
use anyhow::bail;
@@ -31,6 +32,7 @@ use ping_pong::ping;
3132
use ping_pong::pong;
3233
use seed::Identities;
3334
use std::collections::HashSet;
35+
use std::path::PathBuf;
3436
use std::sync::Arc;
3537
use std::time::Duration;
3638
use time::ext::NumericalDuration;
@@ -78,7 +80,7 @@ pub const N_PAYOUTS: usize = 200;
7880

7981
pub struct TakerActorSystem<O, W, P> {
8082
pub cfd_actor: Address<taker_cfd::Actor>,
81-
wallet_actor: Address<W>,
83+
pub wallet_actor: Address<W>,
8284
_oracle_actor: Address<O>,
8385
pub auto_rollover_actor: Address<auto_rollover::Actor>,
8486
pub price_feed_actor: Address<P>,
@@ -93,6 +95,8 @@ pub struct TakerActorSystem<O, W, P> {
9395
pub identify_info_feed_receiver: watch::Receiver<Option<PeerInfo>>,
9496

9597
_tasks: Tasks,
98+
99+
db: sqlite_db::Connection,
96100
}
97101

98102
impl<O, W, P> TakerActorSystem<O, W, P>
@@ -105,6 +109,7 @@ where
105109
W: Handler<wallet::BuildPartyParams, Return = Result<maia_core::PartyParams>>
106110
+ Handler<wallet::Sign, Return = Result<PartiallySignedTransaction>>
107111
+ Handler<wallet::Withdraw, Return = Result<Txid>>
112+
+ Handler<wallet::ImportSeed, Return = Result<bdk::wallet::AddressInfo>>
108113
+ Handler<wallet::Sync, Return = ()>
109114
+ Actor<Stop = ()>,
110115
P: Handler<
@@ -333,7 +338,7 @@ where
333338
let close_cfds_actor = archive_closed_cfds::Actor::new(db.clone())
334339
.create(None)
335340
.spawn(&mut tasks);
336-
let archive_failed_cfds_actor = archive_failed_cfds::Actor::new(db)
341+
let archive_failed_cfds_actor = archive_failed_cfds::Actor::new(db.clone())
337342
.create(None)
338343
.spawn(&mut tasks);
339344

@@ -354,6 +359,7 @@ where
354359
_online_status_actor: online_status_actor,
355360
_pong_actor: pong_address,
356361
_identify_dialer_actor: identify_dialer_actor,
362+
db,
357363
})
358364
}
359365

@@ -445,6 +451,36 @@ where
445451
self.wallet_actor.send(wallet::Sync).await?;
446452
Ok(())
447453
}
454+
455+
#[instrument(skip(self, seed), err)]
456+
pub async fn import_seed(
457+
&self,
458+
seed: Vec<u8>,
459+
data_dir: PathBuf,
460+
network: Network,
461+
name: &str,
462+
) -> Result<()> {
463+
let open_cfd_ids = self.db.load_open_cfd_ids().await?;
464+
465+
if !open_cfd_ids.is_empty() {
466+
use anyhow::bail;
467+
bail!("Cannot accept imported seed since open CFDs exist");
468+
}
469+
470+
// recreate the wallet within the wallet actor
471+
self.wallet_actor
472+
.send(wallet::ImportSeed {
473+
seed,
474+
path: data_dir,
475+
network,
476+
name: name.to_string(),
477+
})
478+
.await??;
479+
480+
self.wallet_actor.send(wallet::Sync).await?;
481+
482+
Ok(())
483+
}
448484
}
449485

450486
/// A struct defining our environment

crates/daemon/src/seed.rs

+28-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ use std::fmt;
1414
use std::fmt::Debug;
1515
use std::path::Path;
1616

17+
pub const TAKER_WALLET_SEED_FILE: &str = "taker_seed";
18+
pub const TAKER_IDENTITY_SEED_FILE: &str = "taker_id_seed";
19+
20+
pub const MAKER_WALLET_SEED_FILE: &str = "maker_seed";
21+
pub const MAKER_IDENTITY_SEED_FILE: &str = "maker_id_seed";
22+
23+
pub const RANDOM_SEED_SIZE: usize = 256;
24+
pub const APP_SEED_SIZE: usize = 32;
25+
1726
/// Struct containing keys for both legacy and libp2p connections.
1827
///
1928
/// It is located here as all the information is derived from the seed.
@@ -33,6 +42,8 @@ impl Identities {
3342
pub trait Seed {
3443
fn seed(&self) -> Vec<u8>;
3544

45+
fn is_managed(&self) -> bool;
46+
3647
fn derive_extended_priv_key(&self, network: Network) -> Result<ExtendedPrivKey> {
3748
let mut ext_priv_key_seed = [0u8; 64];
3849

@@ -82,12 +93,15 @@ pub trait Seed {
8293
}
8394

8495
#[derive(Copy, Clone)]
85-
pub struct RandomSeed([u8; 256]);
96+
pub struct RandomSeed([u8; RANDOM_SEED_SIZE]);
8697

8798
impl Seed for RandomSeed {
8899
fn seed(&self) -> Vec<u8> {
89100
self.0.to_vec()
90101
}
102+
fn is_managed(&self) -> bool {
103+
true
104+
}
91105
}
92106

93107
impl Debug for RandomSeed {
@@ -133,26 +147,35 @@ impl RandomSeed {
133147
}
134148
}
135149

150+
impl From<[u8; RANDOM_SEED_SIZE]> for RandomSeed {
151+
fn from(bytes: [u8; RANDOM_SEED_SIZE]) -> Self {
152+
Self(bytes)
153+
}
154+
}
155+
136156
impl Default for RandomSeed {
137157
fn default() -> Self {
138-
let mut seed = [0u8; 256];
158+
let mut seed = [0u8; RANDOM_SEED_SIZE];
139159
rand::thread_rng().fill(&mut seed);
140160

141161
Self(seed)
142162
}
143163
}
144164

145165
#[derive(Copy, Clone)]
146-
pub struct AppSeed([u8; 32]);
166+
pub struct AppSeed([u8; APP_SEED_SIZE]);
147167

148168
impl Seed for AppSeed {
149169
fn seed(&self) -> Vec<u8> {
150170
self.0.to_vec()
151171
}
172+
fn is_managed(&self) -> bool {
173+
false
174+
}
152175
}
153176

154-
impl From<[u8; 32]> for AppSeed {
155-
fn from(bytes: [u8; 32]) -> Self {
177+
impl From<[u8; APP_SEED_SIZE]> for AppSeed {
178+
fn from(bytes: [u8; APP_SEED_SIZE]) -> Self {
156179
Self(bytes)
157180
}
158181
}

0 commit comments

Comments
 (0)