Skip to content

Commit b4e31f8

Browse files
committed
Adding threshold error on exceeding 1000 ways
Signed-off-by: Harshil Jani <[email protected]>
1 parent 3f0e743 commit b4e31f8

File tree

6 files changed

+47
-27
lines changed

6 files changed

+47
-27
lines changed

src/descriptor/mod.rs

+14-10
Original file line numberDiff line numberDiff line change
@@ -549,33 +549,33 @@ impl Descriptor<DefiniteDescriptorKey> {
549549

550550
impl Descriptor<DescriptorPublicKey> {
551551
/// Count total possible assets for a given descriptor.
552-
pub fn count_assets(&self) -> u64 {
552+
pub fn count_assets(&self) -> u32 {
553553
match self {
554554
Descriptor::Bare(k) => k.as_inner().count_assets(),
555555
Descriptor::Pkh(_) => 1,
556556
Descriptor::Wpkh(_) => 1,
557557
Descriptor::Sh(k) => match k.as_inner() {
558558
ShInner::Wsh(k) => match k.as_inner() {
559559
WshInner::SortedMulti(k) => {
560-
let n = k.pks.len() as u64;
561-
let k = k.k as u64;
562-
k_of_n(k, n)
560+
let n = k.pks.len() as u32;
561+
let k = k.k as u32;
562+
k_of_n(k, n).unwrap()
563563
}
564564
WshInner::Ms(k) => k.count_assets(),
565565
},
566566
ShInner::Wpkh(_) => 1,
567567
ShInner::SortedMulti(k) => {
568-
let n = k.clone().pks.len() as u64;
569-
let k = k.clone().k as u64;
570-
k_of_n(k, n)
568+
let n = k.clone().pks.len() as u32;
569+
let k = k.clone().k as u32;
570+
k_of_n(k, n).unwrap()
571571
}
572572
ShInner::Ms(k) => k.count_assets(),
573573
},
574574
Descriptor::Wsh(k) => match k.as_inner() {
575575
WshInner::SortedMulti(k) => {
576-
let n = k.clone().pks.len() as u64;
577-
let k = k.clone().k as u64;
578-
k_of_n(k, n)
576+
let n = k.clone().pks.len() as u32;
577+
let k = k.clone().k as u32;
578+
k_of_n(k, n).unwrap()
579579
}
580580
WshInner::Ms(k) => k.count_assets(),
581581
},
@@ -595,6 +595,10 @@ impl Descriptor<DescriptorPublicKey> {
595595

596596
/// Get all possible assets for a given descriptor
597597
pub fn all_assets(&self) -> Result<Vec<Assets>, Error> {
598+
let threshold = self.count_assets();
599+
if threshold >= 1000 {
600+
return Err(Error::MaxAssetThresholdExceeded);
601+
}
598602
match self {
599603
Descriptor::Bare(k) => Ok(k.as_inner().all_assets()),
600604
Descriptor::Pkh(k) => {

src/descriptor/tr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ impl TapTree<DescriptorPublicKey> {
168168
}
169169

170170
/// Get total possible assets for TapTree
171-
pub fn count_assets(&self) -> u64 {
171+
pub fn count_assets(&self) -> u32 {
172172
match self {
173173
TapTree::Tree { left, right, height: _ } => {
174174
let a = left.count_assets();

src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,9 @@ pub enum Error {
509509
const MAX_RECURSION_DEPTH: u32 = 402;
510510
// https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
511511
const MAX_SCRIPT_SIZE: u32 = 10000;
512+
// For the planning module we are considering that total possible ways to spend
513+
// should be less than 1000
514+
const MAX_ASSET_THRESHOLD: u32 = 1000;
512515

513516
impl fmt::Display for Error {
514517
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

src/miniscript/astelem.rs

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

599599
impl<Ctx: ScriptContext> Terminal<DescriptorPublicKey, Ctx> {
600600
/// Count total possible assets
601-
pub fn count_assets(&self) -> u64 {
601+
pub fn count_assets(&self) -> u32 {
602602
match self {
603603
Terminal::True => 0,
604604
Terminal::False => 0,
@@ -642,17 +642,17 @@ impl<Ctx: ScriptContext> Terminal<DescriptorPublicKey, Ctx> {
642642
for ms in ms_v {
643643
count_array.push(ms.count_assets());
644644
}
645-
let products = get_combinations_product(&count_array, *k as u64);
646-
let mut total_count: u64 = 0;
645+
let products = get_combinations_product(&count_array, *k as u32);
646+
let mut total_count: u32 = 0;
647647
for product in products {
648648
total_count += product;
649649
}
650650
total_count
651651
}
652652
Terminal::Multi(k, dpk) | Terminal::MultiA(k, dpk) => {
653-
let k: u64 = *k as u64;
654-
let n: u64 = dpk.len() as u64;
655-
k_of_n(k, n)
653+
let k: u32 = *k as u32;
654+
let n: u32 = dpk.len() as u32;
655+
k_of_n(k, n).unwrap()
656656
}
657657
}
658658
}

src/miniscript/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ impl<Ctx: ScriptContext> Miniscript<DescriptorPublicKey, Ctx> {
391391
pub fn all_assets(&self) -> Vec<Assets> { self.node.all_assets() }
392392

393393
/// Get the total number of assets possible
394-
pub fn count_assets(&self) -> u64 { self.node.count_assets() }
394+
pub fn count_assets(&self) -> u32 { self.node.count_assets() }
395395
}
396396

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

src/util.rs

+22-9
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ use crate::miniscript::context;
1010
use crate::miniscript::satisfy::Placeholder;
1111
use crate::plan::Assets;
1212
use crate::prelude::*;
13-
use crate::{DescriptorPublicKey, MiniscriptKey, ScriptContext, ToPublicKey};
13+
use crate::{
14+
DescriptorPublicKey, Error, MiniscriptKey, ScriptContext, ToPublicKey, MAX_ASSET_THRESHOLD,
15+
};
1416
pub(crate) fn varint_len(n: usize) -> usize { bitcoin::VarInt(n as u64).len() }
1517

1618
pub(crate) trait ItemSize {
@@ -56,7 +58,7 @@ pub(crate) fn witness_to_scriptsig(witness: &[Vec<u8>]) -> ScriptBuf {
5658
} else {
5759
let push = <&PushBytes>::try_from(wit.as_slice())
5860
.expect("All pushes in miniscript are <73 bytes");
59-
b = b.push_slice(push)
61+
b = b.push_slice(push);
6062
}
6163
}
6264
b.into_script()
@@ -134,7 +136,7 @@ pub fn combine_assets(
134136
}
135137

136138
// Do product of K combinations
137-
pub fn get_combinations_product(values: &[u64], k: u64) -> Vec<u64> {
139+
pub fn get_combinations_product(values: &[u32], k: u32) -> Vec<u32> {
138140
let mut products = Vec::new();
139141
let n = values.len();
140142

@@ -145,10 +147,10 @@ pub fn get_combinations_product(values: &[u64], k: u64) -> Vec<u64> {
145147
// Using bitwise operations to generate combinations
146148
let max_combinations = 1u32 << n;
147149
for combination_bits in 1..max_combinations {
148-
if combination_bits.count_ones() as usize == k as usize {
150+
if (combination_bits.count_ones() as usize) == (k as usize) {
149151
let mut product = 1;
150152
for i in 0..n {
151-
if combination_bits & (1u32 << i) != 0 {
153+
if (combination_bits & (1u32 << i)) != 0 {
152154
product *= values[i];
153155
}
154156
}
@@ -160,9 +162,20 @@ pub fn get_combinations_product(values: &[u64], k: u64) -> Vec<u64> {
160162
}
161163

162164
// ways to select k things out of n
163-
pub fn k_of_n(k: u64, n: u64) -> u64 {
164-
if k == 0 || k == n {
165-
return 1;
165+
pub fn k_of_n(k: u32, n: u32) -> Result<u32, Error> {
166+
let mut k = k;
167+
if k > n - k {
168+
k = n - k;
166169
}
167-
k_of_n(k - 1, n - 1) + k_of_n(k, n - 1)
170+
171+
let mut result = 1;
172+
for i in 0..k {
173+
result *= n - i;
174+
result /= i + 1;
175+
if result > MAX_ASSET_THRESHOLD.into() {
176+
return Err(Error::MaxAssetThresholdExceeded);
177+
}
178+
}
179+
180+
Ok(result)
168181
}

0 commit comments

Comments
 (0)