Skip to content

Commit 1d94b39

Browse files
authored
Merge pull request #178 from frostsnap/key-recovery-2
Basic wallet recovery
2 parents ec3552c + d9c3f65 commit 1d94b39

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2921
-1228
lines changed

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ rand_core = "0.6"
3434
futures = "0.3.31"
3535

3636
[patch.crates-io]
37-
schnorr_fun = { git = "https://github.com/LLFourn/secp256kfun.git", rev = "029bfab341132e9e29583b8876377c8c8d5279cf" }
37+
schnorr_fun = { git = "https://github.com/LLFourn/secp256kfun.git", rev = "8132fbe806d9f306a9fb646f2147e5971bfea88d" }
3838

3939
[profile.dev]
4040
# Rust debug is too slow.

device/src/esp32_run.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ where
548548
SignTask::BitcoinTransaction(transaction) => {
549549
let network = signer
550550
.wallet_network(master_appkey.key_id())
551-
.unwrap_or(bitcoin::Network::Bitcoin);
551+
.expect("asked to sign a bitcoin transaction that doesn't support bitcoin");
552552

553553
let fee = transaction.fee().expect("transaction validity should have already been checked");
554554
let foreign_recipients = transaction

frostsnap_comms/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ edition = "2021"
66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

88
[dependencies]
9-
frostsnap_core = { workspace = true }
9+
frostsnap_core = { workspace = true }
1010
serde = { workspace = true }
1111
bincode = { workspace = true }
1212

1313
[features]
1414
std = []
15-
default = ["std"]
15+
coordinator = ["std", "frostsnap_core/coordinator"]
16+
default = ["coordinator"]

frostsnap_comms/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use alloc::vec::Vec;
1010
use alloc::{collections::BTreeSet, string::String};
1111
use bincode::{de::read::Reader, enc::write::Writer, Decode, Encode};
1212
use core::marker::PhantomData;
13-
use frostsnap_core::message::CoordinatorSend;
1413
use frostsnap_core::{DeviceId, Gist};
1514

1615
pub const BAUDRATE: u32 = 14_400;
@@ -64,12 +63,13 @@ pub struct CoordinatorSendMessage {
6463
pub message_body: CoordinatorSendBody,
6564
}
6665

67-
impl TryFrom<CoordinatorSend> for CoordinatorSendMessage {
66+
#[cfg(feature = "coordinator")]
67+
impl TryFrom<frostsnap_core::coordinator::CoordinatorSend> for CoordinatorSendMessage {
6868
type Error = &'static str;
6969

70-
fn try_from(value: CoordinatorSend) -> Result<Self, Self::Error> {
70+
fn try_from(value: frostsnap_core::coordinator::CoordinatorSend) -> Result<Self, Self::Error> {
7171
match value {
72-
CoordinatorSend::ToDevice {
72+
frostsnap_core::coordinator::CoordinatorSend::ToDevice {
7373
message,
7474
destinations,
7575
} => Ok(CoordinatorSendMessage {

frostsnap_coordinator/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ edition = "2021"
77

88
[dependencies]
99
anyhow = "1"
10-
frostsnap_comms = { workspace = true }
11-
frostsnap_core = { workspace = true, features = ["std", "rusqlite"] }
10+
frostsnap_comms = { workspace = true, features = ["coordinator"] }
11+
frostsnap_core = { workspace = true, features = ["coordinator", "rusqlite"] }
1212
bincode = { workspace = true, features = ["std"] }
1313
tracing = { workspace = true }
1414
serialport = { version = "4.6", default-features = false } # default-features = false so no libudev

frostsnap_coordinator/src/bitcoin/chain_sync.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use crate::Sink;
3232

3333
use super::{
3434
descriptor_for_account_keychain,
35-
wallet::{FrostsnapWallet, KeychainId},
35+
wallet::{CoordSuperWallet, KeychainId},
3636
};
3737

3838
pub struct ReqAndResponse<I, O> {
@@ -159,17 +159,17 @@ impl ConnectionHandler {
159159
pub fn run(
160160
self,
161161
url: String,
162-
wallet: Arc<std::sync::Mutex<FrostsnapWallet>>,
162+
super_wallet: Arc<std::sync::Mutex<CoordSuperWallet>>,
163163
mut update_action: impl FnMut(MasterAppkey, Vec<crate::bitcoin::wallet::Transaction>)
164164
+ Send
165165
+ 'static,
166166
) {
167-
let wallet_ = wallet.lock().unwrap();
168-
let lookahead = wallet_.lookahead();
167+
let super_wallet_ = super_wallet.lock().unwrap();
168+
let lookahead = super_wallet_.lookahead();
169169
let (mut emitter, cmd_sender, mut update_recv) =
170-
Emitter::<KeychainId>::new(wallet_.chain_tip(), lookahead);
171-
emitter.insert_txs(wallet_.tx_cache());
172-
drop(wallet_);
170+
Emitter::<KeychainId>::new(super_wallet_.chain_tip(), lookahead);
171+
emitter.insert_txs(super_wallet_.tx_cache());
172+
drop(super_wallet_);
173173
let target_server = Arc::new(std::sync::Mutex::new(TargetServer { url, conn: None }));
174174
let status_sink = Arc::new(std::sync::Mutex::<Box<dyn Sink<ChainStatus>>>::new(
175175
Box::new(()),
@@ -302,7 +302,7 @@ impl ConnectionHandler {
302302
.keys()
303303
.map(|(k, _)| *k)
304304
.collect::<Vec<_>>();
305-
let mut wallet = wallet.lock().unwrap();
305+
let mut wallet = super_wallet.lock().unwrap();
306306
let changed = match wallet.apply_update(update) {
307307
Ok(changed) => changed,
308308
Err(err) => {

frostsnap_coordinator/src/bitcoin/wallet.rs

Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::{chain_sync::ChainClient, multi_x_descriptor_for_account};
22
use crate::persist::Persisted;
33
use anyhow::{anyhow, Context, Result};
44
use bdk_chain::{
5-
bitcoin::{self, bip32, Amount, SignedAmount, Txid},
5+
bitcoin::{self, bip32, Amount, Script, SignedAmount, Txid},
66
indexed_tx_graph,
77
indexer::keychain_txout::{self, KeychainTxOutIndex},
88
local_chain,
@@ -31,16 +31,16 @@ pub type WalletIndexedTxGraph =
3131
pub type WalletIndexedTxGraphChangeSet =
3232
indexed_tx_graph::ChangeSet<ConfirmationBlockTime, keychain_txout::ChangeSet>;
3333

34-
/// Pretty much a generic bitcoin wallet that indexes everything by key id
35-
pub struct FrostsnapWallet {
34+
/// Wallet that manages all the frostsnap keys on the same network in a single transaction graph
35+
pub struct CoordSuperWallet {
3636
tx_graph: Persisted<WalletIndexedTxGraph>,
3737
chain: Persisted<local_chain::LocalChain>,
3838
chain_client: ChainClient,
3939
pub network: bitcoin::Network,
4040
db: Arc<Mutex<rusqlite::Connection>>,
4141
}
4242

43-
impl FrostsnapWallet {
43+
impl CoordSuperWallet {
4444
pub fn load_or_init(
4545
db: Arc<Mutex<rusqlite::Connection>>,
4646
network: bitcoin::Network,
@@ -138,43 +138,51 @@ impl FrostsnapWallet {
138138

139139
pub fn list_addresses(&mut self, master_appkey: MasterAppkey) -> Vec<AddressInfo> {
140140
self.lazily_initialize_key(master_appkey);
141+
let keychain = BitcoinAccountKeychain::external();
141142
self.tx_graph
142143
.index
143-
.revealed_keychain_spks((master_appkey, BitcoinAccountKeychain::external()))
144+
.revealed_keychain_spks((master_appkey, keychain))
144145
.rev()
145-
.map(|(i, spk)| AddressInfo {
146-
index: i,
147-
address: bitcoin::Address::from_script(&spk, self.network)
148-
.expect("has address form"),
149-
external: true,
150-
used: self
151-
.tx_graph
152-
.index
153-
.is_used((master_appkey, BitcoinAccountKeychain::external()), i),
154-
})
146+
.map(|(i, spk)| self.address_info(master_appkey, &spk, i, keychain))
155147
.collect()
156148
}
157149

150+
fn address_info(
151+
&self,
152+
master_appkey: MasterAppkey,
153+
spk: &Script,
154+
index: u32,
155+
keychain: BitcoinAccountKeychain,
156+
) -> AddressInfo {
157+
AddressInfo {
158+
index,
159+
address: bitcoin::Address::from_script(spk, self.network).expect("has address form"),
160+
external: true,
161+
used: self
162+
.tx_graph
163+
.index
164+
.is_used((master_appkey, keychain), index),
165+
derivation_path: BitcoinBip32Path {
166+
account_keychain: keychain,
167+
index,
168+
}
169+
.path_segments_from_bitcoin_appkey()
170+
.collect(),
171+
}
172+
}
173+
158174
pub fn next_address(&mut self, master_appkey: MasterAppkey) -> Result<AddressInfo> {
159175
self.lazily_initialize_key(master_appkey);
160-
176+
let keychain = BitcoinAccountKeychain::external();
161177
let mut db = self.db.lock().unwrap();
162178
let (index, spk) = self.tx_graph.mutate(&mut *db, |tx_graph| {
163179
tx_graph
164180
.index
165-
.reveal_next_spk((master_appkey, BitcoinAccountKeychain::external()))
181+
.reveal_next_spk((master_appkey, keychain))
166182
.ok_or(anyhow!("no more addresses on this keychain"))
167183
})?;
168184

169-
Ok(AddressInfo {
170-
index,
171-
address: bitcoin::Address::from_script(&spk, self.network).expect("has address form"),
172-
external: true,
173-
used: self
174-
.tx_graph
175-
.index
176-
.is_used((master_appkey, BitcoinAccountKeychain::external()), index),
177-
})
185+
Ok(self.address_info(master_appkey, &spk, index, keychain))
178186
}
179187

180188
pub fn search_for_address(
@@ -203,23 +211,15 @@ impl FrostsnapWallet {
203211
let address = derived.address(self.network).ok()?;
204212
if address == target_address {
205213
let spk = derived.script_pubkey();
206-
let address = bitcoin::Address::from_script(&spk, self.network).unwrap();
207-
// bit hacky but we assume first descriptor is external elsewhere
214+
// FIXME: this should get the derivation path from the descriptor itself
208215
let external = account_descriptors[0] == *descriptor;
209216
let keychain = if external {
210217
BitcoinAccountKeychain::external()
211218
} else {
212219
BitcoinAccountKeychain::internal()
213220
};
214-
// there's a good chance this is not synced if the address is far out
215-
let used = self.tx_graph.index.is_used((master_appkey, keychain), i);
216-
217-
Some(AddressInfo {
218-
index: i,
219-
address,
220-
external,
221-
used,
222-
})
221+
222+
Some(self.address_info(master_appkey, &spk, i, keychain))
223223
} else {
224224
None
225225
}
@@ -611,6 +611,7 @@ pub struct AddressInfo {
611611
pub address: bitcoin::Address,
612612
pub external: bool,
613613
pub used: bool,
614+
pub derivation_path: Vec<u32>,
614615
}
615616

616617
#[derive(Clone, Debug)]
@@ -637,7 +638,7 @@ mod test {
637638
let master_appkey =
638639
MasterAppkey::derive_from_rootkey(Point::random(&mut rand::thread_rng()));
639640
let descriptors =
640-
FrostsnapWallet::descriptors_for_key(master_appkey, bitcoin::NetworkKind::Main);
641+
CoordSuperWallet::descriptors_for_key(master_appkey, bitcoin::NetworkKind::Main);
641642

642643
let (account_keychain, external_descriptor) = &descriptors[0];
643644
let xonly = AppTweak::Bitcoin(BitcoinBip32Path {

frostsnap_coordinator/src/check_share.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ use std::borrow::BorrowMut;
22

33
use frostsnap_comms::CoordinatorSendMessage;
44
use frostsnap_core::{
5-
coordinator::{AccessStructureRef, FrostCoordinator},
6-
message::CoordinatorToUserMessage,
7-
DeviceId, SymmetricKey,
5+
coordinator::FrostCoordinator, message::CoordinatorToUserMessage, AccessStructureRef, DeviceId,
6+
SymmetricKey,
87
};
98
use tracing::{event, Level};
109

frostsnap_coordinator/src/display_backup.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use frostsnap_comms::CoordinatorSendMessage;
22
use frostsnap_core::{
3-
coordinator::{AccessStructureRef, FrostCoordinator},
4-
message::CoordinatorToUserMessage,
5-
DeviceId, SymmetricKey,
3+
coordinator::FrostCoordinator, message::CoordinatorToUserMessage, AccessStructureRef, DeviceId,
4+
SymmetricKey,
65
};
76

87
use crate::{Completion, Sink, UiProtocol, UiToStorageMessage};

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy