use std::sync::Arc;
use zebra_chain::{
amount::{Amount, NegativeAllowed},
block::{self, Block},
transaction::Transaction,
transparent,
value_balance::ValueBalance,
};
use crate::{
request::ContextuallyVerifiedBlock, service::chain_tip::ChainTipBlock, CheckpointVerifiedBlock,
SemanticallyVerifiedBlock,
};
pub trait Prepare {
fn prepare(self) -> SemanticallyVerifiedBlock;
}
impl Prepare for Arc<Block> {
fn prepare(self) -> SemanticallyVerifiedBlock {
let block = self;
let hash = block.hash();
let height = block.coinbase_height().unwrap();
let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect();
let new_outputs =
transparent::new_ordered_outputs_with_height(&block, height, &transaction_hashes);
SemanticallyVerifiedBlock {
block,
hash,
height,
new_outputs,
transaction_hashes,
}
}
}
impl<T> From<T> for ChainTipBlock
where
T: Prepare,
{
fn from(block: T) -> Self {
block.prepare().into()
}
}
impl From<SemanticallyVerifiedBlock> for ChainTipBlock {
fn from(prepared: SemanticallyVerifiedBlock) -> Self {
let SemanticallyVerifiedBlock {
block,
hash,
height,
new_outputs: _,
transaction_hashes,
} = prepared;
Self {
hash,
height,
time: block.header.time,
transactions: block.transactions.clone(),
transaction_hashes,
previous_block_hash: block.header.previous_block_hash,
}
}
}
impl SemanticallyVerifiedBlock {
#[cfg(test)]
pub fn test_with_zero_spent_utxos(&self) -> ContextuallyVerifiedBlock {
ContextuallyVerifiedBlock::test_with_zero_spent_utxos(self)
}
#[cfg(test)]
pub fn test_with_chain_pool_change(
&self,
fake_chain_value_pool_change: ValueBalance<NegativeAllowed>,
) -> ContextuallyVerifiedBlock {
ContextuallyVerifiedBlock::test_with_chain_pool_change(self, fake_chain_value_pool_change)
}
#[cfg(test)]
pub fn test_with_zero_chain_pool_change(&self) -> ContextuallyVerifiedBlock {
ContextuallyVerifiedBlock::test_with_zero_chain_pool_change(self)
}
}
impl ContextuallyVerifiedBlock {
pub fn test_with_zero_spent_utxos(block: impl Into<SemanticallyVerifiedBlock>) -> Self {
let block = block.into();
let zero_output = transparent::Output {
value: Amount::zero(),
lock_script: transparent::Script::new(&[]),
};
let zero_utxo = transparent::OrderedUtxo::new(zero_output, block::Height(1), 1);
let zero_spent_utxos = block
.block
.transactions
.iter()
.map(AsRef::as_ref)
.flat_map(Transaction::inputs)
.flat_map(transparent::Input::outpoint)
.map(|outpoint| (outpoint, zero_utxo.clone()))
.collect();
ContextuallyVerifiedBlock::with_block_and_spent_utxos(block, zero_spent_utxos)
.expect("all UTXOs are provided with zero values")
}
pub fn test_with_chain_pool_change(
block: impl Into<SemanticallyVerifiedBlock>,
fake_chain_value_pool_change: ValueBalance<NegativeAllowed>,
) -> Self {
let SemanticallyVerifiedBlock {
block,
hash,
height,
new_outputs,
transaction_hashes,
} = block.into();
Self {
block,
hash,
height,
new_outputs: new_outputs.clone(),
spent_outputs: new_outputs,
transaction_hashes,
chain_value_pool_change: fake_chain_value_pool_change,
}
}
pub fn test_with_zero_chain_pool_change(block: impl Into<SemanticallyVerifiedBlock>) -> Self {
Self::test_with_chain_pool_change(block, ValueBalance::zero())
}
}
impl CheckpointVerifiedBlock {
#[cfg(any(test, feature = "proptest-impl"))]
pub fn with_hash_and_height(
block: Arc<Block>,
hash: block::Hash,
height: block::Height,
) -> Self {
let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect();
let new_outputs =
transparent::new_ordered_outputs_with_height(&block, height, &transaction_hashes);
Self(SemanticallyVerifiedBlock {
block,
hash,
height,
new_outputs,
transaction_hashes,
})
}
}