From 60134c88600292cfdc4cc9a56fef2cab52d4f42c Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Mon, 20 May 2024 23:26:27 +0100 Subject: [PATCH] feat: add targeted split --- crates/cdk/src/amount.rs | 83 ++++++++++++++++++++++++++++++++++++++ crates/cdk/src/util/mod.rs | 15 +------ 2 files changed, 85 insertions(+), 13 deletions(-) diff --git a/crates/cdk/src/amount.rs b/crates/cdk/src/amount.rs index af2c0c22..79769ec4 100644 --- a/crates/cdk/src/amount.rs +++ b/crates/cdk/src/amount.rs @@ -21,6 +21,48 @@ impl Amount { }) .collect() } + + /// Split into parts that are powers of two by target + pub fn split_targeted(&self, target: &SplitTarget) -> Vec { + let mut parts = vec![]; + let mut parts_total = Amount::ZERO; + + match target { + &SplitTarget::Value(amount) => { + if self.le(&amount) { + return self.split(); + } + + // The powers of two that are need to create target value + let parts_of_value = amount.split(); + + while parts_total.lt(self) { + for part in parts_of_value.iter().copied() { + if (part + parts_total).le(self) { + parts.push(part); + } else { + let amount_left = *self - parts_total; + parts.extend(amount_left.split()); + } + + parts_total = parts.clone().iter().copied().sum::(); + + if parts_total.eq(self) { + break; + } + } + } + } + } + + parts.sort(); + parts + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +pub enum SplitTarget { + Value(Amount), } impl Default for Amount { @@ -102,4 +144,45 @@ mod tests { .collect(); assert_eq!(Amount::from(255).split(), amounts); } + + #[test] + fn test_split_target_amount() { + let amount = Amount(65); + + let split = amount.split_targeted(&SplitTarget::Value(Amount(32))); + assert_eq!(vec![Amount(1), Amount(32), Amount(32)], split); + + let amount = Amount(150); + + let split = amount.split_targeted(&SplitTarget::Value(Amount::from(50))); + assert_eq!( + vec![ + Amount(2), + Amount(2), + Amount(2), + Amount(16), + Amount(16), + Amount(16), + Amount(32), + Amount(32), + Amount(32) + ], + split + ); + + let amount = Amount::from(63); + + let split = amount.split_targeted(&SplitTarget::Value(Amount::from(32))); + assert_eq!( + vec![ + Amount(1), + Amount(2), + Amount(4), + Amount(8), + Amount(16), + Amount(32) + ], + split + ); + } } diff --git a/crates/cdk/src/util/mod.rs b/crates/cdk/src/util/mod.rs index 293210c9..5da77547 100644 --- a/crates/cdk/src/util/mod.rs +++ b/crates/cdk/src/util/mod.rs @@ -3,10 +3,7 @@ #[cfg(not(target_arch = "wasm32"))] use std::time::{SystemTime, UNIX_EPOCH}; -use bitcoin::hashes::sha256::Hash as Sha256; -use bitcoin::hashes::Hash; -use bitcoin::secp256k1::rand::{self, RngCore}; -use bitcoin::secp256k1::{All, Secp256k1}; +use bitcoin::secp256k1::{rand, All, Secp256k1}; #[cfg(target_arch = "wasm32")] use instant::SystemTime; use once_cell::sync::Lazy; @@ -24,15 +21,7 @@ pub static SECP256K1: Lazy> = Lazy::new(|| { ctx }); -pub fn random_hash() -> Vec { - let mut rng = rand::thread_rng(); - let mut random_bytes: [u8; 32] = [0u8; Sha256::LEN]; - rng.fill_bytes(&mut random_bytes); - - let hash = Sha256::hash(&random_bytes); - hash.to_byte_array().to_vec() -} - +/// Seconds since unix epoch pub fn unix_time() -> u64 { SystemTime::now() .duration_since(UNIX_EPOCH)