Skip to content

Commit 81a59e5

Browse files
committed
Merge branch 'policy-derive'
2 parents 50b05b6 + b9de948 commit 81a59e5

File tree

3 files changed

+58
-36
lines changed

3 files changed

+58
-36
lines changed

src/rust/bitbox02-rust/src/hww/api/bitcoin/common.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,10 @@ impl Payload {
155155
policy: &super::policies::ParsedPolicy,
156156
keypath: &[u32],
157157
) -> Result<Self, Error> {
158-
let witness_script = policy.witness_script_at_keypath(keypath)?;
159-
match &policy.descriptor {
160-
super::policies::Descriptor::Wsh { .. } => Ok(Payload {
161-
data: Sha256::digest(witness_script).to_vec(),
158+
let derived_descriptor = policy.derive_at_keypath(keypath)?;
159+
match derived_descriptor {
160+
super::policies::Descriptor::Wsh(wsh) => Ok(Payload {
161+
data: Sha256::digest(wsh.witness_script()).to_vec(),
162162
output_type: BtcOutputType::P2wsh,
163163
}),
164164
}

src/rust/bitbox02-rust/src/hww/api/bitcoin/policies.rs

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -215,23 +215,32 @@ impl<'a> miniscript::Translator<String, bitcoin::PublicKey, Error>
215215

216216
/// See `ParsedPolicy`.
217217
#[derive(Debug)]
218-
pub struct Wsh {
219-
miniscript_expr: miniscript::Miniscript<String, miniscript::Segwitv0>,
218+
pub struct Wsh<T: miniscript::MiniscriptKey> {
219+
miniscript_expr: miniscript::Miniscript<T, miniscript::Segwitv0>,
220+
}
221+
222+
impl Wsh<bitcoin::PublicKey> {
223+
/// Return the witness script of this concrete wsh() descriptor.
224+
pub fn witness_script(&self) -> Vec<u8> {
225+
self.miniscript_expr.encode().as_bytes().to_vec()
226+
}
220227
}
221228

222229
/// See `ParsedPolicy`.
223230
#[derive(Debug)]
224-
pub enum Descriptor {
231+
pub enum Descriptor<T: miniscript::MiniscriptKey> {
225232
// `wsh(...)` policies
226-
Wsh(Wsh),
233+
Wsh(Wsh<T>),
227234
// `tr(...)` Taproot etc. in the future.
228235
}
229236

230237
/// Result of `parse()`.
231238
#[derive(Debug)]
232239
pub struct ParsedPolicy<'a> {
233240
policy: &'a Policy,
234-
pub descriptor: Descriptor,
241+
// String for pubkeys so we can parse and process the placeholder wallet policy keys like
242+
// `@0/**` etc.
243+
pub descriptor: Descriptor<String>,
235244
}
236245

237246
impl<'a> ParsedPolicy<'a> {
@@ -294,43 +303,52 @@ impl<'a> ParsedPolicy<'a> {
294303
Ok(())
295304
}
296305

297-
/// Derive the witness script of the policy derived at a receive or change path.
298-
/// If is_change is false, the witness script for the receive address is derived.
299-
/// If is_change is true, the witness script for the change address is derived.
306+
/// Derive the descriptor of the policy at a receive or change path.
307+
/// This turns key placeholders into actual pubkeys.
308+
/// If is_change is false, the descriptor for the receive address is derived.
309+
/// If is_change is true, the descriptor for the change address is derived.
300310
/// Example: wsh(and_v(v:pk(@0/**),pk(@1/<20;21>/*))) derived using `is_change=false, address_index=5` derives
301311
/// wsh(and_v(v:pk(@0/0/5),pk(@1/20/5))).
302312
/// The same derived using `is_change=true` derives: wsh(and_v(v:pk(@0/1/5),pk(@1/21/5)))
303-
pub fn witness_script(&self, is_change: bool, address_index: u32) -> Result<Vec<u8>, Error> {
313+
pub fn derive(
314+
&self,
315+
is_change: bool,
316+
address_index: u32,
317+
) -> Result<Descriptor<bitcoin::PublicKey>, Error> {
318+
let mut translator = WalletPolicyPkTranslator {
319+
keys: self.policy.keys.as_ref(),
320+
is_change,
321+
address_index,
322+
};
304323
match &self.descriptor {
305324
Descriptor::Wsh(Wsh { miniscript_expr }) => {
306-
let mut translator = WalletPolicyPkTranslator {
307-
keys: self.policy.keys.as_ref(),
308-
is_change,
309-
address_index,
310-
};
311325
let miniscript_expr = match miniscript_expr.translate_pk(&mut translator) {
312326
Ok(m) => m,
313327
Err(miniscript::TranslateErr::TranslatorErr(e)) => return Err(e),
314328
Err(miniscript::TranslateErr::OuterError(_)) => return Err(Error::Generic),
315329
};
316-
Ok(miniscript_expr.encode().as_bytes().to_vec())
330+
Ok(Descriptor::Wsh(Wsh { miniscript_expr }))
317331
}
318332
}
319333
}
320334

321-
/// Derive the witness script of the policy derived at the given full keypath.
335+
/// Derive the descriptor of the policy derived at the given full keypath.
336+
/// This turns key placeholders into actual pubkeys.
322337
/// Example: wsh(and_v(v:pk(@0/<10;11>/*),pk(@1/<20;21>/*))) with our key [fp/48'/1'/0'/3']xpub...]
323338
/// derived using keypath m/48'/1'/0'/3'/11/5 derives:
324339
/// wsh(and_v(v:pk(@0/11/5),pk(@1/21/5))).
325-
pub fn witness_script_at_keypath(&self, keypath: &[u32]) -> Result<Vec<u8>, Error> {
340+
pub fn derive_at_keypath(
341+
&self,
342+
keypath: &[u32],
343+
) -> Result<Descriptor<bitcoin::PublicKey>, Error> {
326344
match &self.descriptor {
327345
Descriptor::Wsh(Wsh { miniscript_expr }) => {
328346
let (is_change, address_index) = get_change_and_address_index(
329347
miniscript_expr.iter_pk(),
330348
&self.policy.keys,
331349
keypath,
332350
)?;
333-
self.witness_script(is_change, address_index)
351+
self.derive(is_change, address_index)
334352
}
335353
}
336354
}
@@ -899,7 +917,7 @@ mod tests {
899917
}
900918

901919
#[test]
902-
fn test_witness_script() {
920+
fn test_wsh_witness_script() {
903921
mock_unlocked_using_mnemonic(
904922
"sudden tenant fault inject concert weather maid people chunk youth stumble grit",
905923
"",
@@ -913,20 +931,22 @@ mod tests {
913931
let address_index = 5;
914932

915933
let witness_script = |pol: &str, keys: &[pb::KeyOriginInfo], is_change: bool| {
916-
hex::encode(
917-
parse(&make_policy(pol, keys))
918-
.unwrap()
919-
.witness_script(is_change, address_index)
920-
.unwrap(),
921-
)
934+
let derived = parse(&make_policy(pol, keys))
935+
.unwrap()
936+
.derive(is_change, address_index)
937+
.unwrap();
938+
match derived {
939+
Descriptor::Wsh(wsh) => hex::encode(wsh.witness_script()),
940+
}
922941
};
923942
let witness_script_at_keypath = |pol: &str, keys: &[pb::KeyOriginInfo], keypath: &[u32]| {
924-
hex::encode(
925-
parse(&make_policy(pol, keys))
926-
.unwrap()
927-
.witness_script_at_keypath(keypath)
928-
.unwrap(),
929-
)
943+
let derived = parse(&make_policy(pol, keys))
944+
.unwrap()
945+
.derive_at_keypath(keypath)
946+
.unwrap();
947+
match derived {
948+
Descriptor::Wsh(wsh) => hex::encode(wsh.witness_script()),
949+
}
930950
};
931951

932952
// pk(key) => <key> OP_CHECKSIG

src/rust/bitbox02-rust/src/hww/api/bitcoin/signtx.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,9 @@ fn sighash_script(
293293
ValidatedScriptConfigWithKeypath {
294294
config: ValidatedScriptConfig::Policy(policy),
295295
..
296-
} => policy.witness_script_at_keypath(keypath),
296+
} => match policy.derive_at_keypath(keypath)? {
297+
super::policies::Descriptor::Wsh(wsh) => Ok(wsh.witness_script()),
298+
},
297299
}
298300
}
299301

0 commit comments

Comments
 (0)