Skip to content

Commit e33e007

Browse files
committed
feat!: introduce from_changeset for Wallet
Also refactored corresponding function for `KeyRing` and associated error types.
1 parent 4177147 commit e33e007

File tree

5 files changed

+340
-140
lines changed

5 files changed

+340
-140
lines changed

src/keyring/error.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
use crate::descriptor::{DescriptorError, ExtendedDescriptor};
2+
use core::fmt;
3+
4+
/// Error when loading a `KeyRing`.
5+
#[derive(Debug, PartialEq)]
6+
pub enum LoadError<K> {
7+
/// There was a problem with the passed-in descriptor(s).
8+
Descriptor(crate::descriptor::DescriptorError),
9+
/// Data is missing the network.
10+
MissingNetwork,
11+
/// Data is not as expected.
12+
Mismatch(LoadMismatch<K>),
13+
/// The default keychain is missing.
14+
MissingDefaultKeychain,
15+
/// The keychain is missing,
16+
MissingKeychain(K),
17+
}
18+
19+
/// A mismatch while loading the [`KeyRing`] from a [`ChangeSet`]
20+
///
21+
/// [`KeyRing`]: crate::keyring::KeyRing
22+
/// [`ChangeSet`]: crate::keyring::ChangeSet
23+
#[derive(Debug, PartialEq)]
24+
pub enum LoadMismatch<K> {
25+
/// Network does not match.
26+
Network {
27+
/// The network that is loaded.
28+
loaded: bitcoin::Network,
29+
/// The expected network.
30+
expected: bitcoin::Network,
31+
},
32+
/// Descriptor does not match for the `keychain`.
33+
Descriptor {
34+
/// Keychain identifying the descriptor
35+
keychain: K,
36+
/// The loaded descriptor
37+
loaded: ExtendedDescriptor,
38+
/// The expected descriptor
39+
expected: ExtendedDescriptor,
40+
},
41+
/// The default keychain is not as expected
42+
DefaultKeychain {
43+
/// The loaded default keychain
44+
loaded: K,
45+
/// The expected default keychain
46+
expected: K,
47+
},
48+
}
49+
50+
impl<K> fmt::Display for LoadError<K>
51+
where
52+
K: fmt::Display,
53+
{
54+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55+
match self {
56+
Self::Descriptor(e) => e.fmt(f),
57+
Self::MissingNetwork => write!(f, "network is missing"),
58+
Self::MissingDefaultKeychain => write!(f, "default keychain is missing"),
59+
Self::Mismatch(e) => e.fmt(f),
60+
Self::MissingKeychain(keychain) => write!(f, "keychain {keychain} is missing"),
61+
}
62+
}
63+
}
64+
65+
impl<K> fmt::Display for LoadMismatch<K>
66+
where
67+
K: fmt::Display,
68+
{
69+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70+
match self {
71+
Self::Network { loaded, expected } => {
72+
write!(f, "Network mismatch: loaded {loaded}, expected {expected}")
73+
}
74+
Self::Descriptor {
75+
keychain,
76+
loaded,
77+
expected,
78+
} => write!(
79+
f,
80+
"Descriptor mismatch for {} keychain: loaded {}, expected {}",
81+
keychain, loaded, expected
82+
),
83+
Self::DefaultKeychain { loaded, expected } => write!(
84+
f,
85+
"Loaded: {loaded} as default keychain though expected: {expected}"
86+
),
87+
}
88+
}
89+
}
90+
91+
#[cfg(feature = "std")]
92+
impl<K> std::error::Error for LoadError<K> where K: fmt::Debug + fmt::Display {}
93+
94+
impl<K> From<LoadMismatch<K>> for LoadError<K> {
95+
fn from(mismatch: LoadMismatch<K>) -> Self {
96+
Self::Mismatch(mismatch)
97+
}
98+
}
99+
100+
impl<K> From<DescriptorError> for LoadError<K> {
101+
fn from(desc_error: DescriptorError) -> Self {
102+
Self::Descriptor(desc_error)
103+
}
104+
}

