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)
    }
}