-
Notifications
You must be signed in to change notification settings - Fork 25
/
descriptor.rs
215 lines (184 loc) · 6.6 KB
/
descriptor.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
use std::str::FromStr;
use elements::bitcoin::bip32::{DerivationPath, KeySource, Xpub};
use elements::hex::ToHex;
use elements_miniscript::descriptor::checksum::desc_checksum;
use rand::{thread_rng, Rng};
use thiserror::Error;
use crate::Signer;
// TODO impl error handling
pub fn singlesig_desc<S: Signer>(
signer: &S,
script_variant: Singlesig,
blinding_variant: DescriptorBlindingKey,
is_mainnet: bool,
) -> Result<String, String> {
let coin_type = if is_mainnet { 1776 } else { 1 };
let (prefix, path, suffix) = match script_variant {
Singlesig::Wpkh => ("elwpkh", format!("84h/{coin_type}h/0h"), ""),
Singlesig::ShWpkh => ("elsh(wpkh", format!("49h/{coin_type}h/0h"), ")"),
};
let master = signer
.derive_xpub(&DerivationPath::master())
.map_err(|e| format!("{:?}", e))?;
let fingerprint = master.fingerprint();
let xpub = signer
.derive_xpub(
&DerivationPath::from_str(&format!("m/{path}")).map_err(|e| format!("{:?}", e))?,
)
.map_err(|e| format!("{:?}", e))?;
let blinding_key = match blinding_variant {
DescriptorBlindingKey::Slip77 => format!(
"slip77({})",
signer
.slip77_master_blinding_key()
.map_err(|e| format!("{:?}", e))?
),
DescriptorBlindingKey::Slip77Rand => {
return Err("Random slip77 key not supported in singlesig descriptor generation".into())
}
DescriptorBlindingKey::Elip151 => "elip151".to_string(),
};
// m / purpose' / coin_type' / account' / change / address_index
let desc = format!("ct({blinding_key},{prefix}([{fingerprint}/{path}]{xpub}/<0;1>/*){suffix})");
let checksum = desc_checksum(&desc).map_err(|e| format!("{:?}", e))?;
Ok(format!("{desc}#{checksum}"))
}
fn fmt_path(path: &DerivationPath) -> String {
path.to_string().replace("m/", "").replace('\'', "h")
}
// TODO impl error handling
pub fn multisig_desc(
threshold: u32,
xpubs: Vec<(Option<KeySource>, Xpub)>,
script_variant: Multisig,
blinding_variant: DescriptorBlindingKey,
) -> Result<String, String> {
if threshold == 0 {
return Err("Threshold cannot be 0".into());
} else if threshold as usize > xpubs.len() {
return Err("Threshold cannot be greater than the number of xpubs".into());
}
let (prefix, suffix) = match script_variant {
Multisig::Wsh => ("elwsh(multi", ")"),
};
let blinding_key = match blinding_variant {
DescriptorBlindingKey::Slip77 => {
return Err(
"Deterministic slip77 key not supported in multisig descriptor generation".into(),
)
}
DescriptorBlindingKey::Slip77Rand => {
let mut bytes = [0u8; 32];
thread_rng().fill(&mut bytes);
format!("slip77({})", bytes.to_hex())
}
DescriptorBlindingKey::Elip151 => "elip151".to_string(),
};
let xpubs = xpubs
.iter()
.map(|(keyorigin, xpub)| {
let prefix = if let Some((fingerprint, path)) = keyorigin {
format!("[{fingerprint}/{}]", fmt_path(path))
} else {
"".to_string()
};
format!("{prefix}{xpub}/<0;1>/*")
})
.collect::<Vec<_>>()
.join(",");
let desc = format!("ct({blinding_key},{prefix}({threshold},{xpubs}){suffix})");
let checksum = desc_checksum(&desc).map_err(|e| format!("{:?}", e))?;
Ok(format!("{desc}#{checksum}"))
}
#[derive(Debug, Clone, Copy)]
pub enum Singlesig {
/// as defined by bip84
Wpkh,
/// as defined by bip49
ShWpkh,
}
#[derive(Error, Debug)]
#[error("Invalid singlesig variant '{0}' supported variant are: 'wpkh', 'shwpkh'")]
pub struct InvalidSinglesigVariant(String);
impl FromStr for Singlesig {
type Err = InvalidSinglesigVariant;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"wpkh" => Singlesig::Wpkh,
"shwpkh" => Singlesig::ShWpkh,
v => return Err(InvalidSinglesigVariant(v.to_string())),
})
}
}
/// Some descriptor blinding keys variant
///
/// Not all the possible cases described in
/// [ELIP150](<https://github.com/ElementsProject/ELIPs/blob/main/elip-0150.mediawiki>)
/// have a corresponding variant in this enum.
#[derive(Debug, Clone, Copy)]
pub enum DescriptorBlindingKey {
/// Deterministic [SLIP77](<https://github.com/satoshilabs/slips/blob/master/slip-0077.md>) master blinding key
///
/// Derived from the BIP32 seed.
Slip77,
/// Random [SLIP77](<https://github.com/satoshilabs/slips/blob/master/slip-0077.md>) master blinding key
///
/// Randomly generated SLIP77 master blinding key.
/// Useful fot cases where the seed isn't available or is not well defined (e.g. multisig).
///
/// Note that single blinding keys are derived _deterministically_ from this SLIP77 master
/// blinding key.
Slip77Rand,
/// [ELIP151](<https://github.com/ElementsProject/ELIPs/blob/main/elip-0151.mediawiki>) descriptor blinding key
///
/// Derived from the ordinary descriptor.
Elip151,
}
#[derive(Error, Debug)]
#[error("Invalid blinding key variant '{0}' supported variant are: 'slip77', 'elip151'")]
pub struct InvalidBlindingKeyVariant(String);
impl FromStr for DescriptorBlindingKey {
type Err = InvalidBlindingKeyVariant;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"slip77" => DescriptorBlindingKey::Slip77,
"slip77-rand" => DescriptorBlindingKey::Slip77Rand,
"elip151" => DescriptorBlindingKey::Elip151,
v => return Err(InvalidBlindingKeyVariant(v.to_string())),
})
}
}
pub enum Bip {
Bip84,
Bip49,
Bip87,
}
#[derive(Error, Debug)]
#[error("Invalid bip variant '{0}' supported variant are: 'bip84'")]
pub struct InvalidBipVariant(String);
impl FromStr for Bip {
type Err = InvalidBipVariant;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"bip84" => Bip::Bip84,
"bip49" => Bip::Bip49,
"bip87" => Bip::Bip87,
v => return Err(InvalidBipVariant(v.to_string())),
})
}
}
pub enum Multisig {
Wsh,
}
#[derive(Error, Debug)]
#[error("Invalid multisig variant '{0}' supported variant are: 'wsh'")]
pub struct InvalidMultisigVariant(String);
impl FromStr for Multisig {
type Err = InvalidMultisigVariant;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"wsh" => Multisig::Wsh,
v => return Err(InvalidMultisigVariant(v.to_string())),
})
}
}