src/keyring/mod.rs

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,16 @@
1515
1616
/// Contains `Changeset` corresponding to `KeyRing`.
1717
pub mod changeset;
18+
/// Contains error types corresponding to `KeyRing`.
19+
pub mod error;
1820

21+
pub use changeset::ChangeSet;
22+
pub use error::{LoadError, LoadMismatch};
23+
24+
use crate::chain::{DescriptorExt, Merge};
1925
use crate::descriptor::check_wallet_descriptor;
2026
use crate::descriptor::{DescriptorError, IntoWalletDescriptor};
21-
use crate::keyring::changeset::ChangeSet;
27+
use crate::wallet::DescriptorToExtract;
2228
use alloc::collections::BTreeMap;
2329
use bitcoin::secp256k1::{All, Secp256k1};
2430
use bitcoin::Network;
@@ -124,13 +130,78 @@ where
124130
}
125131
}
126132

127-
/// Construct from changeset.
128-
pub fn from_changeset(changeset: ChangeSet<K>) -> Option<Self> {
129-
Some(Self {
133+
/// Construct `KeyRing` from changeset.
134+
pub fn from_changeset(
135+
changeset: ChangeSet<K>,
136+
check_network: Option<bitcoin::Network>,
137+
check_descs: BTreeMap<K, Option<DescriptorToExtract>>, /* none means just check if
138+
* keychain is there. */
139+
check_default: Option<K>,
140+
) -> Result<Option<Self>, LoadError<K>> {
141+
if changeset.is_empty() {
142+
return Ok(None);
143+
}
144+
let secp = Secp256k1::new();
145+
146+
// check network is present
147+
let loaded_network = changeset.network.ok_or(LoadError::MissingNetwork)?;
148+
149+
// check network is as expected
150+
if let Some(expected_network) = check_network {
151+
if loaded_network != expected_network {
152+
return Err(LoadError::Mismatch(LoadMismatch::Network {
153+
loaded: loaded_network,
154+
expected: expected_network,
155+
}));
156+
}
157+
}
158+
159+
// check the descriptors are valid
160+
for desc in changeset.descriptors.values() {
161+
check_wallet_descriptor(desc)?;
162+
}
163+
164+
// check expected descriptors are present
165+
for (keychain, check_desc) in check_descs {
166+
match changeset.descriptors.get(&keychain) {
167+
None => Err(LoadError::MissingKeychain(keychain))?,
168+
Some(loaded_desc) => {
169+
if let Some(make_desc) = check_desc {
170+
let (exp_desc, _) = make_desc(&secp, loaded_network)?;
171+
if exp_desc.descriptor_id() != loaded_desc.descriptor_id() {
172+
Err(LoadMismatch::Descriptor {
173+
keychain,
174+
loaded: loaded_desc.clone(),
175+
expected: exp_desc,
176+
})?
177+
}
178+
}
179+
}
180+
}
181+
}
182+
183+
// check if default keychain is present and loaded correctly.
184+
match &changeset.default_keychain {
185+
None => Err(LoadError::MissingDefaultKeychain)?,
186+
Some(loaded) => {
187+
if let Some(expected) = check_default {
188+
if *loaded != expected {
189+
Err(LoadMismatch::DefaultKeychain {
190+
loaded: loaded.clone(),
191+
expected,
192+
})?
193+
}
194+
}
195+
}
196+
}
197+
198+
Ok(Some(Self {
130199
secp: Secp256k1::new(),
131-
network: changeset.network?,
200+
network: loaded_network,
132201
descriptors: changeset.descriptors,
133-
default_keychain: changeset.default_keychain?,
134-
})
202+
default_keychain: changeset
203+
.default_keychain
204+
.expect("checked few lines back that default_keychain is not None "),
205+
}))
135206
}
136207
}

0 commit comments

Comments
 (0)