remove founders reward code (#3430)
* remove founders reward code * panic if a block before Canopy is validated for subsidy Co-Authored-By: Daira Hopwood <daira@jacaranda.org> Co-authored-by: Daira Hopwood <daira@jacaranda.org>
This commit is contained in:
parent
e5b5ea5889
commit
6821ba9791
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<Amount<NonNegative>, 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(())
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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<Amount<NonNegat
|
|||
}
|
||||
}
|
||||
|
||||
/// `MinerSubsidy(height)` as described in [protocol specification §7.8][7.8]
|
||||
///
|
||||
/// [7.8]: https://zips.z.cash/protocol/protocol.pdf#subsidies
|
||||
///
|
||||
/// `non_miner_reward` is the founders reward or funding stream value.
|
||||
/// If all the rewards for a block go to the miner, use `None`.
|
||||
#[allow(dead_code)]
|
||||
pub fn miner_subsidy(
|
||||
height: Height,
|
||||
network: Network,
|
||||
non_miner_reward: Option<Amount<NonNegative>>,
|
||||
) -> Result<Amount<NonNegative>, 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<NonNegative>,
|
||||
) -> Vec<transparent::Output> {
|
||||
// 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<Amount<NonNegative>> {
|
||||
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::<Result<Amount<NonNegative>, 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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::<Block>()
|
||||
.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::<Block>::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_415000_BYTES[..])
|
||||
Arc::<Block>::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::<Block>::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_434873_BYTES[..])
|
||||
Arc::<Block>::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::<Block>::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_434873_BYTES[..])
|
||||
Arc::<Block>::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::<Block>::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<Arc<zebra_chain::transaction::Transaction>> = 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
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -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]
|
||||
///
|
||||
|
|
Loading…
Reference in New Issue