use ckb_traits::{BlockEpoch, EpochProvider};
use ckb_types::{
core::{
capacity_bytes, BlockBuilder, BlockExt, BlockNumber, Capacity, EpochExt, HeaderBuilder,
HeaderView, TransactionBuilder,
},
packed::{Byte32, Bytes},
prelude::*,
utilities::DIFF_TWO,
};
use crate::consensus::{
build_genesis_epoch_ext, ConsensusBuilder, DEFAULT_EPOCH_DURATION_TARGET,
DEFAULT_ORPHAN_RATE_TARGET, GENESIS_EPOCH_LENGTH,
};
#[test]
fn test_init_epoch_reward() {
let cellbase = TransactionBuilder::default()
.witness(Bytes::default())
.build();
let epoch_ext = build_genesis_epoch_ext(
capacity_bytes!(100),
DIFF_TWO,
GENESIS_EPOCH_LENGTH,
DEFAULT_EPOCH_DURATION_TARGET,
DEFAULT_ORPHAN_RATE_TARGET,
);
let genesis = BlockBuilder::default().transaction(cellbase).build();
let consensus = ConsensusBuilder::new(genesis, epoch_ext)
.initial_primary_epoch_reward(capacity_bytes!(100))
.build();
assert_eq!(capacity_bytes!(100), consensus.initial_primary_epoch_reward);
}
#[test]
fn test_halving_epoch_reward() {
let cellbase = TransactionBuilder::default()
.witness(Bytes::default())
.build();
let epoch_ext = build_genesis_epoch_ext(
capacity_bytes!(100),
DIFF_TWO,
GENESIS_EPOCH_LENGTH,
DEFAULT_EPOCH_DURATION_TARGET,
DEFAULT_ORPHAN_RATE_TARGET,
);
let genesis = BlockBuilder::default().transaction(cellbase).build();
let consensus = ConsensusBuilder::new(genesis, epoch_ext)
.initial_primary_epoch_reward(capacity_bytes!(100))
.build();
let genesis_epoch = consensus.genesis_epoch_ext();
let header = |epoch: &EpochExt, number: u64| {
HeaderBuilder::default()
.number(number.pack())
.epoch(epoch.number_with_fraction(number).pack())
.build()
};
struct DummyEpochProvider(EpochExt);
impl EpochProvider for DummyEpochProvider {
fn get_epoch_ext(&self, _block_header: &HeaderView) -> Option<EpochExt> {
Some(self.0.clone())
}
fn get_block_hash(&self, _number: BlockNumber) -> Option<Byte32> {
None
}
fn get_block_ext(&self, _block_hash: &Byte32) -> Option<BlockExt> {
None
}
fn get_block_header(&self, _hash: &Byte32) -> Option<HeaderView> {
None
}
fn get_block_epoch(&self, block_header: &HeaderView) -> Option<BlockEpoch> {
let block_epoch =
if block_header.number() == self.0.start_number() + self.0.length() - 1 {
BlockEpoch::TailBlock {
epoch: self.0.clone(),
epoch_uncles_count: 0,
epoch_duration_in_milliseconds: DEFAULT_EPOCH_DURATION_TARGET * 1000,
}
} else {
BlockEpoch::NonTailBlock {
epoch: self.0.clone(),
}
};
Some(block_epoch)
}
}
let initial_primary_epoch_reward = genesis_epoch.primary_reward();
{
let epoch = consensus
.next_epoch_ext(
&header(genesis_epoch, genesis_epoch.length() - 1),
&DummyEpochProvider(genesis_epoch.clone()),
)
.expect("test: get next epoch")
.epoch();
assert_eq!(initial_primary_epoch_reward, epoch.primary_reward());
}
let first_halving_epoch_number = consensus.primary_epoch_reward_halving_interval();
let epoch = genesis_epoch
.clone()
.into_builder()
.number(first_halving_epoch_number - 2)
.build();
let epoch = consensus
.next_epoch_ext(
&header(&epoch, epoch.start_number() + epoch.length() - 1),
&DummyEpochProvider(epoch),
)
.expect("test: get next epoch")
.epoch();
assert_eq!(initial_primary_epoch_reward, epoch.primary_reward());
let epoch = consensus
.next_epoch_ext(
&header(&epoch, epoch.start_number() + epoch.length() - 1),
&DummyEpochProvider(epoch),
)
.expect("test: get next epoch")
.epoch();
assert_eq!(
initial_primary_epoch_reward.as_u64() / 2,
epoch.primary_reward().as_u64()
);
let epoch = consensus
.next_epoch_ext(
&header(&epoch, epoch.start_number() + epoch.length() - 1),
&DummyEpochProvider(epoch),
)
.expect("test: get next epoch")
.epoch();
assert_eq!(
initial_primary_epoch_reward.as_u64() / 2,
epoch.primary_reward().as_u64()
);
let epoch = genesis_epoch
.clone()
.into_builder()
.number(first_halving_epoch_number * 4 - 2)
.base_block_reward(Capacity::shannons(
initial_primary_epoch_reward.as_u64() / 8 / genesis_epoch.length(),
))
.remainder_reward(Capacity::shannons(
initial_primary_epoch_reward.as_u64() / 8 % genesis_epoch.length(),
))
.build();
let epoch = consensus
.next_epoch_ext(
&header(&epoch, epoch.start_number() + epoch.length() - 1),
&DummyEpochProvider(epoch),
)
.expect("test: get next epoch")
.epoch();
assert_eq!(
initial_primary_epoch_reward.as_u64() / 8,
epoch.primary_reward().as_u64()
);
let epoch = consensus
.next_epoch_ext(
&header(&epoch, epoch.start_number() + epoch.length() - 1),
&DummyEpochProvider(epoch),
)
.expect("test: get next epoch")
.epoch();
assert_eq!(
initial_primary_epoch_reward.as_u64() / 16,
epoch.primary_reward().as_u64()
);
}