Skip to content

Commit 200433c

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. This API will help developer to count the possible ways. In-fact this will also help in protecting that if some descriptor is very large and has huge number of possible spend paths then it could simply be dropped off and we can protect the network from traffic. Signed-off-by: Harshil Jani <[email protected]>
1 parent ebcc7e4 commit 200433c

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)