ckb_script/verify_env.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
//! Transaction verification environment.
use ckb_chain_spec::consensus::ProposalWindow;
use ckb_types::{
core::{BlockNumber, EpochNumber, EpochNumberWithFraction, HeaderView},
packed::Byte32,
};
/// The phase that transactions are in.
#[derive(Debug, Clone, Copy)]
enum TxVerifyPhase {
/// The transaction has just been submitted.
///
/// So the transaction will be:
/// - proposed after (or in) the `tip_number + 1` block.
/// - committed after (or in) `tip_number + 1 + proposal_window.closest()` block.
Submitted,
/// The transaction has already been proposed before several blocks.
///
/// Assume that the inner block number is `N`.
/// So the transaction is proposed in the `tip_number - N` block.
/// Then it will be committed after (or in) the `tip_number - N + proposal_window.closest()` block.
Proposed(BlockNumber),
/// The transaction is commit.
///
/// So the transaction will be committed in current block.
Committed,
}
/// The environment that transactions are in.
#[derive(Debug, Clone)]
pub struct TxVerifyEnv {
// Please keep these fields to be private.
// So we can update this struct easier when we want to add more data.
phase: TxVerifyPhase,
// Current Tip Environment
number: BlockNumber,
epoch: EpochNumberWithFraction,
hash: Byte32,
parent_hash: Byte32,
}
impl TxVerifyEnv {
/// The transaction has just been submitted.
///
/// The input is current tip header.
pub fn new_submit(header: &HeaderView) -> Self {
Self {
phase: TxVerifyPhase::Submitted,
number: header.number(),
epoch: header.epoch(),
hash: header.hash(),
parent_hash: header.parent_hash(),
}
}
/// The transaction has already been proposed before several blocks.
///
/// The input is current tip header and how many blocks have been passed since the transaction was proposed.
pub fn new_proposed(header: &HeaderView, n_blocks: BlockNumber) -> Self {
Self {
phase: TxVerifyPhase::Proposed(n_blocks),
number: header.number(),
epoch: header.epoch(),
hash: header.hash(),
parent_hash: header.parent_hash(),
}
}
/// The transaction will committed in current block.
///
/// The input is current tip header.
pub fn new_commit(header: &HeaderView) -> Self {
Self {
phase: TxVerifyPhase::Committed,
number: header.number(),
epoch: header.epoch(),
hash: header.hash(),
parent_hash: header.parent_hash(),
}
}
/// The block number of the earliest block which the transaction will committed in.
pub fn block_number(&self, proposal_window: ProposalWindow) -> BlockNumber {
match self.phase {
TxVerifyPhase::Submitted => self.number + 1 + proposal_window.closest(),
TxVerifyPhase::Proposed(already_proposed) => {
self.number.saturating_sub(already_proposed) + proposal_window.closest()
}
TxVerifyPhase::Committed => self.number,
}
}
/// The epoch number of the earliest epoch which the transaction will committed in.
pub fn epoch_number(&self, proposal_window: ProposalWindow) -> EpochNumber {
let n_blocks = match self.phase {
TxVerifyPhase::Submitted => 1 + proposal_window.closest(),
TxVerifyPhase::Proposed(already_proposed) => {
proposal_window.closest().saturating_sub(already_proposed)
}
TxVerifyPhase::Committed => 0,
};
self.epoch.minimum_epoch_number_after_n_blocks(n_blocks)
}
/// The parent block hash of the earliest block which the transaction will committed in.
pub fn parent_hash(&self) -> Byte32 {
match self.phase {
TxVerifyPhase::Submitted => &self.hash,
TxVerifyPhase::Proposed(_) => &self.hash,
TxVerifyPhase::Committed => &self.parent_hash,
}
.to_owned()
}
/// The earliest epoch which the transaction will committed in.
pub fn epoch(&self) -> EpochNumberWithFraction {
self.epoch
}
/// The epoch number of the earliest epoch which the transaction will committed in without
/// consider about the proposal window.
pub fn epoch_number_without_proposal_window(&self) -> EpochNumber {
let n_blocks = match self.phase {
TxVerifyPhase::Submitted | TxVerifyPhase::Proposed(_) => 1,
TxVerifyPhase::Committed => 0,
};
self.epoch.minimum_epoch_number_after_n_blocks(n_blocks)
}
}