Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions bitcoind-tests/tests/test_desc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,11 @@ fn test_descs(cl: &Client, testdata: &TestData) {
test_desc_satisfy(cl, testdata, "tr(X!,{pk(X1!),multi_a(3,X2,X3,X4,X5!)})").unwrap();
test_desc_satisfy(cl, testdata, "tr(X!,{pk(X1!),multi_a(4,X2,X3,X4,X5)})").unwrap();

test_desc_satisfy(cl, testdata, "tr(X!,{pk(X1!),sortedmulti_a(1,X2,X3!,X4!,X5!)})").unwrap();
test_desc_satisfy(cl, testdata, "tr(X!,{pk(X1!),sortedmulti_a(2,X2,X3,X4!,X5!)})").unwrap();
test_desc_satisfy(cl, testdata, "tr(X!,{pk(X1!),sortedmulti_a(3,X2,X3,X4,X5!)})").unwrap();
test_desc_satisfy(cl, testdata, "tr(X!,{pk(X1!),sortedmulti_a(4,X2,X3,X4,X5)})").unwrap();

// Test 7: Test script tree of depth 127 is valid, only X128 is known
test_desc_satisfy(cl, testdata, "tr(X!,{pk(X1!),{pk(X2!),{pk(X3!),{pk(X4!),{pk(X5!),{pk(X6!),{pk(X7!),{pk(X8!),{pk(X9!),{pk(X10!),{pk(X11!),{pk(X12!),{pk(X13!),{pk(X14!),{pk(X15!),{pk(X16!),{pk(X17!),{pk(X18!),{pk(X19!),{pk(X20!),{pk(X21!),{pk(X22!),{pk(X23!),{pk(X24!),{pk(X25!),{pk(X26!),{pk(X27!),{pk(X28!),{pk(X29!),{pk(X30!),{pk(X31!),{pk(X32!),{pk(X33!),{pk(X34!),{pk(X35!),{pk(X36!),{pk(X37!),{pk(X38!),{pk(X39!),{pk(X40!),{pk(X41!),{pk(X42!),{pk(X43!),{pk(X44!),{pk(X45!),{pk(X46!),{pk(X47!),{pk(X48!),{pk(X49!),{pk(X50!),{pk(X51!),{pk(X52!),{pk(X53!),{pk(X54!),{pk(X55!),{pk(X56!),{pk(X57!),{pk(X58!),{pk(X59!),{pk(X60!),{pk(X61!),{pk(X62!),{pk(X63!),{pk(X64!),{pk(X65!),{pk(X66!),{pk(X67!),{pk(X68!),{pk(X69!),{pk(X70!),{pk(X71!),{pk(X72!),{pk(X73!),{pk(X74!),{pk(X75!),{pk(X76!),{pk(X77!),{pk(X78!),{pk(X79!),{pk(X80!),{pk(X81!),{pk(X82!),{pk(X83!),{pk(X84!),{pk(X85!),{pk(X86!),{pk(X87!),{pk(X88!),{pk(X89!),{pk(X90!),{pk(X91!),{pk(X92!),{pk(X93!),{pk(X94!),{pk(X95!),{pk(X96!),{pk(X97!),{pk(X98!),{pk(X99!),{pk(X100!),{pk(X101!),{pk(X102!),{pk(X103!),{pk(X104!),{pk(X105!),{pk(X106!),{pk(X107!),{pk(X108!),{pk(X109!),{pk(X110!),{pk(X111!),{pk(X112!),{pk(X113!),{pk(X114!),{pk(X115!),{pk(X116!),{pk(X117!),{pk(X118!),{pk(X119!),{pk(X120!),{pk(X121!),{pk(X122!),{pk(X123!),{pk(X124!),{pk(X125!),{pk(X126!),{pk(X127!),pk(X128)}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}})").unwrap();

Expand Down
34 changes: 34 additions & 0 deletions examples/xpub_descriptors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ fn main() {

// P2WSH-P2SH and ranged xpubs.
let _ = p2sh_p2wsh(&secp);

// P2TR with xpubs in sortedmulti_a
let _ = p2tr_sortedmulti_a(&secp);
}

/// Parses a P2WSH descriptor, returns the associated address.
Expand Down Expand Up @@ -64,3 +67,34 @@ fn p2sh_p2wsh<C: Verification>(secp: &Secp256k1<C>) -> Address {
assert_eq!(address, expected);
address
}

/// Parses a P2TR sortedmulti_a descriptor, returns the associated address.
fn p2tr_sortedmulti_a<C: Verification>(secp: &Secp256k1<C>) -> Address {
let internal = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0";
// It does not matter what order the two xpubs go in, the same address will be generated.
let s1 = format!("tr({},sortedmulti_a(2,{}/1/0/*,{}/0/0/*))", internal, XPUB_1, XPUB_2);
let s2 = format!("tr({},sortedmulti_a(2,{}/0/0/*,{}/1/0/*))", internal, XPUB_2, XPUB_1);

let [address1, address2]: [Address; 2] = [s1, s2]
.into_iter()
.map(|s| {
Descriptor::<DescriptorPublicKey>::from_str(&s)
.unwrap()
.derived_descriptor(secp, 5)
.unwrap()
.address(Network::Bitcoin)
.unwrap()
})
.collect::<Vec<_>>()
.try_into()
.unwrap();

let expected =
Address::from_str("bc1ppfd3y5lxq4nf3tfstccz0t0hly3vmj93t7z46e52zlpt6dyf4hwqxaxnxc")
.unwrap()
.require_network(Network::Bitcoin)
.unwrap();
assert_eq!(address1, expected);
assert_eq!(address1, address2);
address1
}
11 changes: 1 addition & 10 deletions src/descriptor/sortedmulti.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
where
Pk: ToPublicKey,
{
let mut thresh = self.inner.clone();
// Sort pubkeys lexicographically according to BIP 67
thresh.data_mut().sort_by(|a, b| {
a.to_public_key()
.inner
.serialize()
.partial_cmp(&b.to_public_key().inner.serialize())
.unwrap()
});
Terminal::Multi(thresh)
Terminal::Multi(self.inner.clone().into_sorted_bip67())
}

/// Encode as a Bitcoin script
Expand Down
8 changes: 4 additions & 4 deletions src/descriptor/wallet_policy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,12 +304,12 @@ mod tests {
"sh(multi(1,@0/**,@0/<2;3>/*))",
"sh(multi(1,xpub6Bex1CHWGXNNwGVKHLqNC7kcV348FxkCxpZXyCWp1k27kin8sRPayjZUKDjyQeZzGUdyeAj2emoW5zStFFUAHRgd5w8iVVbLgZ7PmjAKAm9/<0;1>/*,xpub6Bex1CHWGXNNwGVKHLqNC7kcV348FxkCxpZXyCWp1k27kin8sRPayjZUKDjyQeZzGUdyeAj2emoW5zStFFUAHRgd5w8iVVbLgZ7PmjAKAm9/<2;3>/*))"
),
(
"tr(@0/**,{sortedmulti_a(1,@0/<2;3>/*,@1/**),or_b(pk(@2/**),s:pk(@3/**))})",
"tr([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa/<0;1>/*,{sortedmulti_a(1,[6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa/<2;3>/*,xpub6Fc2TRaCWNgfT49nRGG2G78d1dPnjhW66gEXi7oYZML7qEFN8e21b2DLDipTZZnfV6V7ivrMkvh4VbnHY2ChHTS9qM3XVLJiAgcfagYQk6K/<0;1>/*),or_b(pk(xpub6GxHB9kRdFfTqYka8tgtX9Gh3Td3A9XS8uakUGVcJ9NGZ1uLrGZrRVr67DjpMNCHprZmVmceFTY4X4wWfksy8nVwPiNvzJ5pjLxzPtpnfEM/<0;1>/*),s:pk(xpub6GjFUVVYewLj5no5uoNKCWuyWhQ1rKGvV8DgXBG9Uc6DvAKxt2dhrj1EZFrTNB5qxAoBkVW3wF8uCS3q1ri9fueAa6y7heFTcf27Q4gyeh6/<0;1>/*))})"
),
// TODO: uncomment if BIP-390 is ever supported
// (
// "tr(@0/**,{sortedmulti_a(1,@0/<2;3>/*,@1/**),or_b(pk(@2/**),s:pk(@3/**))})",
// "tr([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa/<0;1>/*,{sortedmulti_a(1,[6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa/<2;3>/*,xpub6Fc2TRaCWNgfT49nRGG2G78d1dPnjhW66gEXi7oYZML7qEFN8e21b2DLDipTZZnfV6V7ivrMkvh4VbnHY2ChHTS9qM3XVLJiAgcfagYQk6K/<0;1>/*),or_b(pk(xpub6GxHB9kRdFfTqYka8tgtX9Gh3Td3A9XS8uakUGVcJ9NGZ1uLrGZrRVr67DjpMNCHprZmVmceFTY4X4wWfksy8nVwPiNvzJ5pjLxzPtpnfEM/<0;1>/*),s:pk(xpub6GjFUVVYewLj5no5uoNKCWuyWhQ1rKGvV8DgXBG9Uc6DvAKxt2dhrj1EZFrTNB5qxAoBkVW3wF8uCS3q1ri9fueAa6y7heFTcf27Q4gyeh6/<0;1>/*))})"
// ),
// (
// "tr(musig(@0,@1,@2)/**,{and_v(v:pk(musig(@0,@1)/**),older(12960)),{and_v(v:pk(musig(@0,@2)/**),older(12960)),and_v(v:pk(musig(@1,@2)/**),older(12960))}})",
// "tr(musig([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa,[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js,[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2)/<0;1>/*,{and_v(v:pk(musig([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa,[b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js)/<0;1>/*),older(12960)),{and_v(v:pk(musig([6738736c/48'/0'/0'/100']xpub6FC1fXFP1GXQpyRFfSE1vzzySqs3Vg63bzimYLeqtNUYbzA87kMNTcuy9ubr7MmavGRjW2FRYHP4WGKjwutbf1ghgkUW9H7e3ceaPLRcVwa,[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2)/<0;1>/*),older(12960)),and_v(v:pk(musig([b2b1f0cf/44'/0'/0'/100']xpub6EYajCJHe2CK53RLVXrN14uWoEttZgrRSaRztujsXg7yRhGtHmLBt9ot9Pd5ugfwWEu6eWyJYKSshyvZFKDXiNbBcoK42KRZbxwjRQpm5Js,[a666a867/44'/0'/0'/100']xpub6Dgsze3ujLi1EiHoCtHFMS9VLS1UheVqxrHGfP7sBJ2DBfChEUHV4MDwmxAXR2ayeytpwm3zJEU3H3pjCR6q6U5sP2p2qzAD71x9z5QShK2)/<0;1>/*),older(12960))}})"
// ),
Expand Down
2 changes: 1 addition & 1 deletion src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,7 @@ where
None => return Some(Err(Error::UnexpectedStackEnd)),
}
}
Terminal::MultiA(ref thresh) => {
Terminal::MultiA(ref thresh) | Terminal::SortedMultiA(ref thresh) => {
if node_state.n_evaluated == thresh.n() {
if node_state.n_satisfied == thresh.k() {
self.stack.push(stack::Element::Satisfied);
Expand Down
9 changes: 6 additions & 3 deletions src/iter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> TreeLike for &'a Miniscript<Pk,
use Terminal::*;
match self.node {
PkK(..) | PkH(..) | RawPkH(..) | After(..) | Older(..) | Sha256(..) | Hash256(..)
| Ripemd160(..) | Hash160(..) | True | False | Multi(..) | MultiA(..) => Tree::Nullary,
| Ripemd160(..) | Hash160(..) | True | False | Multi(..) | MultiA(..)
| SortedMultiA(..) => Tree::Nullary,
Alt(ref sub)
| Swap(ref sub)
| Check(ref sub)
Expand Down Expand Up @@ -57,7 +58,8 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> TreeLike for &'a Arc<Miniscript<
use Terminal::*;
match self.node {
PkK(..) | PkH(..) | RawPkH(..) | After(..) | Older(..) | Sha256(..) | Hash256(..)
| Ripemd160(..) | Hash160(..) | True | False | Multi(..) | MultiA(..) => Tree::Nullary,
| Ripemd160(..) | Hash160(..) | True | False | Multi(..) | MultiA(..)
| SortedMultiA(..) => Tree::Nullary,
Alt(ref sub)
| Swap(ref sub)
| Check(ref sub)
Expand Down Expand Up @@ -87,7 +89,8 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> TreeLike for &'a Terminal<Pk, Ct
use Terminal::*;
match self {
PkK(..) | PkH(..) | RawPkH(..) | After(..) | Older(..) | Sha256(..) | Hash256(..)
| Ripemd160(..) | Hash160(..) | True | False | Multi(..) | MultiA(..) => Tree::Nullary,
| Ripemd160(..) | Hash160(..) | True | False | Multi(..) | MultiA(..)
| SortedMultiA(..) => Tree::Nullary,
Alt(ref sub)
| Swap(ref sub)
| Check(ref sub)
Expand Down
17 changes: 13 additions & 4 deletions src/miniscript/astelem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,21 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
.push_int(thresh.n() as i64)
.push_opcode(opcodes::all::OP_CHECKMULTISIG)
}
Terminal::MultiA(ref thresh) => {
Terminal::MultiA(ref thresh) | Terminal::SortedMultiA(ref thresh) => {
debug_assert!(Ctx::sig_type() == SigType::Schnorr);
// keys must be atleast len 1 here, guaranteed by typing rules
builder = builder.push_ms_key::<_, Ctx>(&thresh.data()[0]);
let sorted;
let mut iter = if let Terminal::SortedMultiA(thresh) = self {
sorted = thresh.clone().into_sorted_bip67_xonly();
sorted.iter()
} else {
thresh.iter()
};
builder = builder.push_ms_key::<_, Ctx>(iter.next().expect(
"multi_a keys must be at least len 1 here, guaranteed by typing rules",
));
builder = builder.push_opcode(opcodes::all::OP_CHECKSIG);
for pk in thresh.iter().skip(1) {

for pk in iter {
builder = builder.push_ms_key::<_, Ctx>(pk);
builder = builder.push_opcode(opcodes::all::OP_CHECKSIGADD);
}
Expand Down
14 changes: 10 additions & 4 deletions src/miniscript/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,9 @@ impl ScriptContext for Legacy {
}
Ok(())
}
Terminal::MultiA(..) => Err(ScriptContextError::MultiANotAllowed),
Terminal::MultiA(..) | Terminal::SortedMultiA(..) => {
Err(ScriptContextError::MultiANotAllowed)
}
_ => Ok(()),
};
// 2. After fragment and param check, validate the script size finally
Expand Down Expand Up @@ -494,7 +496,9 @@ impl ScriptContext for Segwitv0 {
}
Ok(())
}
Terminal::MultiA(..) => Err(ScriptContextError::MultiANotAllowed),
Terminal::MultiA(..) | Terminal::SortedMultiA(..) => {
Err(ScriptContextError::MultiANotAllowed)
}
_ => Ok(()),
};
// 2. After fragment and param check, validate the script size finally
Expand Down Expand Up @@ -599,7 +603,7 @@ impl ScriptContext for Tap {
// 1. Check the node first, throw an error on the language itself
let node_checked = match ms.node {
Terminal::PkK(ref pk) => Self::check_pk(pk),
Terminal::MultiA(ref thresh) => {
Terminal::MultiA(ref thresh) | Terminal::SortedMultiA(ref thresh) => {
for pk in thresh.iter() {
Self::check_pk(pk)?;
}
Expand Down Expand Up @@ -716,7 +720,9 @@ impl ScriptContext for BareCtx {
}
Ok(())
}
Terminal::MultiA(..) => Err(ScriptContextError::MultiANotAllowed),
Terminal::MultiA(..) | Terminal::SortedMultiA(..) => {
Err(ScriptContextError::MultiANotAllowed)
}
_ => Ok(()),
};
// 2. After fragment and param check, validate the script size finally
Expand Down
11 changes: 10 additions & 1 deletion src/miniscript/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ pub enum Terminal<Pk: MiniscriptKey, Ctx: ScriptContext> {
Multi(Threshold<Pk, MAX_PUBKEYS_PER_MULTISIG>),
/// `<key> CHECKSIG (<key> CHECKSIGADD)*(n-1) k NUMEQUAL`
MultiA(Threshold<Pk, MAX_PUBKEYS_IN_CHECKSIGADD>),
/// `<key> CHECKSIG (<key> CHECKSIGADD)*(n-1) k NUMEQUAL`
SortedMultiA(Threshold<Pk, MAX_PUBKEYS_IN_CHECKSIGADD>),
}

impl<Pk: MiniscriptKey, Ctx: ScriptContext> Clone for Terminal<Pk, Ctx> {
Expand Down Expand Up @@ -213,6 +215,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Clone for Terminal<Pk, Ctx> {
}
Terminal::Multi(ref thresh) => Terminal::Multi(thresh.clone()),
Terminal::MultiA(ref thresh) => Terminal::MultiA(thresh.clone()),
Terminal::SortedMultiA(ref thresh) => Terminal::SortedMultiA(thresh.clone()),
}
}
}
Expand All @@ -232,6 +235,9 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> PartialEq for Terminal<Pk, Ctx> {
(Terminal::Hash160(h1), Terminal::Hash160(h2)) if h1 != h2 => return false,
(Terminal::Multi(th1), Terminal::Multi(th2)) if th1 != th2 => return false,
(Terminal::MultiA(th1), Terminal::MultiA(th2)) if th1 != th2 => return false,
(Terminal::SortedMultiA(th1), Terminal::SortedMultiA(th2)) if th1 != th2 => {
return false
}
_ => {
if mem::discriminant(me) != mem::discriminant(you) {
return false;
Expand Down Expand Up @@ -264,7 +270,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> core::hash::Hash for Terminal<Pk, Ct
// The actual children will be hashed when we iterate
}
Terminal::Multi(th) => th.hash(hasher),
Terminal::MultiA(th) => th.hash(hasher),
Terminal::MultiA(th) | Terminal::SortedMultiA(th) => th.hash(hasher),
_ => {}
}
}
Expand Down Expand Up @@ -534,6 +540,9 @@ pub fn decode<Ctx: ScriptContext>(
term.push(Miniscript::multi(thresh));
},
// MultiA
// NOTE: We never decode into a sortedmulti_a. This may be
// done in the future if there are validation parameters
// that allow for it.
Tk::NumEqual, Tk::Num(k) => {
threshold::validate_k_n::<MAX_PUBKEYS_IN_CHECKSIGADD>(k as usize, k as usize).map_err(Error::Threshold)?;

Expand Down
4 changes: 4 additions & 0 deletions src/miniscript/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> TreeLike for DisplayNode<'a, Pk,
Terminal::MultiA(ref thresh) => {
Tree::Nary(NaryChildren::Keys(thresh.k(), thresh.data()))
}
Terminal::SortedMultiA(thresh) => {
Tree::Nary(NaryChildren::Keys(thresh.k(), thresh.data()))
}
},
// Only nodes have children; the rest are terminals.
_ => Tree::Nullary,
Expand Down Expand Up @@ -272,6 +275,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
Terminal::Thresh(..) => "thresh",
Terminal::Multi(..) => "multi",
Terminal::MultiA(..) => "multi_a",
Terminal::SortedMultiA(..) => "sortedmulti_a",
}
}

Expand Down
1 change: 1 addition & 0 deletions src/miniscript/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
(Terminal::PkK(key), 0) | (Terminal::PkH(key), 0) => Some(key.clone()),
(Terminal::Multi(thresh), _) => thresh.data().get(n).cloned(),
(Terminal::MultiA(thresh), _) => thresh.data().get(n).cloned(),
(Terminal::SortedMultiA(thresh), _) => thresh.data().get(n).cloned(),
_ => None,
}
}
Expand Down
Loading
Loading