1#[cfg(feature = "sync")]
4use std::sync::OnceLock;
5
6pub use self::block_extra::*;
7pub use self::block_id::*;
8pub use self::block_proof::*;
9pub use self::shard_hashes::*;
10use crate::cell::*;
11#[allow(unused)]
12use crate::dict::Dict;
13use crate::error::Error;
14use crate::merkle::MerkleUpdate;
15use crate::models::currency::CurrencyCollection;
16use crate::models::global_version::GlobalVersion;
17use crate::util::*;
18
19mod block_extra;
20mod block_id;
21mod block_proof;
22mod shard_hashes;
23
24#[cfg(test)]
25mod tests;
26
27#[derive(Debug, Clone, Eq, PartialEq)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize))]
30pub struct Block {
31 pub global_id: i32,
33 pub info: Lazy<BlockInfo>,
35 pub value_flow: Lazy<ValueFlow>,
37 pub state_update: LazyExotic<MerkleUpdate>,
39 #[cfg(not(feature = "tycho"))]
41 pub out_msg_queue_updates: Option<Dict<u32, LazyExotic<MerkleUpdate>>>,
42 #[cfg(feature = "tycho")]
44 pub out_msg_queue_updates: OutMsgQueueUpdates,
45 pub extra: Lazy<BlockExtra>,
47}
48
49impl Block {
50 #[cfg(not(feature = "tycho"))]
51 const TAG_V1: u32 = 0x11ef55aa;
52 const TAG_V2: u32 = 0x11ef55bb;
53
54 const DATA_FOR_SIGN_SIZE: usize = 4 + 32 + 32;
55 const DATA_FOR_SIGN_TAG: [u8; 4] = [0x70, 0x6e, 0x0b, 0xc5];
56
57 pub fn load_info(&self) -> Result<BlockInfo, Error> {
59 self.info.load()
60 }
61
62 pub fn load_value_flow(&self) -> Result<ValueFlow, Error> {
64 self.value_flow.load()
65 }
66
67 pub fn load_state_update(&self) -> Result<MerkleUpdate, Error> {
69 self.state_update.load()
70 }
71
72 pub fn load_extra(&self) -> Result<BlockExtra, Error> {
74 self.extra.load()
75 }
76
77 pub fn build_data_for_sign(block_id: &BlockId) -> [u8; Self::DATA_FOR_SIGN_SIZE] {
79 let mut data = [0u8; Self::DATA_FOR_SIGN_SIZE];
80 data[0..4].copy_from_slice(&Self::DATA_FOR_SIGN_TAG);
81 data[4..36].copy_from_slice(block_id.root_hash.as_ref());
82 data[36..68].copy_from_slice(block_id.file_hash.as_ref());
83 data
84 }
85}
86
87impl Store for Block {
88 fn store_into(
89 &self,
90 builder: &mut CellBuilder,
91 context: &dyn CellContext,
92 ) -> Result<(), Error> {
93 #[cfg(not(feature = "tycho"))]
94 let tag = if self.out_msg_queue_updates.is_none() {
95 Self::TAG_V1
96 } else {
97 Self::TAG_V2
98 };
99 #[cfg(feature = "tycho")]
100 let tag = Self::TAG_V2;
101
102 ok!(builder.store_u32(tag));
103 ok!(builder.store_u32(self.global_id as u32));
104 ok!(builder.store_reference(self.info.inner().clone()));
105 ok!(builder.store_reference(self.value_flow.inner().clone()));
106
107 #[cfg(not(feature = "tycho"))]
108 let out_msg_queue_updates = self.out_msg_queue_updates.as_ref();
109 #[cfg(feature = "tycho")]
110 let out_msg_queue_updates = Some(&self.out_msg_queue_updates);
111
112 ok!(if let Some(out_msg_queue_updates) = out_msg_queue_updates {
113 let cell = {
114 let mut builder = CellBuilder::new();
115 ok!(self.state_update.store_into(&mut builder, context));
116 ok!(out_msg_queue_updates.store_into(&mut builder, context));
117 ok!(builder.build_ext(context))
118 };
119 builder.store_reference(cell)
120 } else {
121 self.state_update.store_into(builder, context)
122 });
123
124 self.extra.store_into(builder, context)
125 }
126}
127
128impl<'a> Load<'a> for Block {
129 fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
130 #[cfg(not(feature = "tycho"))]
131 let with_out_msg_queue_updates = match ok!(slice.load_u32()) {
132 Self::TAG_V1 => false,
133 Self::TAG_V2 => true,
134 _ => return Err(Error::InvalidTag),
135 };
136
137 #[cfg(feature = "tycho")]
138 if ok!(slice.load_u32()) != Self::TAG_V2 {
139 return Err(Error::InvalidTag);
140 }
141
142 let global_id = ok!(slice.load_u32()) as i32;
143 let info = ok!(Lazy::load_from(slice));
144 let value_flow = ok!(Lazy::load_from(slice));
145
146 #[cfg(not(feature = "tycho"))]
147 let (state_update, out_msg_queue_updates) = if with_out_msg_queue_updates {
148 let slice = &mut ok!(slice.load_reference_as_slice());
149 (
150 ok!(Lazy::load_from(slice)),
151 Some(ok!(Dict::load_from(slice))),
152 )
153 } else {
154 (ok!(Lazy::load_from(slice)), None)
155 };
156
157 #[cfg(feature = "tycho")]
158 let (state_update, out_msg_queue_updates) = {
159 let slice = &mut ok!(slice.load_reference_as_slice());
160 (ok!(<_>::load_from(slice)), ok!(<_>::load_from(slice)))
161 };
162
163 Ok(Self {
164 global_id,
165 info,
166 value_flow,
167 state_update,
168 out_msg_queue_updates,
169 extra: ok!(<_>::load_from(slice)),
170 })
171 }
172}
173
174#[derive(Debug, Clone, Eq, PartialEq)]
176#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
177pub struct BlockInfo {
178 pub version: u32,
180 pub after_merge: bool,
182 pub before_split: bool,
184 pub after_split: bool,
186 pub want_split: bool,
188 pub want_merge: bool,
190 pub key_block: bool,
192
193 pub flags: u8,
197 pub seqno: u32,
199 pub vert_seqno: u32,
201
202 pub shard: ShardIdent,
204 pub gen_utime: u32,
206 #[cfg(feature = "tycho")]
208 pub gen_utime_ms: u16,
209 pub start_lt: u64,
211 pub end_lt: u64,
213 pub gen_validator_list_hash_short: u32,
215 pub gen_catchain_seqno: u32,
217 pub min_ref_mc_seqno: u32,
219 pub prev_key_block_seqno: u32,
221 pub gen_software: GlobalVersion,
223
224 pub master_ref: Option<Lazy<BlockRef>>,
226 #[cfg_attr(feature = "serde", serde(with = "serde_prev_block_ref"))]
228 pub prev_ref: Cell,
229 pub prev_vert_ref: Option<Lazy<BlockRef>>,
231}
232
233#[cfg(feature = "sync")]
234impl Default for BlockInfo {
235 fn default() -> Self {
236 Self {
237 version: 0,
238 after_merge: false,
239 before_split: false,
240 after_split: false,
241 want_split: false,
242 want_merge: false,
243 key_block: false,
244 flags: 0,
245 seqno: 0,
246 vert_seqno: 0,
247 shard: ShardIdent::MASTERCHAIN,
248 gen_utime: 0,
249 #[cfg(feature = "tycho")]
250 gen_utime_ms: 0,
251 start_lt: 0,
252 end_lt: 0,
253 gen_validator_list_hash_short: 0,
254 gen_catchain_seqno: 0,
255 min_ref_mc_seqno: 0,
256 prev_key_block_seqno: 0,
257 gen_software: Default::default(),
258 master_ref: None,
259 prev_ref: PrevBlockRef::empty_single_ref().clone(),
260 prev_vert_ref: None,
261 }
262 }
263}
264
265impl BlockInfo {
266 const TAG_V1: u32 = 0x9bc7a987;
267 #[cfg(feature = "tycho")]
268 const TAG_V2: u32 = 0x9bc7a988;
269 const FLAG_WITH_GEN_SOFTWARE: u8 = 0x1;
270
271 pub fn set_gen_software(&mut self, gen_software: Option<GlobalVersion>) {
273 if let Some(gen_software) = gen_software {
274 self.gen_software = gen_software;
275 self.flags |= BlockInfo::FLAG_WITH_GEN_SOFTWARE;
276 } else {
277 self.gen_software = Default::default();
278 self.flags &= !BlockInfo::FLAG_WITH_GEN_SOFTWARE;
279 }
280 }
281
282 pub fn load_master_ref(&self) -> Result<Option<BlockRef>, Error> {
284 match &self.master_ref {
285 Some(master_ref) => master_ref.load().map(Some),
286 None => Ok(None),
287 }
288 }
289
290 pub fn load_prev_ref(&self) -> Result<PrevBlockRef, Error> {
292 PrevBlockRef::load_from_cell(&self.prev_ref, self.after_merge)
293 }
294
295 pub fn set_prev_ref(&mut self, prev_ref: &PrevBlockRef) {
297 match prev_ref {
298 PrevBlockRef::Single(prev_ref) => self.set_prev_ref_single(prev_ref),
299 PrevBlockRef::AfterMerge { left, right } => self.set_prev_ref_after_merge(left, right),
300 }
301 }
302
303 pub fn set_prev_ref_single(&mut self, prev_ref: &BlockRef) {
305 self.prev_ref = CellBuilder::build_from(prev_ref).unwrap();
307 }
308
309 pub fn set_prev_ref_after_merge(&mut self, left: &BlockRef, right: &BlockRef) {
311 fn store_split_ref(left: &BlockRef, right: &BlockRef) -> Result<Cell, Error> {
312 let cx = Cell::empty_context();
313 let mut builder = CellBuilder::new();
314 ok!(builder.store_reference(ok!(CellBuilder::build_from_ext(left, cx))));
315 ok!(builder.store_reference(ok!(CellBuilder::build_from_ext(right, cx))));
316 builder.build_ext(cx)
317 }
318
319 self.prev_ref = store_split_ref(left, right).unwrap();
321 }
322}
323
324impl Store for BlockInfo {
325 fn store_into(
326 &self,
327 builder: &mut CellBuilder,
328 context: &dyn CellContext,
329 ) -> Result<(), Error> {
330 let packed_flags = ((self.master_ref.is_some() as u8) << 7)
331 | ((self.after_merge as u8) << 6)
332 | ((self.before_split as u8) << 5)
333 | ((self.after_split as u8) << 4)
334 | ((self.want_split as u8) << 3)
335 | ((self.want_merge as u8) << 2)
336 | ((self.key_block as u8) << 1)
337 | (self.prev_vert_ref.is_some() as u8);
338
339 #[cfg(not(feature = "tycho"))]
340 ok!(builder.store_u32(Self::TAG_V1));
341 #[cfg(feature = "tycho")]
342 ok!(builder.store_u32(Self::TAG_V2));
343
344 ok!(builder.store_u32(self.version));
345 ok!(builder.store_u16(u16::from_be_bytes([packed_flags, self.flags])));
346 ok!(builder.store_u32(self.seqno));
347 ok!(builder.store_u32(self.vert_seqno));
348 ok!(self.shard.store_into(builder, context));
349 ok!(builder.store_u32(self.gen_utime));
350 #[cfg(feature = "tycho")]
351 ok!(builder.store_u16(self.gen_utime_ms));
352 ok!(builder.store_u64(self.start_lt));
353 ok!(builder.store_u64(self.end_lt));
354 ok!(builder.store_u32(self.gen_validator_list_hash_short));
355 ok!(builder.store_u32(self.gen_catchain_seqno));
356 ok!(builder.store_u32(self.min_ref_mc_seqno));
357 ok!(builder.store_u32(self.prev_key_block_seqno));
358
359 if self.flags & Self::FLAG_WITH_GEN_SOFTWARE != 0 {
360 ok!(self.gen_software.store_into(builder, context));
361 }
362
363 if let Some(master_ref) = &self.master_ref {
364 ok!(builder.store_reference(master_ref.inner().clone()));
365 }
366
367 ok!(builder.store_reference(self.prev_ref.clone()));
368
369 if let Some(prev_vert_ref) = &self.prev_vert_ref {
370 builder.store_reference(prev_vert_ref.inner().clone())
371 } else {
372 Ok(())
373 }
374 }
375}
376
377impl<'a> Load<'a> for BlockInfo {
378 fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
379 let with_ms = match slice.load_u32() {
380 Ok(Self::TAG_V1) => false,
381 #[cfg(feature = "tycho")]
382 Ok(Self::TAG_V2) => true,
383 Ok(_) => return Err(Error::InvalidTag),
384 Err(e) => return Err(e),
385 };
386
387 #[cfg(not(feature = "tycho"))]
388 let _ = with_ms;
389
390 let version = ok!(slice.load_u32());
391 let [packed_flags, flags] = ok!(slice.load_u16()).to_be_bytes();
392 let seqno = ok!(slice.load_u32());
393 if seqno == 0 {
394 return Err(Error::InvalidData);
395 }
396 let vert_seqno = ok!(slice.load_u32());
397 let shard = ok!(ShardIdent::load_from(slice));
398 let gen_utime = ok!(slice.load_u32());
399 #[cfg(feature = "tycho")]
400 let gen_utime_ms = if with_ms { ok!(slice.load_u16()) } else { 0 };
401 let start_lt = ok!(slice.load_u64());
402 let end_lt = ok!(slice.load_u64());
403 let gen_validator_list_hash_short = ok!(slice.load_u32());
404 let gen_catchain_seqno = ok!(slice.load_u32());
405 let min_ref_mc_seqno = ok!(slice.load_u32());
406 let prev_key_block_seqno = ok!(slice.load_u32());
407
408 let gen_software = if flags & Self::FLAG_WITH_GEN_SOFTWARE != 0 {
409 ok!(GlobalVersion::load_from(slice))
410 } else {
411 GlobalVersion::default()
412 };
413
414 let master_ref = if packed_flags & 0b10000000 != 0 {
415 Some(ok!(Lazy::<BlockRef>::load_from(slice)))
416 } else {
417 None
418 };
419
420 let prev_ref = ok!(slice.load_reference_cloned());
421
422 let prev_vert_ref = if packed_flags & 0b00000001 != 0 {
423 Some(ok!(Lazy::<BlockRef>::load_from(slice)))
424 } else {
425 None
426 };
427
428 if vert_seqno < prev_vert_ref.is_some() as u32 {
429 return Err(Error::InvalidData);
430 }
431
432 Ok(Self {
433 version,
434 after_merge: packed_flags & 0b01000000 != 0,
435 before_split: packed_flags & 0b00100000 != 0,
436 after_split: packed_flags & 0b00010000 != 0,
437 want_split: packed_flags & 0b00001000 != 0,
438 want_merge: packed_flags & 0b00000100 != 0,
439 key_block: packed_flags & 0b00000010 != 0,
440 flags,
441 seqno,
442 vert_seqno,
443 shard,
444 gen_utime,
445 #[cfg(feature = "tycho")]
446 gen_utime_ms,
447 start_lt,
448 end_lt,
449 gen_validator_list_hash_short,
450 gen_catchain_seqno,
451 min_ref_mc_seqno,
452 prev_key_block_seqno,
453 gen_software,
454 master_ref,
455 prev_ref,
456 prev_vert_ref,
457 })
458 }
459}
460
461#[cfg(feature = "serde")]
462mod serde_prev_block_ref {
463 use super::*;
464
465 pub fn serialize<S: serde::Serializer>(value: &Cell, serializer: S) -> Result<S::Ok, S::Error> {
466 use serde::ser::{Error, Serialize};
467
468 if serializer.is_human_readable() {
469 let prev_block_ref = ok!(PrevBlockRef::load_from_cell(
470 value,
471 value.reference_count() > 0
472 )
473 .map_err(Error::custom));
474 prev_block_ref.serialize(serializer)
475 } else {
476 crate::boc::Boc::serialize(value, serializer)
477 }
478 }
479
480 pub fn deserialize<'de, D>(deserializer: D) -> Result<Cell, D::Error>
481 where
482 D: serde::Deserializer<'de>,
483 {
484 use serde::de::{Deserialize, Error};
485
486 if deserializer.is_human_readable() {
487 PrevBlockRef::deserialize(deserializer).and_then(|prev_block_ref| {
488 CellBuilder::build_from(prev_block_ref).map_err(Error::custom)
489 })
490 } else {
491 crate::boc::Boc::deserialize(deserializer)
492 }
493 }
494}
495
496#[derive(Debug, Clone, Eq, PartialEq)]
498#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
499#[cfg_attr(feature = "serde", serde(tag = "type", content = "id"))]
500pub enum PrevBlockRef {
501 Single(BlockRef),
503 AfterMerge {
505 left: BlockRef,
511 right: BlockRef,
517 },
518}
519
520impl PrevBlockRef {
521 #[cfg(feature = "sync")]
523 pub fn empty_single_ref() -> &'static Cell {
524 static CELL: OnceLock<Cell> = OnceLock::new();
525 CELL.get_or_init(|| {
526 CellBuilder::build_from(&BlockRef {
527 end_lt: 0,
528 seqno: 0,
529 root_hash: HashBytes::ZERO,
530 file_hash: HashBytes::ZERO,
531 })
532 .unwrap()
533 })
534 }
535
536 fn load_from_cell(value: &Cell, after_merge: bool) -> Result<Self, Error> {
537 let mut s = ok!(value.as_slice());
538 Ok(if unlikely(after_merge) {
539 let left = ok!(BlockRef::load_from(&mut ok!(s.load_reference_as_slice())));
540 let right = ok!(BlockRef::load_from(&mut ok!(s.load_reference_as_slice())));
541 PrevBlockRef::AfterMerge { left, right }
542 } else {
543 PrevBlockRef::Single(ok!(BlockRef::load_from(&mut s)))
544 })
545 }
546}
547
548impl Store for PrevBlockRef {
549 fn store_into(&self, builder: &mut CellBuilder, cx: &dyn CellContext) -> Result<(), Error> {
550 match self {
551 PrevBlockRef::Single(block_ref) => block_ref.store_into(builder, cx),
552 PrevBlockRef::AfterMerge { left, right } => {
553 ok!(builder.store_reference(ok!(CellBuilder::build_from_ext(left, cx))));
554 builder.store_reference(ok!(CellBuilder::build_from_ext(right, cx)))
555 }
556 }
557 }
558}
559
560#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
562#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
563pub struct BlockRef {
564 pub end_lt: u64,
566 pub seqno: u32,
568 pub root_hash: HashBytes,
570 pub file_hash: HashBytes,
572}
573
574impl BlockRef {
575 pub fn as_block_id(&self, shard: ShardIdent) -> BlockId {
577 BlockId {
578 shard,
579 seqno: self.seqno,
580 root_hash: self.root_hash,
581 file_hash: self.file_hash,
582 }
583 }
584}
585
586#[derive(Debug, Clone, Eq, PartialEq, Default)]
588#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
589pub struct ValueFlow {
590 pub from_prev_block: CurrencyCollection,
592 pub to_next_block: CurrencyCollection,
594 pub imported: CurrencyCollection,
596 pub exported: CurrencyCollection,
598
599 pub fees_collected: CurrencyCollection,
601
602 pub burned: CurrencyCollection,
604
605 pub fees_imported: CurrencyCollection,
607 pub recovered: CurrencyCollection,
609 pub created: CurrencyCollection,
611 pub minted: CurrencyCollection,
613}
614
615impl ValueFlow {
616 const TAG_V1: u32 = 0xb8e48dfb;
617 const TAG_V2: u32 = 0x3ebf98b7;
618
619 pub fn validate(&self) -> Result<bool, Error> {
621 let in_val = self
622 .from_prev_block
623 .checked_add(&self.imported)
624 .and_then(|val| val.checked_add(&self.fees_imported))
625 .and_then(|val| val.checked_add(&self.created))
626 .and_then(|val| val.checked_add(&self.minted))
627 .and_then(|val| val.checked_add(&self.recovered))?;
628
629 let out_val = self
630 .to_next_block
631 .checked_add(&self.exported)
632 .and_then(|val| val.checked_add(&self.fees_collected))
633 .and_then(|val| val.checked_add(&self.burned))?;
634
635 Ok(in_val == out_val)
636 }
637}
638
639impl Store for ValueFlow {
640 fn store_into(
641 &self,
642 builder: &mut CellBuilder,
643 context: &dyn CellContext,
644 ) -> Result<(), Error> {
645 let (tag, store_burned) = if self.burned.is_zero() {
646 (Self::TAG_V1, false)
647 } else {
648 (Self::TAG_V2, true)
649 };
650
651 let cell1 = {
652 let mut builder = CellBuilder::new();
653 ok!(self.from_prev_block.store_into(&mut builder, context));
654 ok!(self.to_next_block.store_into(&mut builder, context));
655 ok!(self.imported.store_into(&mut builder, context));
656 ok!(self.exported.store_into(&mut builder, context));
657 ok!(builder.build_ext(context))
658 };
659
660 ok!(builder.store_u32(tag));
661 ok!(builder.store_reference(cell1));
662
663 ok!(self.fees_collected.store_into(builder, context));
664 if store_burned {
665 ok!(self.burned.store_into(builder, context));
666 }
667
668 let cell2 = {
669 let mut builder = CellBuilder::new();
670 ok!(self.fees_imported.store_into(&mut builder, context));
671 ok!(self.recovered.store_into(&mut builder, context));
672 ok!(self.created.store_into(&mut builder, context));
673 ok!(self.minted.store_into(&mut builder, context));
674 ok!(builder.build_ext(context))
675 };
676 builder.store_reference(cell2)
677 }
678}
679
680impl<'a> Load<'a> for ValueFlow {
681 fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
682 let with_burned = match ok!(slice.load_u32()) {
683 Self::TAG_V1 => false,
684 Self::TAG_V2 => true,
685 _ => return Err(Error::InvalidTag),
686 };
687
688 let fees_collected = ok!(CurrencyCollection::load_from(slice));
689 let burned = if with_burned {
690 ok!(CurrencyCollection::load_from(slice))
691 } else {
692 CurrencyCollection::ZERO
693 };
694
695 let slice1 = &mut ok!(slice.load_reference_as_slice());
696 let slice2 = &mut ok!(slice.load_reference_as_slice());
697
698 Ok(Self {
699 from_prev_block: ok!(CurrencyCollection::load_from(slice1)),
700 to_next_block: ok!(CurrencyCollection::load_from(slice1)),
701 imported: ok!(CurrencyCollection::load_from(slice1)),
702 exported: ok!(CurrencyCollection::load_from(slice1)),
703 fees_collected,
704 burned,
705 fees_imported: ok!(CurrencyCollection::load_from(slice2)),
706 recovered: ok!(CurrencyCollection::load_from(slice2)),
707 created: ok!(CurrencyCollection::load_from(slice2)),
708 minted: ok!(CurrencyCollection::load_from(slice2)),
709 })
710 }
711}
712
713#[cfg(feature = "tycho")]
715#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
716#[cfg_attr(feature = "serde", derive(serde::Serialize))]
717#[tlb(tag = "#1")]
718pub struct OutMsgQueueUpdates {
719 pub diff_hash: HashBytes,
721 pub tail_len: u32,
724}