Skip to content

Commit a1b8985

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

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
@@ -571,33 +571,33 @@ impl Descriptor<DefiniteDescriptorKey> {
571571

572572
impl Descriptor<DescriptorPublicKey> {
573573
/// Count total possible assets for a given descriptor.
574-
pub fn count_assets(&self) -> u64 {
574+
pub fn count_assets(&self) -> u32 {
575575
match self {
576576
Descriptor::Bare(k) => k.as_inner().count_assets(),
577577
Descriptor::Pkh(_) => 1,
578578
Descriptor::Wpkh(_) => 1,
579579
Descriptor::Sh(k) => match k.as_inner() {
580580
ShInner::Wsh(k) => match k.as_inner() {
581581
WshInner::SortedMulti(k) => {
582-
let n = k.pks.len() as u64;
583-
let k = k.k as u64;
584-
k_of_n(k, n)
582+
let n = k.pks.len() as u32;
583+
let k = k.k as u32;
584+
k_of_n(k, n).unwrap()
585585
}
586586
WshInner::Ms(k) => k.count_assets(),
587587
},
588588
ShInner::Wpkh(_) => 1,
589589
ShInner::SortedMulti(k) => {
590-
let n = k.clone().pks.len() as u64;
591-
let k = k.clone().k as u64;
592-
k_of_n(k, n)
590+
let n = k.clone().pks.len() as u32;
591+
let k = k.clone().k as u32;
592+
k_of_n(k, n).unwrap()
593593
}
594594
ShInner::Ms(k) => k.count_assets(),
595595
},
596596
Descriptor::Wsh(k) => match k.as_inner() {
597597
WshInner::SortedMulti(k) => {
598-
let n = k.clone().pks.len() as u64;
599-
let k = k.clone().k as u64;
600-
k_of_n(k, n)
598+
let n = k.clone().pks.len() as u32;
599+
let k = k.clone().k as u32;
600+
k_of_n(k, n).unwrap()
601601
}
602602
WshInner::Ms(k) => k.count_assets(),
603603
},
@@ -617,6 +617,10 @@ impl Descriptor<DescriptorPublicKey> {
617617

618618
/// Get all possible assets for a given descriptor
619619
pub fn all_assets(&self) -> Result<Vec<Assets>, Error> {
620+
let threshold = self.count_assets();
621+
if threshold >= 1000 {
622+
return Err(Error::MaxAssetThresholdExceeded);
623+
}
620624
match self {
621625
Descriptor::Bare(k) => Ok(k.as_inner().all_assets()),
622626
Descriptor::Pkh(k) => {

src/descriptor/tr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ impl TapTree<DescriptorPublicKey> {
163163
}
164164

165165
/// Get total possible assets for TapTree
166-
pub fn count_assets(&self) -> u64 {
166+
pub fn count_assets(&self) -> u32 {
167167
match self {
168168
TapTree::Tree(left, right) => {
169169
let a = left.count_assets();

src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,9 @@ pub enum Error {
568568
const MAX_RECURSION_DEPTH: u32 = 402;
569569
// https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
570570
const MAX_SCRIPT_SIZE: u32 = 10000;
571+
// For the planning module we are considering that total possible ways to spend
572+
// should be less than 1000
573+
const MAX_ASSET_THRESHOLD: u32 = 1000;
571574

572575
impl fmt::Display for Error {
573576
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
@@ -412,7 +412,7 @@ impl<Ctx: ScriptContext> Miniscript<DescriptorPublicKey, Ctx> {
412412
}
413413

414414
/// Get the total number of assets possible
415-
pub fn count_assets(&self) -> u64 {
415+
pub fn count_assets(&self) -> u32 {
416416
self.node.count_assets()
417417
}
418418
}

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 {
1517
bitcoin::VarInt(n as u64).len()
1618
}
@@ -60,7 +62,7 @@ pub(crate) fn witness_to_scriptsig(witness: &[Vec<u8>]) -> ScriptBuf {
6062
} else {
6163
let push = <&PushBytes>::try_from(wit.as_slice())
6264
.expect("All pushes in miniscript are <73 bytes");
63-
b = b.push_slice(push)
65+
b = b.push_slice(push);
6466
}
6567
}
6668
b.into_script()
@@ -138,7 +140,7 @@ pub fn combine_assets(
138140
}
139141

140142
// Do product of K combinations
141-
pub fn get_combinations_product(values: &[u64], k: u64) -> Vec<u64> {
143+
pub fn get_combinations_product(values: &[u32], k: u32) -> Vec<u32> {
142144
let mut products = Vec::new();
143145
let n = values.len();
144146

@@ -149,10 +151,10 @@ pub fn get_combinations_product(values: &[u64], k: u64) -> Vec<u64> {
149151
// Using bitwise operations to generate combinations
150152
let max_combinations = 1u32 << n;
151153
for combination_bits in 1..max_combinations {
152-
if combination_bits.count_ones() as usize == k as usize {
154+
if (combination_bits.count_ones() as usize) == (k as usize) {
153155
let mut product = 1;
154156
for i in 0..n {
155-
if combination_bits & (1u32 << i) != 0 {
157+
if (combination_bits & (1u32 << i)) != 0 {
156158
product *= values[i];
157159
}
158160
}
@@ -164,9 +166,20 @@ pub fn get_combinations_product(values: &[u64], k: u64) -> Vec<u64> {
164166
}
165167

166168
// ways to select k things out of n
167-
pub fn k_of_n(k: u64, n: u64) -> u64 {
168-
if k == 0 || k == n {
169-
return 1;
169+
pub fn k_of_n(k: u32, n: u32) -> Result<u32, Error> {
170+
let mut k = k;
171+
if k > n - k {
172+
k = n - k;
170173
}
171-
k_of_n(k - 1, n - 1) + k_of_n(k, n - 1)
174+
175+
let mut result = 1;
176+
for i in 0..k {
177+
result *= n - i;
178+
result /= i + 1;
179+
if result > MAX_ASSET_THRESHOLD.into() {
180+
return Err(Error::MaxAssetThresholdExceeded);
181+
}
182+
}
183+
184+
Ok(result)
172185
}

0 commit comments

Comments
 (0)