1use chik_streamable_macro::streamable;
2
3use crate::{Bytes32, ClassgroupElement, Coin, SubEpochSummary};
4
5#[cfg(feature = "py-bindings")]
6use pyo3::prelude::*;
7
8#[streamable]
12pub struct BlockRecord {
13 header_hash: Bytes32,
14 prev_hash: Bytes32,
16 height: u32,
17 weight: u128,
19 total_iters: u128,
21 signage_point_index: u8,
22 challenge_vdf_output: ClassgroupElement,
24 infused_challenge_vdf_output: Option<ClassgroupElement>,
26 reward_infusion_new_challenge: Bytes32,
28 challenge_block_info_hash: Bytes32,
30 sub_slot_iters: u64,
32 pool_puzzle_hash: Bytes32,
34 farmer_puzzle_hash: Bytes32,
35 required_iters: u64,
37 deficit: u8,
39 overflow: bool,
40 prev_transaction_block_height: u32,
41
42 timestamp: Option<u64>,
44 prev_transaction_block_hash: Option<Bytes32>,
46 fees: Option<u64>,
47 reward_claims_incorporated: Option<Vec<Coin>>,
48
49 finished_challenge_slot_hashes: Option<Vec<Bytes32>>,
51 finished_infused_challenge_slot_hashes: Option<Vec<Bytes32>>,
52 finished_reward_slot_hashes: Option<Vec<Bytes32>>,
53
54 sub_epoch_summary_included: Option<SubEpochSummary>,
56}
57
58impl BlockRecord {
59 pub fn is_transaction_block(&self) -> bool {
60 self.timestamp.is_some()
61 }
62
63 pub fn first_in_sub_slot(&self) -> bool {
64 self.finished_challenge_slot_hashes.is_some()
65 }
66
67 pub fn is_challenge_block(&self, min_blocks_per_challenge_block: u8) -> bool {
68 self.deficit == min_blocks_per_challenge_block - 1
69 }
70}
71
72#[cfg(feature = "py-bindings")]
73use pyo3::types::PyDict;
74
75#[cfg(feature = "py-bindings")]
76use pyo3::exceptions::PyValueError;
77
78#[cfg(feature = "py-bindings")]
79use chik_traits::ChikToPython;
80
81#[cfg(feature = "py-bindings")]
82#[pymethods]
83impl BlockRecord {
84 #[getter]
85 #[pyo3(name = "is_transaction_block")]
86 fn py_is_transaction_block(&self) -> bool {
87 self.is_transaction_block()
88 }
89
90 #[getter]
91 #[pyo3(name = "first_in_sub_slot")]
92 fn py_first_in_sub_slot(&self) -> bool {
93 self.first_in_sub_slot()
94 }
95
96 #[pyo3(name = "is_challenge_block")]
97 fn py_is_challenge_block(&self, constants: &Bound<'_, PyAny>) -> PyResult<bool> {
98 Ok(self.is_challenge_block(
99 constants
100 .getattr("MIN_BLOCKS_PER_CHALLENGE_BLOCK")?
101 .extract::<u8>()?,
102 ))
103 }
104
105 fn sp_sub_slot_total_iters_impl(
108 &self,
109 py: Python<'_>,
110 constants: &Bound<'_, PyAny>,
111 ) -> PyResult<u128> {
112 let ret = self
113 .total_iters
114 .checked_sub(self.ip_iters_impl(py, constants)? as u128)
115 .ok_or(PyValueError::new_err("uint128 overflow"))?;
116 if self.overflow {
117 ret.checked_sub(self.sub_slot_iters as u128)
118 .ok_or(PyValueError::new_err("uint128 overflow"))
119 } else {
120 Ok(ret)
121 }
122 }
123
124 fn ip_sub_slot_total_iters_impl(
125 &self,
126 py: Python<'_>,
127 constants: &Bound<'_, PyAny>,
128 ) -> PyResult<u128> {
129 self.total_iters
130 .checked_sub(self.ip_iters_impl(py, constants)? as u128)
131 .ok_or(PyValueError::new_err("uint128 overflow"))
132 }
133
134 fn sp_iters_impl(&self, py: Python<'_>, constants: &Bound<'_, PyAny>) -> PyResult<u64> {
135 let ctx = PyDict::new(py);
136 ctx.set_item("sub_slot_iters", self.sub_slot_iters)?;
137 ctx.set_item("signage_point_index", self.signage_point_index)?;
138 ctx.set_item("constants", constants)?;
139 py.run(
140 c"from chik.consensus.pot_iterations import calculate_ip_iters, calculate_sp_iters\n\
141 ret = calculate_sp_iters(constants, sub_slot_iters, signage_point_index)\n",
142 None,
143 Some(&ctx),
144 )?;
145 ctx.get_item("ret").unwrap().unwrap().extract::<u64>()
146 }
147
148 fn ip_iters_impl(&self, py: Python<'_>, constants: &Bound<'_, PyAny>) -> PyResult<u64> {
149 let ctx = PyDict::new(py);
150 ctx.set_item("sub_slot_iters", self.sub_slot_iters)?;
151 ctx.set_item("signage_point_index", self.signage_point_index)?;
152 ctx.set_item("required_iters", self.required_iters)?;
153 ctx.set_item("constants", constants)?;
154 py.run(
155 c"from chik.consensus.pot_iterations import calculate_ip_iters, calculate_sp_iters\n\
156 ret = calculate_ip_iters(constants, sub_slot_iters, signage_point_index, required_iters)\n",
157 None,
158 Some(&ctx),
159 )?;
160 ctx.get_item("ret").unwrap().unwrap().extract::<u64>()
161 }
162
163 fn sp_total_iters_impl(&self, py: Python<'_>, constants: &Bound<'_, PyAny>) -> PyResult<u128> {
164 self.sp_sub_slot_total_iters_impl(py, constants)?
165 .checked_add(self.sp_iters_impl(py, constants)? as u128)
166 .ok_or(PyValueError::new_err("uint128 overflow"))
167 }
168
169 fn sp_sub_slot_total_iters<'a>(
170 &self,
171 py: Python<'a>,
172 constants: &Bound<'_, PyAny>,
173 ) -> PyResult<Bound<'a, PyAny>> {
174 ChikToPython::to_python(&self.sp_sub_slot_total_iters_impl(py, constants)?, py)
175 }
176
177 fn ip_sub_slot_total_iters<'a>(
178 &self,
179 py: Python<'a>,
180 constants: &Bound<'_, PyAny>,
181 ) -> PyResult<Bound<'a, PyAny>> {
182 ChikToPython::to_python(&self.ip_sub_slot_total_iters_impl(py, constants)?, py)
183 }
184
185 fn sp_iters<'a>(
186 &self,
187 py: Python<'a>,
188 constants: &Bound<'_, PyAny>,
189 ) -> PyResult<Bound<'a, PyAny>> {
190 ChikToPython::to_python(&self.sp_iters_impl(py, constants)?, py)
191 }
192
193 fn ip_iters<'a>(
194 &self,
195 py: Python<'a>,
196 constants: &Bound<'_, PyAny>,
197 ) -> PyResult<Bound<'a, PyAny>> {
198 ChikToPython::to_python(&self.ip_iters_impl(py, constants)?, py)
199 }
200
201 fn sp_total_iters<'a>(
202 &self,
203 py: Python<'a>,
204 constants: &Bound<'_, PyAny>,
205 ) -> PyResult<Bound<'a, PyAny>> {
206 ChikToPython::to_python(&self.sp_total_iters_impl(py, constants)?, py)
207 }
208}