chia_protocol/
block_record.rs

1use crate::{Bytes32, ClassgroupElement, Coin, SubEpochSummary};
2use crate::{calculate_ip_iters, calculate_sp_iters};
3use chia_streamable_macro::streamable;
4use chia_traits::chia_error::Result;
5#[cfg(feature = "py-bindings")]
6use pyo3::exceptions::PyValueError;
7
8#[cfg(feature = "py-bindings")]
9use pyo3::prelude::*;
10
11// This class is not included or hashed into the blockchain, but it is kept in memory as a more
12// efficient way to maintain data about the blockchain. This allows us to validate future blocks,
13// difficulty adjustments, etc, without saving the whole header block in memory.
14#[streamable]
15pub struct BlockRecord {
16    header_hash: Bytes32,
17    // Header hash of the previous block
18    prev_hash: Bytes32,
19    height: u32,
20    // Total cumulative difficulty of all ancestor blocks since genesis
21    weight: u128,
22    // Total number of VDF iterations since genesis, including this block
23    total_iters: u128,
24    signage_point_index: u8,
25    // This is the intermediary VDF output at ip_iters in challenge chain
26    challenge_vdf_output: ClassgroupElement,
27    // This is the intermediary VDF output at ip_iters in infused cc, iff deficit <= 3
28    infused_challenge_vdf_output: Option<ClassgroupElement>,
29    // The reward chain infusion output, input to next VDF
30    reward_infusion_new_challenge: Bytes32,
31    // Hash of challenge chain data, used to validate end of slots in the future
32    challenge_block_info_hash: Bytes32,
33    // Current network sub_slot_iters parameter
34    sub_slot_iters: u64,
35    // Need to keep track of these because Coins are created in a future block
36    pool_puzzle_hash: Bytes32,
37    farmer_puzzle_hash: Bytes32,
38    // The number of iters required for this proof of space
39    required_iters: u64,
40    // A deficit of 16 is an overflow block after an infusion. Deficit of 15 is a challenge block
41    deficit: u8,
42    overflow: bool,
43    prev_transaction_block_height: u32,
44
45    // Transaction block (present iff is_transaction_block)
46    timestamp: Option<u64>,
47    // Header hash of the previous transaction block
48    prev_transaction_block_hash: Option<Bytes32>,
49    fees: Option<u64>,
50    reward_claims_incorporated: Option<Vec<Coin>>,
51
52    // Slot (present iff this is the first SB in sub slot)
53    finished_challenge_slot_hashes: Option<Vec<Bytes32>>,
54    finished_infused_challenge_slot_hashes: Option<Vec<Bytes32>>,
55    finished_reward_slot_hashes: Option<Vec<Bytes32>>,
56
57    // Sub-epoch (present iff this is the first SB after sub-epoch)
58    sub_epoch_summary_included: Option<SubEpochSummary>,
59}
60
61impl BlockRecord {
62    pub fn is_transaction_block(&self) -> bool {
63        self.timestamp.is_some()
64    }
65
66    pub fn first_in_sub_slot(&self) -> bool {
67        self.finished_challenge_slot_hashes.is_some()
68    }
69
70    pub fn is_challenge_block(&self, min_blocks_per_challenge_block: u8) -> bool {
71        self.deficit == min_blocks_per_challenge_block - 1
72    }
73
74    pub fn sp_iters_impl(&self, num_sps_sub_slot: u8) -> Result<u64> {
75        calculate_sp_iters(
76            num_sps_sub_slot,
77            self.sub_slot_iters,
78            self.signage_point_index,
79        )
80    }
81
82    pub fn ip_iters_impl(&self, num_sps_sub_slot: u8, num_sp_intervals_extra: u8) -> Result<u64> {
83        calculate_ip_iters(
84            num_sps_sub_slot,
85            num_sp_intervals_extra,
86            self.sub_slot_iters,
87            self.signage_point_index,
88            self.required_iters,
89        )
90    }
91}
92
93#[cfg(feature = "py-bindings")]
94#[pymethods]
95impl BlockRecord {
96    #[getter]
97    #[pyo3(name = "is_transaction_block")]
98    fn py_is_transaction_block(&self) -> bool {
99        self.is_transaction_block()
100    }
101
102    #[getter]
103    #[pyo3(name = "first_in_sub_slot")]
104    fn py_first_in_sub_slot(&self) -> bool {
105        self.first_in_sub_slot()
106    }
107
108    #[pyo3(name = "is_challenge_block")]
109    fn py_is_challenge_block(&self, constants: &Bound<'_, PyAny>) -> PyResult<bool> {
110        Ok(self.is_challenge_block(
111            constants
112                .getattr("MIN_BLOCKS_PER_CHALLENGE_BLOCK")?
113                .extract::<u8>()?,
114        ))
115    }
116
117    #[pyo3(name = "ip_sub_slot_total_iters")]
118    fn ip_sub_slot_total_iters_impl(&self, constants: &Bound<'_, PyAny>) -> PyResult<u128> {
119        self.total_iters
120            .checked_sub(self.py_ip_iters_impl(constants)? as u128)
121            .ok_or(PyValueError::new_err("uint128 overflow"))
122    }
123
124    #[pyo3(name = "sp_iters")]
125    fn py_sp_iters_impl(&self, constants: &Bound<'_, PyAny>) -> PyResult<u64> {
126        let num_sps_sub_slot = constants.getattr("NUM_SPS_SUB_SLOT")?.extract::<u8>()?;
127        self.sp_iters_impl(num_sps_sub_slot).map_err(Into::into)
128    }
129
130    #[pyo3(name = "ip_iters")]
131    fn py_ip_iters_impl(&self, constants: &Bound<'_, PyAny>) -> PyResult<u64> {
132        let num_sps_sub_slot = constants.getattr("NUM_SPS_SUB_SLOT")?.extract::<u8>()?;
133        let num_sp_intervals_extra = constants
134            .getattr("NUM_SP_INTERVALS_EXTRA")?
135            .extract::<u8>()?;
136        self.ip_iters_impl(num_sps_sub_slot, num_sp_intervals_extra)
137            .map_err(Into::into)
138    }
139
140    #[pyo3(name = "sp_sub_slot_total_iters")]
141    fn sp_sub_slot_total_iters_impl(&self, constants: &Bound<'_, PyAny>) -> PyResult<u128> {
142        let ret = self
143            .total_iters
144            .checked_sub(self.py_ip_iters_impl(constants)? as u128)
145            .ok_or(PyValueError::new_err("uint128 overflow"))?;
146        if self.overflow {
147            ret.checked_sub(self.sub_slot_iters as u128)
148                .ok_or(PyValueError::new_err("uint128 overflow"))
149        } else {
150            Ok(ret)
151        }
152    }
153
154    #[pyo3(name = "sp_total_iters")]
155    fn sp_total_iters_impl(&self, constants: &Bound<'_, PyAny>) -> PyResult<u128> {
156        self.sp_sub_slot_total_iters_impl(constants)?
157            .checked_add(self.py_sp_iters_impl(constants)? as u128)
158            .ok_or(PyValueError::new_err("uint128 overflow"))
159    }
160}