Skip to content

Commit 4773718

Browse files
committed
count_assets method for total ways to obtain assets
Signed-off-by: Harshil Jani <[email protected]>
1 parent b2ead84 commit 4773718

File tree

4 files changed

+192
-0
lines changed

4 files changed

+192
-0
lines changed

src/descriptor/mod.rs

+54
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,52 @@ impl Descriptor<DefiniteDescriptorKey> {
567567
}
568568

569569
impl Descriptor<DescriptorPublicKey>{
570+
571+
/// Count total possible assets for a given descriptor.
572+
pub fn count_assets(&self) -> u64 {
573+
match self {
574+
Descriptor::Bare(k) => k.as_inner().count_assets(),
575+
Descriptor::Pkh(_) => 1,
576+
Descriptor::Wpkh(_) => 1,
577+
Descriptor::Sh(k) => match k.as_inner() {
578+
ShInner::Wsh(k) => match k.as_inner() {
579+
WshInner::SortedMulti(k) => {
580+
let n = k.pks.len() as u64;
581+
let k = k.k as u64;
582+
Self::k_of_n(k, n)
583+
}
584+
WshInner::Ms(k) => k.count_assets(),
585+
},
586+
ShInner::Wpkh(_) => 1,
587+
ShInner::SortedMulti(k) => {
588+
let n = k.clone().pks.len() as u64;
589+
let k = k.clone().k as u64;
590+
Self::k_of_n(k, n)
591+
}
592+
ShInner::Ms(k) => k.count_assets(),
593+
},
594+
Descriptor::Wsh(k) => match k.as_inner() {
595+
WshInner::SortedMulti(k) => {
596+
let n = k.clone().pks.len() as u64;
597+
let k = k.clone().k as u64;
598+
Self::k_of_n(k, n)
599+
}
600+
WshInner::Ms(k) => k.count_assets(),
601+
},
602+
Descriptor::Tr(k) => {
603+
let s = k.taptree().clone().unwrap();
604+
match s {
605+
TapTree::Tree(ref left, ref right) => {
606+
let a = left.count_assets();
607+
let b = right.count_assets();
608+
a + b
609+
}
610+
TapTree::Leaf(k) => k.count_assets(),
611+
}
612+
}
613+
}
614+
}
615+
570616
/// Get all possible assets for a given descriptor
571617
pub fn get_all_assets(&self) -> Result<Vec<Assets>, Error> {
572618

@@ -659,6 +705,14 @@ impl Descriptor<DescriptorPublicKey>{
659705
Self::combine_assets(k - 1, dpk_v, index + 1, new_asset, all_assets)
660706
}
661707

708+
// ways to select k things out of n
709+
fn k_of_n(k: u64, n: u64) -> u64 {
710+
if k == 0 || k == n {
711+
return 1;
712+
}
713+
Self::k_of_n(n - 1, k - 1) + Self::k_of_n(n - 1, k)
714+
}
715+
662716
}
663717

664718
impl<P, Q> TranslatePk<P, Q> for Descriptor<P>

src/descriptor/tr.rs

+12
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,18 @@ impl TapTree<DescriptorPublicKey> {
161161
TapTree::Leaf(k) => k.get_all_assets(),
162162
}
163163
}
164+
165+
/// Get total possible assets for TapTree
166+
pub fn count_assets(&self) -> u64 {
167+
match self {
168+
TapTree::Tree(left, right) => {
169+
let a = left.count_assets();
170+
let b = right.count_assets();
171+
a + b
172+
}
173+
TapTree::Leaf(k) => k.count_assets(),
174+
}
175+
}
164176
}
165177

166178
impl<Pk: MiniscriptKey> fmt::Display for TapTree<Pk> {

src/miniscript/astelem.rs

+121
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,93 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
598598
}
599599

