Skip to content

Commit 48346ca

Browse files
committed
Wrap all_assets for desc and add count_assets method
This commit wraps the all_assets function for all possible descriptor types. It also adds `count_assets` function which can count the total possible ways to obtain an asset for a given descriptor. Signed-off-by: Harshil Jani <[email protected]>
1 parent 30bcc4a commit 48346ca

File tree

6 files changed

+383
-37
lines changed

6 files changed

+383
-37
lines changed

src/descriptor/mod.rs

Lines changed: 191 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ use sync::Arc;
2323
use self::checksum::verify_checksum;
2424
use crate::miniscript::decode::Terminal;
2525
use crate::miniscript::{satisfy, Legacy, Miniscript, Segwitv0};
26-
use crate::plan::{AssetProvider, Plan};
26+
use crate::plan::{AssetProvider, Assets, Plan};
2727
use crate::prelude::*;
28+
use crate::util::get_asset_combination;
2829
use crate::{
2930
expression, hash256, BareCtx, Error, ForEachKey, MiniscriptKey, Satisfier, ToPublicKey,
3031
TranslateErr, TranslatePk, Translator,
@@ -546,6 +547,124 @@ impl Descriptor<DefiniteDescriptorKey> {
546547
}
547548
}
548549

550+
impl Descriptor<DescriptorPublicKey> {
551+
/// Count total possible assets for a given descriptor.
552+
pub fn count_assets(&self) -> u64 {
553+
match self {
554+
Descriptor::Bare(k) => k.as_inner().count_assets(),
555+
Descriptor::Pkh(_) => 1,
556+
Descriptor::Wpkh(_) => 1,
557+
Descriptor::Sh(k) => match k.as_inner() {
558+
ShInner::Wsh(k) => match k.as_inner() {
559+
WshInner::SortedMulti(k) => {
560+
let n = k.pks.len() as u64;
561+
let k = k.k as u64;
562+
Self::k_of_n(k, n)
563+
}
564+
WshInner::Ms(k) => k.count_assets(),
565+
},
566+
ShInner::Wpkh(_) => 1,
567+
ShInner::SortedMulti(k) => {
568+
let n = k.clone().pks.len() as u64;
569+
let k = k.clone().k as u64;
570+
Self::k_of_n(k, n)
571+
}
572+
ShInner::Ms(k) => k.count_assets(),
573+
},
574+
Descriptor::Wsh(k) => match k.as_inner() {
575+
WshInner::SortedMulti(k) => {
576+
let n = k.clone().pks.len() as u64;
577+
let k = k.clone().k as u64;
578+
Self::k_of_n(k, n)
579+
}
580+
WshInner::Ms(k) => k.count_assets(),
581+
},
582+
Descriptor::Tr(k) => {
583+
let s = k.tap_tree().clone().unwrap();
584+
match s {
585+
TapTree::Tree { left, right, height: _ } => {
586+
let a = left.count_assets();
587+
let b = right.count_assets();
588+
a + b
589+
}
590+
TapTree::Leaf(k) => k.count_assets(),
591+
}
592+
}
593+
}
594+
}
595+
596+
/// Get all possible assets for a given descriptor
597+
pub fn all_assets(&self) -> Result<Vec<Assets>, Error> {
598+
match self {
599+
Descriptor::Bare(k) => Ok(k.as_inner().all_assets()),
600+
Descriptor::Pkh(k) => {
601+
let mut asset = Assets::new();
602+
asset = asset.add(k.as_inner().clone());
603+
Ok(vec![asset])
604+
}
605+
Descriptor::Wpkh(k) => {
606+
let mut asset = Assets::new();
607+
asset = asset.add(k.as_inner().clone());
608+
Ok(vec![asset])
609+
}
610+
Descriptor::Sh(k) => match k.as_inner() {
611+
ShInner::Wsh(k) => match k.as_inner() {
612+
WshInner::SortedMulti(k) => {
613+
let dpk_v = k.clone().pks;
614+
let k = k.clone().k;
615+
Ok(get_asset_combination(k, &dpk_v))
616+
}
617+
WshInner::Ms(k) => Ok(k.all_assets()),
618+
},
619+
ShInner::Wpkh(k) => {
620+
let mut asset = Assets::new();
621+
asset = asset.add(k.as_inner().clone());
622+
Ok(vec![asset])
623+
}
624+
ShInner::SortedMulti(k) => {
625+
let dpk_v = k.clone().pks;
626+
let k = k.clone().k;
627+
Ok(get_asset_combination(k, &dpk_v))
628+
}
629+
ShInner::Ms(k) => Ok(k.all_assets()),
630+
},
631+
Descriptor::Wsh(k) => match k.as_inner() {
632+
WshInner::SortedMulti(k) => {
633+
let dpk_v = k.clone().pks;
634+
let k = k.clone().k;
635+
Ok(get_asset_combination(k, &dpk_v))
636+
}
637+
WshInner::Ms(k) => {
638+
println!("{}", k);
639+
let a = k.all_assets();
640+
println!("{:#?}", a);
641+
Ok(a)
642+
}
643+
},
644+
Descriptor::Tr(k) => {
645+
let s = k.tap_tree().clone().unwrap();
646+
match s {
647+
TapTree::Tree { left, right, height: _ } => {
648+
let mut a = left.all_assets();
649+
let b = right.all_assets();
650+
a.extend(b);
651+
Ok(a)
652+
}
653+
TapTree::Leaf(k) => Ok(k.all_assets()),
654+
}
655+
}
656+
}
657+
}
658+
659+
// ways to select k things out of n
660+
fn k_of_n(k: u64, n: u64) -> u64 {
661+
if k == 0 || k == n {
662+
return 1;
663+
}
664+
Self::k_of_n(k - 1, n - 1) + Self::k_of_n(k - 1, n)
665+
}
666+
}
667+
549668
impl<P, Q> TranslatePk<P, Q> for Descriptor<P>
550669
where
551670
P: MiniscriptKey,
@@ -2041,4 +2160,75 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
20412160
Desc::from_str(&format!("tr({},pk({}))", x_only_key, uncomp_key)).unwrap_err();
20422161
Desc::from_str(&format!("tr({},pk({}))", x_only_key, x_only_key)).unwrap();
20432162
}
2163+
2164+
#[test]
2165+
fn test_all_assets_bare() {
2166+
let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
2167+
"pk(0237b1c59ab055a8d3de40eaeb215c7b1922664b5ac049d849fb3346f81431e77f)",
2168+
)
2169+
.unwrap();
2170+
2171+
// Getting the assets from the all_assets method
2172+
let assets = descriptor.all_assets().unwrap();
2173+
2174+
let mut expected_asset = Assets::new();
2175+
expected_asset = expected_asset.add(
2176+
DescriptorPublicKey::from_str(
2177+
"0237b1c59ab055a8d3de40eaeb215c7b1922664b5ac049d849fb3346f81431e77f",
2178+
)
2179+
.unwrap(),
2180+
);
2181+
assert_eq!(assets, vec![expected_asset]);
2182+
}
2183+
2184+
#[test]
2185+
fn test_all_assets_sh_sortedmulti() {
2186+
let keys = vec![
2187+
"0360eabc52e04f70c89e944f379895cdff28fed60849afe7736815c091765afb3c",
2188+
"03a80a24196e66ccf6bca6e6f96633bb629ec702acd76b074de10922b0cf41cc81",
2189+
];
2190+
2191+
let descriptor = Descriptor::<DescriptorPublicKey>::from_str(&format!(
2192+
"sh(sortedmulti(1,{},{}))",
2193+
keys[0], keys[1]
2194+
))
2195+
.unwrap();
2196+
2197+
let assets = descriptor.all_assets().unwrap();
2198+
2199+
let mut expected_assets: Vec<Assets> = Vec::new();
2200+
2201+
let mut asset1 = Assets::new();
2202+
asset1 = asset1.add(DescriptorPublicKey::from_str(keys[0]).unwrap());
2203+
expected_assets.push(asset1);
2204+
2205+
let mut asset2 = Assets::new();
2206+
asset2 = asset2.add(DescriptorPublicKey::from_str(keys[1]).unwrap());
2207+
expected_assets.push(asset2);
2208+
2209+
for expected_asset in &expected_assets {
2210+
assert!(assets.contains(expected_asset));
2211+
}
2212+
}
2213+
2214+
#[test]
2215+
fn test_all_assets_taproot() {
2216+
let x_only_key = bitcoin::key::XOnlyPublicKey::from_str(
2217+
"015e4cb53458bf813db8c79968e76e10d13ed6426a23fa71c2f41ba021c2a7ab",
2218+
)
2219+
.unwrap();
2220+
let descriptor =
2221+
Descriptor::from_str(&format!("tr({},pk({}))", x_only_key, x_only_key)).unwrap();
2222+
let assets = descriptor.all_assets().unwrap();
2223+
let mut expected_assets: Vec<Assets> = Vec::new();
2224+
let mut asset_1 = Assets::new();
2225+
asset_1 = asset_1.add(
2226+
DescriptorPublicKey::from_str(
2227+
"015e4cb53458bf813db8c79968e76e10d13ed6426a23fa71c2f41ba021c2a7ab",
2228+
)
2229+
.unwrap(),
2230+
);
2231+
expected_assets.push(asset_1);
2232+
assert_eq!(assets, expected_assets);
2233+
}
20442234
}

