From 5bf0358f423922f2188ae0d0dffbc304d2204002 Mon Sep 17 00:00:00 2001 From: MSGhais Date: Wed, 7 Aug 2024 18:10:26 +0200 Subject: [PATCH 1/7] start compute block reward impl --- src/lib.cairo | 1 + src/utils.cairo | 66 ++++++++++++++++++++++++++++++++++++++++++++ src/validation.cairo | 44 ++++++++++++++++++++++++++++- 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 src/utils.cairo diff --git a/src/lib.cairo b/src/lib.cairo index 8abc785f..84e12e93 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -1,3 +1,4 @@ mod state; mod validation; mod main; +mod utils; diff --git a/src/utils.cairo b/src/utils.cairo new file mode 100644 index 00000000..92a31d55 --- /dev/null +++ b/src/utils.cairo @@ -0,0 +1,66 @@ +// Joyboy utils for Pow2 +trait Pow2 { + #[inline] + fn pow2(n: N) -> V; +} + +impl Pow2u8u8 of Pow2 { + fn pow2(n: u8) -> u8 { + match n { + 0 => 0b1, + 1 => 0b10, + 2 => 0b100, + 3 => 0b1000, + 4 => 0b10000, + 5 => 0b100000, + 6 => 0b1000000, + 7 => 0b10000000, + _ => core::panic_with_felt252('n ouf of range'), + } + } +} + +impl Pow2u32u32 of Pow2 { + fn pow2(n: u32) -> u32 { + match n { + 0 => 0b1, + 1 => 0b10, + 2 => 0b100, + 3 => 0b1000, + 4 => 0b10000, + 5 => 0b100000, + 6 => 0b1000000, + 7 => 0b10000000, + 8 => 0b100000000, + 9 => 0b1000000000, + 10 => 0b10000000000, + 11 => 0b100000000000, + 12 => 0b1000000000000, + 13 => 0b10000000000000, + 14 => 0b100000000000000, + 15 => 0b1000000000000000, + 16 => 0b10000000000000000, + 17 => 0b100000000000000000, + 18 => 0b1000000000000000000, + 19 => 0b10000000000000000000, + 20 => 0b100000000000000000000, + 21 => 0b1000000000000000000000, + 22 => 0b10000000000000000000000, + 23 => 0b100000000000000000000000, + 24 => 0b1000000000000000000000000, + 25 => 0b10000000000000000000000000, + 26 => 0b100000000000000000000000000, + 27 => 0b1000000000000000000000000000, + 28 => 0b10000000000000000000000000000, + 29 => 0b100000000000000000000000000000, + 30 => 0b1000000000000000000000000000000, + 31 => 0b10000000000000000000000000000000, + _ => core::panic_with_felt252('n ouf of range'), + } + } +} + +#[inline] +pub fn shr, +Div, +Pow2>(v: V, n: N) -> V { + v / Pow2::pow2(n.into()) +} diff --git a/src/validation.cairo b/src/validation.cairo index 2b65e832..b6640369 100644 --- a/src/validation.cairo +++ b/src/validation.cairo @@ -1,4 +1,5 @@ use super::state::{Block, ChainState, UtreexoState}; +use raito::utils::{shr}; #[generate_trait] impl BlockValidatorImpl of BlockValidator { @@ -70,9 +71,25 @@ fn validate_merkle_root(self: @ChainState, block: @Block) -> Result<(), ByteArra Result::Ok(()) } +fn compute_block_reward(self: @ChainState, block: @Block) -> Result { + let number_halvings: u32 = *self.block_height / 210000; + match number_halvings { + 0 => { return Result::Err("number_halvings equal 0"); }, + _ => {} + } + let denominator: u32 = shr(number_halvings, 2); + match denominator { + 0 => { return Result::Err("denominator = 0"); }, + _ => { println!("compute block reward"); } + } + let pow_of = 8_u32; + let mul: u32 = (50_u32 * 10_u32 * *@pow_of); + Result::Ok((mul / denominator).try_into().unwrap()) +} + #[cfg(test)] mod tests { - use super::{validate_target, validate_timestamp}; + use super::{validate_target, validate_timestamp, compute_block_reward}; use super::{Block, ChainState, UtreexoState}; use super::super::state::{Header, Transaction, TxIn, TxOut}; @@ -140,4 +157,29 @@ mod tests { let result = validate_timestamp(@chain_state, @block); assert!(result.is_err(), "Median time is greater than block's timestamp"); } + + // TODO finish test + #[test] + fn test_compute_block_reward() { + // TODO add correct state to compute block reward + let mut chain_state = ChainState { + block_height: 1, + total_work: 1, + best_block_hash: 1, + current_target: 1, + epoch_start_time: 1, + prev_timestamps: array![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].span(), + utreexo_state: UtreexoState { roots: array![].span() }, + }; + let mut block = Block { + header: Header { + version: 1, prev_block_hash: 1, merkle_root_hash: 1, time: 12, bits: 1, nonce: 1, + }, + txs: ArrayTrait::new().span(), + }; + // let result = validate_timestamp(@chain_state, @block); + // TODO add correct state to compute block reward + let result_block = compute_block_reward(@chain_state, @block); + assert!(result_block.is_err(), "number_halvings equal 0"); + } } From dbf199b5bc4bd1d11a85fbf8d927f50ce02c91a7 Mon Sep 17 00:00:00 2001 From: MSGhais Date: Wed, 7 Aug 2024 18:42:38 +0200 Subject: [PATCH 2/7] fix and clean --- src/lib.cairo | 1 - src/utils.cairo | 66 -------------------------------------------- src/validation.cairo | 8 ++---- 3 files changed, 3 insertions(+), 72 deletions(-) delete mode 100644 src/utils.cairo diff --git a/src/lib.cairo b/src/lib.cairo index 84e12e93..8abc785f 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -1,4 +1,3 @@ mod state; mod validation; mod main; -mod utils; diff --git a/src/utils.cairo b/src/utils.cairo deleted file mode 100644 index 92a31d55..00000000 --- a/src/utils.cairo +++ /dev/null @@ -1,66 +0,0 @@ -// Joyboy utils for Pow2 -trait Pow2 { - #[inline] - fn pow2(n: N) -> V; -} - -impl Pow2u8u8 of Pow2 { - fn pow2(n: u8) -> u8 { - match n { - 0 => 0b1, - 1 => 0b10, - 2 => 0b100, - 3 => 0b1000, - 4 => 0b10000, - 5 => 0b100000, - 6 => 0b1000000, - 7 => 0b10000000, - _ => core::panic_with_felt252('n ouf of range'), - } - } -} - -impl Pow2u32u32 of Pow2 { - fn pow2(n: u32) -> u32 { - match n { - 0 => 0b1, - 1 => 0b10, - 2 => 0b100, - 3 => 0b1000, - 4 => 0b10000, - 5 => 0b100000, - 6 => 0b1000000, - 7 => 0b10000000, - 8 => 0b100000000, - 9 => 0b1000000000, - 10 => 0b10000000000, - 11 => 0b100000000000, - 12 => 0b1000000000000, - 13 => 0b10000000000000, - 14 => 0b100000000000000, - 15 => 0b1000000000000000, - 16 => 0b10000000000000000, - 17 => 0b100000000000000000, - 18 => 0b1000000000000000000, - 19 => 0b10000000000000000000, - 20 => 0b100000000000000000000, - 21 => 0b1000000000000000000000, - 22 => 0b10000000000000000000000, - 23 => 0b100000000000000000000000, - 24 => 0b1000000000000000000000000, - 25 => 0b10000000000000000000000000, - 26 => 0b100000000000000000000000000, - 27 => 0b1000000000000000000000000000, - 28 => 0b10000000000000000000000000000, - 29 => 0b100000000000000000000000000000, - 30 => 0b1000000000000000000000000000000, - 31 => 0b10000000000000000000000000000000, - _ => core::panic_with_felt252('n ouf of range'), - } - } -} - -#[inline] -pub fn shr, +Div, +Pow2>(v: V, n: N) -> V { - v / Pow2::pow2(n.into()) -} diff --git a/src/validation.cairo b/src/validation.cairo index b6640369..14a366f0 100644 --- a/src/validation.cairo +++ b/src/validation.cairo @@ -1,5 +1,4 @@ use super::state::{Block, ChainState, UtreexoState}; -use raito::utils::{shr}; #[generate_trait] impl BlockValidatorImpl of BlockValidator { @@ -77,14 +76,13 @@ fn compute_block_reward(self: @ChainState, block: @Block) -> Result { return Result::Err("number_halvings equal 0"); }, _ => {} } - let denominator: u32 = shr(number_halvings, 2); + let denominator: u32 = number_halvings*number_halvings; match denominator { 0 => { return Result::Err("denominator = 0"); }, _ => { println!("compute block reward"); } } - let pow_of = 8_u32; - let mul: u32 = (50_u32 * 10_u32 * *@pow_of); - Result::Ok((mul / denominator).try_into().unwrap()) + let num: u32 = (50_u32 * 10_u32) ** @8_u32; + Result::Ok((num / denominator).try_into().unwrap()) } #[cfg(test)] From dc4653275f1f37a988ae6de8f201590e246565d0 Mon Sep 17 00:00:00 2001 From: MSGhais Date: Thu, 8 Aug 2024 00:54:58 +0200 Subject: [PATCH 3/7] change request + add test --- src/validation.cairo | 67 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/src/validation.cairo b/src/validation.cairo index b7d41226..368d4238 100644 --- a/src/validation.cairo +++ b/src/validation.cairo @@ -2,6 +2,8 @@ use super::utils::{shl, shr}; use super::state::{Block, ChainState, Transaction, UtreexoState}; const MAX_TARGET: u256 = 0x00000000FFFF0000000000000000000000000000000000000000000000000000; +pub const REWARD_INITIAL: u256 = 50; // 50 BTC in satoshis => 5000000000 SATS +pub const POW_SATS_AMOUNT: u256 = 8; // Pow to convert in SATS #[generate_trait] impl BlockValidatorImpl of BlockValidator { @@ -106,20 +108,27 @@ fn validate_merkle_root(self: @ChainState, block: @Block) -> Result<(), ByteArra Result::Ok(()) } -fn compute_block_reward(self: @ChainState, block: @Block) -> Result { - let number_halvings: u32 = *self.block_height / 210000; +// Return BTC reward => pow to 8 to transform into Sats. Otherwise difficult to cast to u64 correctly after if needed +fn compute_block_reward(block_height: u32) -> Result { + let number_halvings = block_height / 210_000; match number_halvings { 0 => { return Result::Err("number_halvings equal 0"); }, _ => {} } - let denominator: u32 = number_halvings*number_halvings; - match denominator { + // Simple way to do it, but breaking the final part of the test + // let subsidy_initial: u256 = REWARD_INITIAL; + // // let current_reward = subsidy_initial >> number_halvings; + // let current_reward = shr(subsidy_initial, number_halvings); + // Result::Ok((current_reward).try_into().unwrap()) + let cast_number_halvings: u256 = number_halvings.try_into().unwrap(); + let denominator = shr(cast_number_halvings, 2); + match denominator.try_into().unwrap() { 0 => { return Result::Err("denominator = 0"); }, - _ => { println!("compute block reward"); } + _ => { } } - let num: u32 = (50_u32 * 10_u32) ** @8_u32; - Result::Ok((num / denominator).try_into().unwrap()) - + let subsidy_initial: u256 = REWARD_INITIAL; + let current_reward = subsidy_initial / denominator; + Result::Ok((current_reward).try_into().unwrap()) } pub fn target_to_bits(target: u256) -> Result { @@ -190,7 +199,10 @@ fn validate_coinbase(block: @Block, total_fees: u256) -> Result<(), ByteArray> { #[cfg(test)] mod tests { - use super::{validate_target, validate_timestamp, validate_proof_of_work}; + use super::{ + validate_target, validate_timestamp, validate_proof_of_work, compute_block_reward, shr, + REWARD_INITIAL, POW_SATS_AMOUNT + }; use super::{Block, ChainState, UtreexoState}; use super::super::state::{Header, Transaction, TxIn, TxOut}; @@ -284,4 +296,41 @@ mod tests { let result = validate_proof_of_work(@10_u256, @block); assert!(result.is_ok(), "Expect prev block hash lt target"); } + + + #[test] + fn test_compute_block_reward() { + let max_halvings: u32 = 64; + let reward_initial: u32 = REWARD_INITIAL.try_into().unwrap(); + let halving_block_range = 210_000; // every 210 000 blocks + let mut nprevious_subsidy = REWARD_INITIAL * 2; + let mut loop_index: u32 = 0; + loop { + if loop_index == max_halvings { + break; + } + let block_height: u32 = loop_index * halving_block_range; + let reward = compute_block_reward(block_height); + if let Result::Ok(r) = reward { + let cast_reward_initial: u64 = reward_initial.try_into().unwrap(); + assert!(r <= cast_reward_initial); + // TODO check this assert + // let cast_reward_previous: u64 = nprevious_subsidy.try_into().unwrap(); + // assert_eq!(r, cast_reward_previous/2); + nprevious_subsidy = r.try_into().unwrap(); + } + loop_index = loop_index + 1; + }; + let last_reward = compute_block_reward(max_halvings); + if let Result::Ok(r) = last_reward { + assert_eq!(r, 0); + }; + + let height_test = compute_block_reward(300000); + if let Result::Ok(r) = height_test { + println!("last reward compute {:?}", r); + let cast_r: u256 = r.try_into().unwrap(); + assert_eq!(shr(cast_r, 8), 250000000); + }; + } } From 22cad3547812b83d452bfaec3336b698f91e811e Mon Sep 17 00:00:00 2001 From: MSGhais Date: Thu, 8 Aug 2024 11:34:07 +0200 Subject: [PATCH 4/7] add comments + fix text + refaco compute block reward --- src/validation.cairo | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/validation.cairo b/src/validation.cairo index e190f596..d7b28921 100644 --- a/src/validation.cairo +++ b/src/validation.cairo @@ -208,6 +208,16 @@ fn validate_coinbase(block: @Block, total_fees: u256) -> Result<(), ByteArray> { Result::Ok(()) } +// Return BTC reward => pow to 8 to transform into Sats. Otherwise difficult to cast to u64 correctly after if needed +fn compute_block_reward(block_height: u32) -> Result { + let number_halvings = block_height / 210_000; + match number_halvings { + 0 => { return Result::Ok(REWARD_INITIAL.try_into().unwrap()); }, // return REWARD_INITAL + _ => {} + } + let current_reward = shr(REWARD_INITIAL, number_halvings); + Result::Ok((current_reward).try_into().unwrap()) +} #[cfg(test)] mod tests { use super::{ @@ -309,6 +319,7 @@ mod tests { } + // Ref implementation here: https://github.com/bitcoin/bitcoin/blob/0f68a05c084bef3e53e3f549c403bc90b1db319c/src/test/validation_tests.cpp#L24 #[test] fn test_compute_block_reward() { let max_halvings: u32 = 64; @@ -316,32 +327,30 @@ mod tests { let halving_block_range = 210_000; // every 210 000 blocks let mut nprevious_subsidy = REWARD_INITIAL * 2; let mut loop_index: u32 = 0; + assert_eq!(nprevious_subsidy.try_into().unwrap(), reward_initial*2); + + // Testing all halvings rewards possible loop { if loop_index == max_halvings { break; } let block_height: u32 = loop_index * halving_block_range; + // Compute reward let reward = compute_block_reward(block_height); + // Check if reward is decrease when another halving is in process. if let Result::Ok(r) = reward { let cast_reward_initial: u64 = reward_initial.try_into().unwrap(); assert!(r <= cast_reward_initial); - // TODO check this assert - // let cast_reward_previous: u64 = nprevious_subsidy.try_into().unwrap(); - // assert_eq!(r, cast_reward_previous/2); + let cast_reward_previous: u64 = nprevious_subsidy.try_into().unwrap(); + assert_eq!(r, cast_reward_previous/2); nprevious_subsidy = r.try_into().unwrap(); } loop_index = loop_index + 1; }; - let last_reward = compute_block_reward(max_halvings); + // Last halving with 0 block reward + let last_reward = compute_block_reward(max_halvings*halving_block_range); if let Result::Ok(r) = last_reward { assert_eq!(r, 0); }; - - let height_test = compute_block_reward(300000); - if let Result::Ok(r) = height_test { - println!("last reward compute {:?}", r); - let cast_r: u256 = r.try_into().unwrap(); - assert_eq!(shr(cast_r, 8), 250000000); - }; } } From abb9b83f67e4520daf2f0768a3420f8cd5d718c9 Mon Sep 17 00:00:00 2001 From: MSGhais Date: Thu, 8 Aug 2024 14:49:56 +0200 Subject: [PATCH 5/7] fix request => no result + sats value returned --- src/validation.cairo | 51 +++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/validation.cairo b/src/validation.cairo index d7b28921..b230d199 100644 --- a/src/validation.cairo +++ b/src/validation.cairo @@ -208,20 +208,26 @@ fn validate_coinbase(block: @Block, total_fees: u256) -> Result<(), ByteArray> { Result::Ok(()) } -// Return BTC reward => pow to 8 to transform into Sats. Otherwise difficult to cast to u64 correctly after if needed -fn compute_block_reward(block_height: u32) -> Result { +// Return BTC reward in SATS +fn compute_block_reward(block_height: u32) -> u64 { let number_halvings = block_height / 210_000; match number_halvings { - 0 => { return Result::Ok(REWARD_INITIAL.try_into().unwrap()); }, // return REWARD_INITAL + 0 => { + return shl(REWARD_INITIAL, POW_SATS_AMOUNT.try_into().unwrap()).try_into().unwrap(); + }, // return REWARD_INITAL _ => {} } - let current_reward = shr(REWARD_INITIAL, number_halvings); - Result::Ok((current_reward).try_into().unwrap()) + // Calculate BTC to SATS amount + let sats_init_amount = shl(REWARD_INITIAL, POW_SATS_AMOUNT.try_into().unwrap()); + // Shift right to divide by number_halvings + let current_reward = shr(sats_init_amount, number_halvings); + // Convert into a u64 + (current_reward).try_into().unwrap() } #[cfg(test)] mod tests { use super::{ - validate_target, validate_timestamp, validate_proof_of_work, compute_block_reward, shr, + validate_target, validate_timestamp, validate_proof_of_work, compute_block_reward, shr, shl, REWARD_INITIAL, POW_SATS_AMOUNT }; use super::{Block, ChainState, UtreexoState}; @@ -318,17 +324,20 @@ mod tests { assert!(result.is_ok(), "Expect prev block hash lt target"); } - - // Ref implementation here: https://github.com/bitcoin/bitcoin/blob/0f68a05c084bef3e53e3f549c403bc90b1db319c/src/test/validation_tests.cpp#L24 + // Ref implementation here: + // https://github.com/bitcoin/bitcoin/blob/0f68a05c084bef3e53e3f549c403bc90b1db319c/src/test/validation_tests.cpp#L24 #[test] fn test_compute_block_reward() { let max_halvings: u32 = 64; - let reward_initial: u32 = REWARD_INITIAL.try_into().unwrap(); + let reward_initial: u256 = shl( + REWARD_INITIAL.try_into().unwrap(), POW_SATS_AMOUNT.try_into().unwrap() + ); let halving_block_range = 210_000; // every 210 000 blocks - let mut nprevious_subsidy = REWARD_INITIAL * 2; + let mut nprevious_subsidy: u256 = shl( + REWARD_INITIAL.try_into().unwrap() * 2, POW_SATS_AMOUNT.try_into().unwrap() + ); let mut loop_index: u32 = 0; - assert_eq!(nprevious_subsidy.try_into().unwrap(), reward_initial*2); - + assert_eq!(nprevious_subsidy.try_into().unwrap(), reward_initial * 2); // Testing all halvings rewards possible loop { if loop_index == max_halvings { @@ -337,20 +346,14 @@ mod tests { let block_height: u32 = loop_index * halving_block_range; // Compute reward let reward = compute_block_reward(block_height); - // Check if reward is decrease when another halving is in process. - if let Result::Ok(r) = reward { - let cast_reward_initial: u64 = reward_initial.try_into().unwrap(); - assert!(r <= cast_reward_initial); - let cast_reward_previous: u64 = nprevious_subsidy.try_into().unwrap(); - assert_eq!(r, cast_reward_previous/2); - nprevious_subsidy = r.try_into().unwrap(); - } + assert!(reward <= reward_initial.try_into().unwrap()); + let cast_nprevious_subsidy: u64 = nprevious_subsidy.try_into().unwrap(); + assert_eq!(reward, cast_nprevious_subsidy / 2); + nprevious_subsidy = reward.try_into().unwrap(); loop_index = loop_index + 1; }; // Last halving with 0 block reward - let last_reward = compute_block_reward(max_halvings*halving_block_range); - if let Result::Ok(r) = last_reward { - assert_eq!(r, 0); - }; + let last_reward = compute_block_reward(max_halvings * halving_block_range); + assert_eq!(last_reward, 0); } } From eee076a663d153cece8192ab1871b4c134a28fa6 Mon Sep 17 00:00:00 2001 From: MSGhais Date: Thu, 8 Aug 2024 15:30:23 +0200 Subject: [PATCH 6/7] add simple test before loop possible halvings --- src/validation.cairo | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/validation.cairo b/src/validation.cairo index b230d199..9c8a2a38 100644 --- a/src/validation.cairo +++ b/src/validation.cairo @@ -336,24 +336,41 @@ mod tests { let mut nprevious_subsidy: u256 = shl( REWARD_INITIAL.try_into().unwrap() * 2, POW_SATS_AMOUNT.try_into().unwrap() ); - let mut loop_index: u32 = 0; + let mut halving_index: u32 = 0; assert_eq!(nprevious_subsidy.try_into().unwrap(), reward_initial * 2); + + // First halving block reward : initial supply in SATS + let first_halving_reward = compute_block_reward(halving_index * halving_block_range); + assert_eq!(first_halving_reward, reward_initial.try_into().unwrap()); + + // Second halving block reward : initial supply in SATS + let second_halving_reward = compute_block_reward((halving_index + 1) * halving_block_range); + assert_eq!(second_halving_reward, reward_initial.try_into().unwrap() / 2); + + // Test the reward when we have 5 halvings + let five_halving_reward = compute_block_reward((halving_index + 5) * halving_block_range); + let five_reward_amount = shr(reward_initial.try_into().unwrap(), halving_index + 5) + .try_into() + .unwrap(); + assert_eq!(five_halving_reward, five_reward_amount); + + // Last halving block reward = 0 + let last_reward = compute_block_reward(max_halvings * halving_block_range); + assert_eq!(last_reward, 0); + // Testing all halvings rewards possible loop { - if loop_index == max_halvings { + if halving_index == max_halvings { break; } - let block_height: u32 = loop_index * halving_block_range; + let block_height: u32 = halving_index * halving_block_range; // Compute reward let reward = compute_block_reward(block_height); assert!(reward <= reward_initial.try_into().unwrap()); let cast_nprevious_subsidy: u64 = nprevious_subsidy.try_into().unwrap(); assert_eq!(reward, cast_nprevious_subsidy / 2); nprevious_subsidy = reward.try_into().unwrap(); - loop_index = loop_index + 1; + halving_index = halving_index + 1; }; - // Last halving with 0 block reward - let last_reward = compute_block_reward(max_halvings * halving_block_range); - assert_eq!(last_reward, 0); } } From c3315bd50409e66c7ad6fc09acb4a0824e47fe98 Mon Sep 17 00:00:00 2001 From: MSGhais Date: Thu, 8 Aug 2024 21:57:27 +0200 Subject: [PATCH 7/7] one line + clean test --- src/validation.cairo | 92 ++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 55 deletions(-) diff --git a/src/validation.cairo b/src/validation.cairo index 9c8a2a38..25f83f2e 100644 --- a/src/validation.cairo +++ b/src/validation.cairo @@ -210,19 +210,7 @@ fn validate_coinbase(block: @Block, total_fees: u256) -> Result<(), ByteArray> { // Return BTC reward in SATS fn compute_block_reward(block_height: u32) -> u64 { - let number_halvings = block_height / 210_000; - match number_halvings { - 0 => { - return shl(REWARD_INITIAL, POW_SATS_AMOUNT.try_into().unwrap()).try_into().unwrap(); - }, // return REWARD_INITAL - _ => {} - } - // Calculate BTC to SATS amount - let sats_init_amount = shl(REWARD_INITIAL, POW_SATS_AMOUNT.try_into().unwrap()); - // Shift right to divide by number_halvings - let current_reward = shr(sats_init_amount, number_halvings); - // Convert into a u64 - (current_reward).try_into().unwrap() + shr(5000000000, block_height / 210_000).try_into().unwrap() } #[cfg(test)] mod tests { @@ -329,48 +317,42 @@ mod tests { #[test] fn test_compute_block_reward() { let max_halvings: u32 = 64; - let reward_initial: u256 = shl( - REWARD_INITIAL.try_into().unwrap(), POW_SATS_AMOUNT.try_into().unwrap() - ); - let halving_block_range = 210_000; // every 210 000 blocks - let mut nprevious_subsidy: u256 = shl( - REWARD_INITIAL.try_into().unwrap() * 2, POW_SATS_AMOUNT.try_into().unwrap() - ); - let mut halving_index: u32 = 0; - assert_eq!(nprevious_subsidy.try_into().unwrap(), reward_initial * 2); - - // First halving block reward : initial supply in SATS - let first_halving_reward = compute_block_reward(halving_index * halving_block_range); - assert_eq!(first_halving_reward, reward_initial.try_into().unwrap()); - - // Second halving block reward : initial supply in SATS - let second_halving_reward = compute_block_reward((halving_index + 1) * halving_block_range); - assert_eq!(second_halving_reward, reward_initial.try_into().unwrap() / 2); - - // Test the reward when we have 5 halvings - let five_halving_reward = compute_block_reward((halving_index + 5) * halving_block_range); - let five_reward_amount = shr(reward_initial.try_into().unwrap(), halving_index + 5) - .try_into() - .unwrap(); - assert_eq!(five_halving_reward, five_reward_amount); - - // Last halving block reward = 0 - let last_reward = compute_block_reward(max_halvings * halving_block_range); - assert_eq!(last_reward, 0); + let reward_initial: u256 = 5000000000; + let mut block_height = 210_000; // halving every 210 000 blocks + // Before first halving + let genesis_halving_reward = compute_block_reward(0); + assert_eq!(genesis_halving_reward, reward_initial.try_into().unwrap()); - // Testing all halvings rewards possible - loop { - if halving_index == max_halvings { - break; - } - let block_height: u32 = halving_index * halving_block_range; - // Compute reward - let reward = compute_block_reward(block_height); - assert!(reward <= reward_initial.try_into().unwrap()); - let cast_nprevious_subsidy: u64 = nprevious_subsidy.try_into().unwrap(); - assert_eq!(reward, cast_nprevious_subsidy / 2); - nprevious_subsidy = reward.try_into().unwrap(); - halving_index = halving_index + 1; - }; + // Before first halving + assert_eq!(compute_block_reward(209999), reward_initial.try_into().unwrap()); + + // First halving + let first_halving_reward = compute_block_reward(block_height); + assert_eq!(first_halving_reward, reward_initial.try_into().unwrap() / 2); + + // Second halving + assert_eq!(compute_block_reward(420000), 1250000000); // 12.5 BTC + + // Third halving + assert_eq!(compute_block_reward(630000), 625000000); // 6.25 + + // Just after fourth halving + assert_eq!(compute_block_reward(840001), 312500000); // 3.125 + + // Fight halving + assert_eq!(compute_block_reward(1050000), 156250000); // 1.5625 + + // Seventh halving + assert_eq!(compute_block_reward(1470000), 39062500); // 0.390625 + + // Ninth halving + assert_eq!(compute_block_reward(1890000), 9765625); // 0.09765625 + + // Tenth halving + let tenth_reward = compute_block_reward(10 * block_height); + assert_eq!(tenth_reward, 4882812); // 0.048828125 + + let last_reward = compute_block_reward(max_halvings * block_height); + assert_eq!(last_reward, 0); } }