600600
impl<Ctx: ScriptContext> Terminal<DescriptorPublicKey, Ctx> {
601+
/// Count total possible assets
602+
pub fn count_assets(&self) -> u64 {
603+
match self {
604+
Terminal::True => 0,
605+
Terminal::False => 0,
606+
Terminal::PkK(_) => 1,
607+
Terminal::PkH(_) => 1,
608+
Terminal::RawPkH(_) => 1,
609+
// What happens to timelocks ? for both the assets and the count.
610+
Terminal::After(_) => 0,
611+
Terminal::Older(_) => 0,
612+
Terminal::Sha256(_) => 1,
613+
Terminal::Hash256(_) => 1,
614+
Terminal::Ripemd160(_) => 1,
615+
Terminal::Hash160(_) => 1,
616+
Terminal::Alt(k) => k.count_assets(),
617+
Terminal::Swap(k) => k.count_assets(),
618+
Terminal::Check(k) => k.count_assets(),
619+
Terminal::DupIf(k) => k.count_assets(),
620+
Terminal::Verify(k) => k.count_assets(),
621+
Terminal::NonZero(k) => k.count_assets(),
622+
Terminal::ZeroNotEqual(k) => k.count_assets(),
623+
Terminal::AndV(left, right) => {
624+
let left_count = left.count_assets();
625+
let right_count = right.count_assets();
626+
left_count * right_count
627+
}
628+
Terminal::AndB(left, right) => {
629+
let left_count = left.count_assets();
630+
let right_count = right.count_assets();
631+
left_count * right_count
632+
}
633+
Terminal::AndOr(a, b, c) => {
634+
let a = a.count_assets();
635+
let b = b.count_assets();
636+
let c = c.count_assets();
637+
(a * b) + c
638+
}
639+
Terminal::OrB(left, right) => {
640+
let left_count = left.count_assets();
641+
let right_count = right.count_assets();
642+
left_count + right_count
643+
}
644+
Terminal::OrD(left, right) => {
645+
let left_count = left.count_assets();
646+
let right_count = right.count_assets();
647+
left_count + right_count
648+
}
649+
Terminal::OrC(left, right) => {
650+
let left_count = left.count_assets();
651+
let right_count = right.count_assets();
652+
left_count + right_count
653+
}
654+
Terminal::OrI(left, right) => {
655+
let left_count = left.count_assets();
656+
let right_count = right.count_assets();
657+
left_count + right_count
658+
}
659+
Terminal::Thresh(k, ms_v) => {
660+
// k = 2, n = ms_v.len()
661+
// ms_v = [ms(A),ms(B),ms(C)];
662+
// Assume count array as [5,7,8] and k=2
663+
// get_combinations_product gives [5*7,5*8,7*8] = [35,40,56]
664+
let mut count_array = Vec::new();
665+
for ms in ms_v {
666+
count_array.push(ms.count_assets());
667+
}
668+
let products = Self::get_combinations_product(&count_array, *k as u64);
669+
let mut total_count: u64 = 0;
670+
for product in products {
671+
total_count += product;
672+
}
673+
total_count
674+
}
675+
Terminal::Multi(k, dpk) => {
676+
let k: u64 = *k as u64;
677+
let n: u64 = dpk.len() as u64;
678+
Self::k_of_n(k, n)
679+
}
680+
Terminal::MultiA(k, dpk) => {
681+
let k: u64 = *k as u64;
682+
let n: u64 = dpk.len() as u64;
683+
Self::k_of_n(k, n)
684+
}
685+
}
686+
}
687+
601688
/// Retrieve the assets associated with the type of miniscript element.
602689
pub fn get_all_assets(&self) -> Vec<Assets> {
603690
match self {
@@ -825,4 +912,38 @@ impl<Ctx: ScriptContext> Terminal<DescriptorPublicKey, Ctx> {
825912
current_combination.truncate(current_combination.len() - 1);
826913
}
827914
}
915+
916+
// Do product of K combinations
917+
fn get_combinations_product(values: &[u64], k: u64) -> Vec<u64> {
918+
let mut products = Vec::new();
919+
let n = values.len();
920+
921+
if k == 0 {
922+
return vec![1]; // Empty combination has a product of 1
923+
}
924+
925+
// Using bitwise operations to generate combinations
926+
let max_combinations = 1u32 << n;
927+
for combination_bits in 1..max_combinations {
928+
if combination_bits.count_ones() as usize == k as usize {
929+
let mut product = 1;
930+
for i in 0..n {
931+
if combination_bits & (1u32 << i) != 0 {
932+
product *= values[i];
933+
}
934+
}
935+
products.push(product);
936+
}
937+
}
938+
939+
products
940+
}
941+
942+
// ways to select k things out of n
943+
fn k_of_n(k: u64, n: u64) -> u64 {
944+
if k == 0 || k == n {
945+
return 1;
946+
}
947+
Self::k_of_n(k - 1, n - 1) + Self::k_of_n(k, n - 1)
948+
}
828949
}

src/miniscript/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,11 @@ impl<Ctx: ScriptContext> Miniscript<DescriptorPublicKey, Ctx> {
351351
pub fn get_all_assets(&self) -> Vec<Assets> {
352352
self.node.get_all_assets()
353353
}
354+
355+
/// Get the total number of assets possible
356+
pub fn count_assets(&self) -> u64 {
357+
self.node.count_assets()
358+
}
354359
}
355360

356361
impl<Pk: MiniscriptKey, Ctx: ScriptContext> ForEachKey<Pk> for Miniscript<Pk, Ctx> {

0 commit comments

Comments
 (0)