src/descriptor/tr.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ use crate::descriptor::DefiniteDescriptorKey;
1515
use crate::expression::{self, FromTree};
1616
use crate::miniscript::satisfy::{Placeholder, Satisfaction, SchnorrSigType, Witness};
1717
use crate::miniscript::Miniscript;
18-
use crate::plan::AssetProvider;
18+
use crate::plan::{AssetProvider, Assets};
1919
use crate::policy::semantic::Policy;
2020
use crate::policy::Liftable;
2121
use crate::prelude::*;
2222
use crate::util::{varint_len, witness_size};
2323
use crate::{
24-
errstr, Error, ForEachKey, MiniscriptKey, Satisfier, ScriptContext, Tap, ToPublicKey,
25-
TranslateErr, TranslatePk, Translator,
24+
errstr, DescriptorPublicKey, Error, ForEachKey, MiniscriptKey, Satisfier, ScriptContext, Tap,
25+
ToPublicKey, TranslateErr, TranslatePk, Translator,
2626
};
2727

2828
/// A Taproot Tree representation.
@@ -153,6 +153,33 @@ impl<Pk: MiniscriptKey> TapTree<Pk> {
153153
}
154154
}
155155

156+
impl TapTree<DescriptorPublicKey> {
157+
/// Get all possible assets for Taptree
158+
pub fn all_assets(&self) -> Vec<Assets> {
159+
match self {
160+
TapTree::Tree { left, right, height: _ } => {
161+
let mut a = left.all_assets();
162+
let b = right.all_assets();
163+
a.extend(b);
164+
a
165+
}
166+
TapTree::Leaf(k) => k.all_assets(),
167+
}
168+
}
169+
170+
/// Get total possible assets for TapTree
171+
pub fn count_assets(&self) -> u64 {
172+
match self {
173+
TapTree::Tree { left, right, height: _ } => {
174+
let a = left.count_assets();
175+
let b = right.count_assets();
176+
a + b
177+
}
178+
TapTree::Leaf(k) => k.count_assets(),
179+
}
180+
}
181+
}
182+
156183
impl<Pk: MiniscriptKey> fmt::Display for TapTree<Pk> {
157184
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158185
match self {

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,8 @@ pub enum Error {
501501
/// At least two BIP389 key expressions in the descriptor contain tuples of
502502
/// derivation indexes of different lengths.
503503
MultipathDescLenMismatch,
504+
/// Cannot get assets for this large descriptor. Exceeds 1000 assets.
505+
MaxAssetThresholdExceeded,
504506
}
505507

506508
// https://github.com/sipa/miniscript/pull/5 for discussion on this number
@@ -577,6 +579,7 @@ impl fmt::Display for Error {
577579
Error::TrNoScriptCode => write!(f, "No script code for Tr descriptors"),
578580
Error::TrNoExplicitScript => write!(f, "No script code for Tr descriptors"),
579581
Error::MultipathDescLenMismatch => write!(f, "At least two BIP389 key expressions in the descriptor contain tuples of derivation indexes of different lengths"),
582+
Error::MaxAssetThresholdExceeded => write!(f,"Cannot plan descriptors having more than 1000 possible spend paths."),
580583
}
581584
}
582585
}
@@ -619,6 +622,7 @@ impl error::Error for Error {
619622
| TrNoScriptCode
620623
| TrNoExplicitScript
621624
| MultipathDescLenMismatch => None,
625+
MaxAssetThresholdExceeded => None,
622626
Script(e) => Some(e),
623627
AddrError(e) => Some(e),
624628
BadPubkey(e) => Some(e),

0 commit comments

Comments
 (0)