1use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
2use primitive_types::U256;
3
4use crate::Version;
5
6pub const MAX_NODE_DATA_SIZE: usize = 32 + 4 + 4 + 4 + 4 + 32 + 32 + 32 + 9 + 9 + 9 + 32 + 32 + 9; #[repr(C)]
25#[derive(Debug, Clone, Default)]
26#[cfg_attr(test, derive(PartialEq, Eq))]
27pub struct NodeData {
28 pub consensus_branch_id: u32,
30 pub subtree_commitment: [u8; 32],
32 pub start_time: u32,
34 pub end_time: u32,
36 pub start_target: u32,
38 pub end_target: u32,
40 pub start_sapling_root: [u8; 32],
42 pub end_sapling_root: [u8; 32],
44 pub subtree_total_work: U256,
46 pub start_height: u64,
48 pub end_height: u64,
50 pub sapling_tx: u64,
52}
53
54impl NodeData {
55 pub fn combine(left: &NodeData, right: &NodeData) -> NodeData {
57 crate::V1::combine(left, right)
58 }
59
60 pub(crate) fn combine_inner(
61 subtree_commitment: [u8; 32],
62 left: &NodeData,
63 right: &NodeData,
64 ) -> NodeData {
65 NodeData {
66 consensus_branch_id: left.consensus_branch_id,
67 subtree_commitment,
68 start_time: left.start_time,
69 end_time: right.end_time,
70 start_target: left.start_target,
71 end_target: right.end_target,
72 start_sapling_root: left.start_sapling_root,
73 end_sapling_root: right.end_sapling_root,
74 subtree_total_work: left.subtree_total_work + right.subtree_total_work,
75 start_height: left.start_height,
76 end_height: right.end_height,
77 sapling_tx: left.sapling_tx + right.sapling_tx,
78 }
79 }
80
81 fn write_compact<W: std::io::Write>(w: &mut W, compact: u64) -> std::io::Result<()> {
82 match compact {
83 0..=0xfc => w.write_all(&[compact as u8])?,
84 0xfd..=0xffff => {
85 w.write_all(&[0xfd])?;
86 w.write_u16::<LittleEndian>(compact as u16)?;
87 }
88 0x10000..=0xffff_ffff => {
89 w.write_all(&[0xfe])?;
90 w.write_u32::<LittleEndian>(compact as u32)?;
91 }
92 _ => {
93 w.write_all(&[0xff])?;
94 w.write_u64::<LittleEndian>(compact)?;
95 }
96 }
97 Ok(())
98 }
99
100 fn read_compact<R: std::io::Read>(reader: &mut R) -> std::io::Result<u64> {
101 let result = match reader.read_u8()? {
102 i @ 0..=0xfc => i.into(),
103 0xfd => reader.read_u16::<LittleEndian>()?.into(),
104 0xfe => reader.read_u32::<LittleEndian>()?.into(),
105 _ => reader.read_u64::<LittleEndian>()?,
106 };
107
108 Ok(result)
109 }
110
111 pub fn write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
113 w.write_all(&self.subtree_commitment)?;
114 w.write_u32::<LittleEndian>(self.start_time)?;
115 w.write_u32::<LittleEndian>(self.end_time)?;
116 w.write_u32::<LittleEndian>(self.start_target)?;
117 w.write_u32::<LittleEndian>(self.end_target)?;
118 w.write_all(&self.start_sapling_root)?;
119 w.write_all(&self.end_sapling_root)?;
120
121 let mut work_buf = [0u8; 32];
122 self.subtree_total_work.to_little_endian(&mut work_buf[..]);
123 w.write_all(&work_buf)?;
124
125 Self::write_compact(w, self.start_height)?;
126 Self::write_compact(w, self.end_height)?;
127 Self::write_compact(w, self.sapling_tx)?;
128 Ok(())
129 }
130
131 pub fn read<R: std::io::Read>(consensus_branch_id: u32, r: &mut R) -> std::io::Result<Self> {
133 let mut data = NodeData {
134 consensus_branch_id,
135 ..Default::default()
136 };
137 r.read_exact(&mut data.subtree_commitment)?;
138 data.start_time = r.read_u32::<LittleEndian>()?;
139 data.end_time = r.read_u32::<LittleEndian>()?;
140 data.start_target = r.read_u32::<LittleEndian>()?;
141 data.end_target = r.read_u32::<LittleEndian>()?;
142 r.read_exact(&mut data.start_sapling_root)?;
143 r.read_exact(&mut data.end_sapling_root)?;
144
145 let mut work_buf = [0u8; 32];
146 r.read_exact(&mut work_buf)?;
147 data.subtree_total_work = U256::from_little_endian(&work_buf);
148
149 data.start_height = Self::read_compact(r)?;
150 data.end_height = Self::read_compact(r)?;
151 data.sapling_tx = Self::read_compact(r)?;
152
153 Ok(data)
154 }
155
156 pub fn to_bytes(&self) -> Vec<u8> {
158 crate::V1::to_bytes(self)
159 }
160
161 pub fn from_bytes<T: AsRef<[u8]>>(consensus_branch_id: u32, buf: T) -> std::io::Result<Self> {
163 crate::V1::from_bytes(consensus_branch_id, buf)
164 }
165
166 pub fn hash(&self) -> [u8; 32] {
168 crate::V1::hash(self)
169 }
170}
171
172#[derive(Debug, Clone, Default)]
174#[cfg_attr(test, derive(PartialEq, Eq))]
175pub struct V2 {
176 pub v1: NodeData,
178 pub start_orchard_root: [u8; 32],
180 pub end_orchard_root: [u8; 32],
182 pub orchard_tx: u64,
184}
185
186impl V2 {
187 pub(crate) fn combine_inner(subtree_commitment: [u8; 32], left: &V2, right: &V2) -> V2 {
188 V2 {
189 v1: NodeData::combine_inner(subtree_commitment, &left.v1, &right.v1),
190 start_orchard_root: left.start_orchard_root,
191 end_orchard_root: right.end_orchard_root,
192 orchard_tx: left.orchard_tx + right.orchard_tx,
193 }
194 }
195
196 pub fn write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
198 self.v1.write(w)?;
199 w.write_all(&self.start_orchard_root)?;
200 w.write_all(&self.end_orchard_root)?;
201 NodeData::write_compact(w, self.orchard_tx)?;
202 Ok(())
203 }
204
205 pub fn read<R: std::io::Read>(consensus_branch_id: u32, r: &mut R) -> std::io::Result<Self> {
207 let mut data = V2 {
208 v1: NodeData::read(consensus_branch_id, r)?,
209 ..Default::default()
210 };
211 r.read_exact(&mut data.start_orchard_root)?;
212 r.read_exact(&mut data.end_orchard_root)?;
213 data.orchard_tx = NodeData::read_compact(r)?;
214
215 Ok(data)
216 }
217}
218
219#[cfg(any(test, feature = "test-dependencies"))]
220pub mod testing {
221 use primitive_types::U256;
222 use proptest::array::uniform32;
223 use proptest::prelude::{any, prop_compose};
224
225 use super::NodeData;
226
227 prop_compose! {
228 pub fn arb_node_data()(
229 subtree_commitment in uniform32(any::<u8>()),
230 start_time in any::<u32>(),
231 end_time in any::<u32>(),
232 start_target in any::<u32>(),
233 end_target in any::<u32>(),
234 start_sapling_root in uniform32(any::<u8>()),
235 end_sapling_root in uniform32(any::<u8>()),
236 subtree_total_work in uniform32(any::<u8>()),
237 start_height in any::<u64>(),
238 end_height in any::<u64>(),
239 sapling_tx in any::<u64>(),
240 ) -> NodeData {
241 NodeData {
242 consensus_branch_id: 0,
243 subtree_commitment,
244 start_time,
245 end_time,
246 start_target,
247 end_target,
248 start_sapling_root,
249 end_sapling_root,
250 subtree_total_work: U256::from_little_endian(&subtree_total_work[..]),
251 start_height,
252 end_height,
253 sapling_tx
254 }
255 }
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::testing::arb_node_data;
262 use proptest::prelude::*;
263
264 use super::NodeData;
265
266 proptest! {
267 #[test]
268 fn serialization_round_trip(node_data in arb_node_data()) {
269 assert_eq!(NodeData::from_bytes(0, node_data.to_bytes()).unwrap(), node_data);
270 }
271 }
272}