From 6821ba97912a52cc6be13c9cc5337da6644215d9 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Fri, 28 Jan 2022 19:14:46 -0300 Subject: [PATCH] remove founders reward code (#3430) * remove founders reward code * panic if a block before Canopy is validated for subsidy Co-Authored-By: Daira Hopwood Co-authored-by: Daira Hopwood --- zebra-consensus/src/block/check.rs | 16 +--- zebra-consensus/src/block/subsidy.rs | 2 - .../src/block/subsidy/founders_reward.rs | 63 ------------- .../src/block/subsidy/funding_streams.rs | 2 +- zebra-consensus/src/block/subsidy/general.rs | 94 ------------------- zebra-consensus/src/block/tests.rs | 74 ++++----------- zebra-consensus/src/error.rs | 3 - zebra-consensus/src/parameters/subsidy.rs | 9 +- 8 files changed, 23 insertions(+), 240 deletions(-) delete mode 100644 zebra-consensus/src/block/subsidy/founders_reward.rs diff --git a/zebra-consensus/src/block/check.rs b/zebra-consensus/src/block/check.rs index 733b18ca9..dfbdb4dd6 100644 --- a/zebra-consensus/src/block/check.rs +++ b/zebra-consensus/src/block/check.rs @@ -107,7 +107,7 @@ pub fn subsidy_is_valid(block: &Block, network: Network) -> Result<(), BlockErro let height = block.coinbase_height().ok_or(SubsidyError::NoCoinbase)?; let coinbase = block.transactions.get(0).ok_or(SubsidyError::NoCoinbase)?; - // Validate founders reward and funding streams + // Validate funding streams let halving_div = subsidy::general::halving_divisor(height, network); let canopy_activation_height = NetworkUpgrade::Canopy .activation_height(network) @@ -121,17 +121,9 @@ pub fn subsidy_is_valid(block: &Block, network: Network) -> Result<(), BlockErro } else if halving_div.count_ones() != 1 { unreachable!("invalid halving divisor: the halving divisor must be a non-zero power of two") } else if height < canopy_activation_height { - // Founders rewards are paid up to Canopy activation, on both mainnet and testnet - let founders_reward = subsidy::founders_reward::founders_reward(height, network) - .expect("invalid Amount: founders reward should be valid"); - let matching_values = subsidy::general::find_output_with_amount(coinbase, founders_reward); - - // TODO: the exact founders reward value must be sent as a single output to the correct address - if !matching_values.is_empty() { - Ok(()) - } else { - Err(SubsidyError::FoundersRewardNotFound)? - } + // Founders rewards are paid up to Canopy activation, on both mainnet and testnet. + // But we checkpoint in Canopy so founders reward does not apply for Zebra. + unreachable!("we cannot verify consensus rules before Canopy activation"); } else if halving_div < 4 { // Funding streams are paid from Canopy activation to the second halving // Note: Canopy activation is at the first halving on mainnet, but not on testnet diff --git a/zebra-consensus/src/block/subsidy.rs b/zebra-consensus/src/block/subsidy.rs index 1ad757251..c9deeca6f 100644 --- a/zebra-consensus/src/block/subsidy.rs +++ b/zebra-consensus/src/block/subsidy.rs @@ -2,8 +2,6 @@ //! //! [7.8]: https://zips.z.cash/protocol/protocol.pdf#subsidies -/// Founders' Reward functions apply for blocks before Canopy. -pub mod founders_reward; /// Funding Streams functions apply for blocks at and after Canopy. pub mod funding_streams; /// General subsidy functions apply for blocks after slow-start mining. diff --git a/zebra-consensus/src/block/subsidy/founders_reward.rs b/zebra-consensus/src/block/subsidy/founders_reward.rs deleted file mode 100644 index 17f3e3c38..000000000 --- a/zebra-consensus/src/block/subsidy/founders_reward.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! Founders' Reward calculations. - [§7.8][7.8] -//! -//! [7.8]: https://zips.z.cash/protocol/protocol.pdf#subsidies - -use std::convert::TryFrom; - -use zebra_chain::{ - amount::{Amount, Error, NonNegative}, - block::Height, - parameters::Network, -}; - -use crate::block::subsidy::general::{block_subsidy, halving_divisor}; -use crate::parameters::subsidy::FOUNDERS_FRACTION_DIVISOR; - -/// `FoundersReward(height)` as described in [protocol specification §7.8][7.8] -/// -/// [7.8]: https://zips.z.cash/protocol/protocol.pdf#subsidies -pub fn founders_reward(height: Height, network: Network) -> Result, Error> { - if halving_divisor(height, network) == 1 { - // this calculation is exact, because the block subsidy is divisible by - // the FOUNDERS_FRACTION_DIVISOR until long after the first halving - block_subsidy(height, network)? / FOUNDERS_FRACTION_DIVISOR - } else { - Amount::try_from(0) - } -} - -#[cfg(test)] -mod test { - use super::*; - use color_eyre::Report; - use zebra_chain::parameters::NetworkUpgrade::*; - #[test] - fn test_founders_reward() -> Result<(), Report> { - zebra_test::init(); - - let network = Network::Mainnet; - let blossom_height = Blossom.activation_height(network).unwrap(); - let canopy_height = Canopy.activation_height(network).unwrap(); - - // Founders reward is 20% of the block subsidy - // https://z.cash/support/faq/#founders-reward-recipients - // Before Blossom this is 20*12.5/100 = 2.5 ZEC - assert_eq!( - Amount::try_from(250_000_000), - founders_reward((blossom_height - 1).unwrap(), network) - ); - // Founders reward is still 20% of the block subsidy but the block reward is half what it was - // After Blossom this is 20*6.25/100 = 1.25 ZEC - // https://z.cash/upgrade/blossom/ - assert_eq!( - Amount::try_from(125_000_000), - founders_reward(blossom_height, network) - ); - - // After first halving(coinciding with Canopy) founders reward will expire - // https://z.cash/support/faq/#does-the-founders-reward-expire - assert_eq!(Amount::try_from(0), founders_reward(canopy_height, network)); - - Ok(()) - } -} diff --git a/zebra-consensus/src/block/subsidy/funding_streams.rs b/zebra-consensus/src/block/subsidy/funding_streams.rs index 6b6937683..ed0909700 100644 --- a/zebra-consensus/src/block/subsidy/funding_streams.rs +++ b/zebra-consensus/src/block/subsidy/funding_streams.rs @@ -119,7 +119,7 @@ pub fn funding_stream_address( Address::from_str(address).expect("Address should deserialize") } -/// Given a founders reward address, create a script and check if it is the same +/// Given a funding stream address, create a script and check if it is the same /// as the given lock_script as described in [protocol specification §7.10][7.10] /// /// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams. diff --git a/zebra-consensus/src/block/subsidy/general.rs b/zebra-consensus/src/block/subsidy/general.rs index 02856ccb4..045ea5777 100644 --- a/zebra-consensus/src/block/subsidy/general.rs +++ b/zebra-consensus/src/block/subsidy/general.rs @@ -9,7 +9,6 @@ use zebra_chain::{ block::Height, parameters::{Network, NetworkUpgrade::*}, transaction::Transaction, - transparent, }; use crate::parameters::subsidy::*; @@ -69,41 +68,7 @@ pub fn block_subsidy(height: Height, network: Network) -> Result>, -) -> Result, Error> { - if let Some(non_miner_reward) = non_miner_reward { - block_subsidy(height, network)? - non_miner_reward - } else { - block_subsidy(height, network) - } -} - -/// Returns a list of outputs in `Transaction`, which have a value equal to `Amount`. -pub fn find_output_with_amount( - transaction: &Transaction, - amount: Amount, -) -> Vec { - // TODO: shielded coinbase - Heartwood - transaction - .outputs() - .iter() - .filter(|o| o.value == amount) - .cloned() - .collect() -} - /// Returns all output amounts in `Transaction`. -#[allow(dead_code)] pub fn output_amounts(transaction: &Transaction) -> HashSet> { transaction .outputs() @@ -118,11 +83,6 @@ mod test { use super::*; use color_eyre::Report; - use crate::block::subsidy::{ - founders_reward::founders_reward, - funding_streams::{funding_stream_values, height_for_first_halving}, - }; - #[test] fn halving_test() -> Result<(), Report> { zebra_test::init(); @@ -300,58 +260,4 @@ mod test { Ok(()) } - - #[test] - fn miner_subsidy_test() -> Result<(), Report> { - zebra_test::init(); - - miner_subsidy_for_network(Network::Mainnet)?; - miner_subsidy_for_network(Network::Testnet)?; - - Ok(()) - } - - fn miner_subsidy_for_network(network: Network) -> Result<(), Report> { - let blossom_height = Blossom.activation_height(network).unwrap(); - let first_halving_height = height_for_first_halving(network); - - // Miner reward before Blossom is 80% of the total block reward - // 80*12.5/100 = 10 ZEC - let founders_amount = founders_reward((blossom_height - 1).unwrap(), network)?; - assert_eq!( - Amount::try_from(1_000_000_000), - miner_subsidy( - (blossom_height - 1).unwrap(), - network, - Some(founders_amount) - ) - ); - - // After blossom the total block reward is "halved", miner still get 80% - // 80*6.25/100 = 5 ZEC - let founders_amount = founders_reward(blossom_height, network)?; - assert_eq!( - Amount::try_from(500_000_000), - miner_subsidy(blossom_height, network, Some(founders_amount)) - ); - - // After first halving, miner will get 2.5 ZEC per mined block (not counting fees) - let funding_stream_values = funding_stream_values(first_halving_height, network)? - .iter() - .map(|row| row.1) - .sum::, Error>>() - .unwrap(); - - assert_eq!( - Amount::try_from(250_000_000), - miner_subsidy(first_halving_height, network, Some(funding_stream_values)) - ); - - // TODO: After second halving, there will be no funding streams, and - // miners will get all the block reward - - // TODO: also add some very large halvings - - Ok(()) - } } diff --git a/zebra-consensus/src/block/tests.rs b/zebra-consensus/src/block/tests.rs index a88a1d17b..bb246bbcb 100644 --- a/zebra-consensus/src/block/tests.rs +++ b/zebra-consensus/src/block/tests.rs @@ -22,10 +22,7 @@ use zebra_chain::{ use zebra_script::CachedFfiTransaction; use zebra_test::transcript::{ExpectedTranscriptError, Transcript}; -use crate::{ - parameters::{SLOW_START_INTERVAL, SLOW_START_SHIFT}, - script, transaction, -}; +use crate::{parameters::SLOW_START_SHIFT, script, transaction}; use super::*; @@ -294,8 +291,12 @@ fn subsidy_is_valid_for_network(network: Network) -> Result<(), Report> { .zcash_deserialize_into::() .expect("block is structurally valid"); + let canopy_activation_height = NetworkUpgrade::Canopy + .activation_height(network) + .expect("Canopy activation height is known"); + // TODO: first halving, second halving, third halving, and very large halvings - if block::Height(height) > SLOW_START_INTERVAL { + if block::Height(height) >= canopy_activation_height { check::subsidy_is_valid(&block, network).expect("subsidies should pass for this block"); } } @@ -308,10 +309,10 @@ fn coinbase_validation_failure() -> Result<(), Report> { zebra_test::init(); let network = Network::Mainnet; - // Get a block in the mainnet that is inside the founders reward period, + // Get a block in the mainnet that is inside the funding stream period, // and delete the coinbase transaction let block = - Arc::::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_415000_BYTES[..]) + Arc::::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_1046400_BYTES[..]) .expect("block should deserialize"); let mut block = Arc::try_unwrap(block).expect("block should unwrap"); @@ -328,9 +329,9 @@ fn coinbase_validation_failure() -> Result<(), Report> { let expected = BlockError::Transaction(TransactionError::Subsidy(SubsidyError::NoCoinbase)); assert_eq!(expected, result); - // Get another founders reward block, and delete the coinbase transaction + // Get another funding stream block, and delete the coinbase transaction let block = - Arc::::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_434873_BYTES[..]) + Arc::::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_1046401_BYTES[..]) .expect("block should deserialize"); let mut block = Arc::try_unwrap(block).expect("block should unwrap"); @@ -347,9 +348,9 @@ fn coinbase_validation_failure() -> Result<(), Report> { let expected = BlockError::Transaction(TransactionError::Subsidy(SubsidyError::NoCoinbase)); assert_eq!(expected, result); - // Get another founders reward block, and duplicate the coinbase transaction + // Get another funding stream, and duplicate the coinbase transaction let block = - Arc::::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_434873_BYTES[..]) + Arc::::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_1180900_BYTES[..]) .expect("block should deserialize"); let mut block = Arc::try_unwrap(block).expect("block should unwrap"); @@ -374,51 +375,6 @@ fn coinbase_validation_failure() -> Result<(), Report> { Ok(()) } -#[test] -fn founders_reward_validation_failure() -> Result<(), Report> { - zebra_test::init(); - let network = Network::Mainnet; - - // Get a block in the mainnet that is inside the founders reward period. - let block = - Arc::::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_415000_BYTES[..]) - .expect("block should deserialize"); - - // Build the new transaction with modified coinbase outputs - let tx = block - .transactions - .get(0) - .map(|transaction| { - let mut output = transaction.outputs()[0].clone(); - output.value = Amount::try_from(i32::MAX).unwrap(); - Transaction::V3 { - inputs: transaction.inputs().to_vec(), - outputs: vec![output], - lock_time: transaction.lock_time().unwrap_or_else(LockTime::unlocked), - expiry_height: Height(0), - joinsplit_data: None, - } - }) - .unwrap(); - - // Build new block - let transactions: Vec> = vec![Arc::new(tx)]; - let block = Block { - header: block.header, - transactions, - }; - - // Validate it - let result = check::subsidy_is_valid(&block, network); - let expected = Err(BlockError::Transaction(TransactionError::Subsidy( - SubsidyError::FoundersRewardNotFound, - ))); - - assert_eq!(expected, result); - - Ok(()) -} - #[test] fn funding_stream_validation() -> Result<(), Report> { zebra_test::init(); @@ -435,8 +391,12 @@ fn funding_stream_validation_for_network(network: Network) -> Result<(), Report> Network::Testnet => zebra_test::vectors::TESTNET_BLOCKS.iter(), }; + let canopy_activation_height = NetworkUpgrade::Canopy + .activation_height(network) + .expect("Canopy activation height is known"); + for (&height, block) in block_iter { - if Height(height) > SLOW_START_SHIFT { + if Height(height) >= canopy_activation_height { let block = Block::zcash_deserialize(&block[..]).expect("block should deserialize"); // Validate diff --git a/zebra-consensus/src/error.rs b/zebra-consensus/src/error.rs index a37d5f30d..5b041fb43 100644 --- a/zebra-consensus/src/error.rs +++ b/zebra-consensus/src/error.rs @@ -23,9 +23,6 @@ pub enum SubsidyError { #[error("no coinbase transaction in block")] NoCoinbase, - #[error("founders reward output not found")] - FoundersRewardNotFound, - #[error("funding stream expected output not found")] FundingStreamNotFound, diff --git a/zebra-consensus/src/parameters/subsidy.rs b/zebra-consensus/src/parameters/subsidy.rs index f7604ca82..863706b0b 100644 --- a/zebra-consensus/src/parameters/subsidy.rs +++ b/zebra-consensus/src/parameters/subsidy.rs @@ -1,4 +1,4 @@ -//! Constants for Block Subsidy, Funding Streams, and Founders' Reward +//! Constants for Block Subsidy and Funding Streams use lazy_static::lazy_static; use std::collections::HashMap; @@ -38,13 +38,6 @@ pub const PRE_BLOSSOM_HALVING_INTERVAL: Height = Height(840_000); pub const POST_BLOSSOM_HALVING_INTERVAL: Height = Height((PRE_BLOSSOM_HALVING_INTERVAL.0 as u64 * BLOSSOM_POW_TARGET_SPACING_RATIO) as u32); -/// The divisor used to calculate the FoundersFraction. -/// -/// Derivation: FOUNDERS_FRACTION_DIVISOR = 1/FoundersFraction -/// -/// Usage: founders_reward = block_subsidy / FOUNDERS_FRACTION_DIVISOR -pub const FOUNDERS_FRACTION_DIVISOR: u64 = 5; - /// The first halving height in the testnet is at block height `1_116_000` /// as specified in [protocol specification §7.10.1][7.10.1] ///