#[cfg(feature = "sync")]
use std::sync::OnceLock;
use crate::cell::*;
use crate::dict::Dict;
use crate::error::Error;
use crate::merkle::MerkleUpdate;
use crate::num::*;
use crate::util::*;
use crate::models::currency::CurrencyCollection;
use crate::models::global_version::GlobalVersion;
use crate::models::Lazy;
pub use self::block_extra::*;
pub use self::block_id::*;
pub use self::block_proof::*;
pub use self::shard_hashes::*;
mod block_extra;
mod block_id;
mod block_proof;
mod shard_hashes;
#[cfg(test)]
mod tests;
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Block {
pub global_id: i32,
pub info: Lazy<BlockInfo>,
pub value_flow: Lazy<ValueFlow>,
pub state_update: Lazy<MerkleUpdate>,
#[cfg(not(feature = "tycho"))]
pub out_msg_queue_updates: Option<Dict<u32, Lazy<MerkleUpdate>>>,
#[cfg(feature = "tycho")]
pub out_msg_queue_updates: OutMsgQueueUpdates,
pub extra: Lazy<BlockExtra>,
}
impl Block {
#[cfg(not(feature = "tycho"))]
const TAG_V1: u32 = 0x11ef55aa;
const TAG_V2: u32 = 0x11ef55bb;
const DATA_FOR_SIGN_SIZE: usize = 4 + 32 + 32;
const DATA_FOR_SIGN_TAG: [u8; 4] = [0x70, 0x6e, 0x0b, 0xc5];
pub fn load_info(&self) -> Result<BlockInfo, Error> {
self.info.load()
}
pub fn load_value_flow(&self) -> Result<ValueFlow, Error> {
self.value_flow.load()
}
pub fn load_state_update(&self) -> Result<MerkleUpdate, Error> {
self.state_update.load()
}
pub fn load_extra(&self) -> Result<BlockExtra, Error> {
self.extra.load()
}
pub fn build_data_for_sign(block_id: &BlockId) -> [u8; Self::DATA_FOR_SIGN_SIZE] {
let mut data = [0u8; Self::DATA_FOR_SIGN_SIZE];
data[0..4].copy_from_slice(&Self::DATA_FOR_SIGN_TAG);
data[4..36].copy_from_slice(block_id.root_hash.as_ref());
data[36..68].copy_from_slice(block_id.file_hash.as_ref());
data
}
}
impl Store for Block {
fn store_into(
&self,
builder: &mut CellBuilder,
context: &mut dyn CellContext,
) -> Result<(), Error> {
#[cfg(not(feature = "tycho"))]
let tag = if self.out_msg_queue_updates.is_none() {
Self::TAG_V1
} else {
Self::TAG_V2
};
#[cfg(feature = "tycho")]
let tag = Self::TAG_V2;
ok!(builder.store_u32(tag));
ok!(builder.store_u32(self.global_id as u32));
ok!(builder.store_reference(self.info.cell.clone()));
ok!(builder.store_reference(self.value_flow.cell.clone()));
#[cfg(not(feature = "tycho"))]
let out_msg_queue_updates = self.out_msg_queue_updates.as_ref();
#[cfg(feature = "tycho")]
let out_msg_queue_updates = Some(&self.out_msg_queue_updates);
ok!(if let Some(out_msg_queue_updates) = out_msg_queue_updates {
let cell = {
let mut builder = CellBuilder::new();
ok!(self.state_update.store_into(&mut builder, context));
ok!(out_msg_queue_updates.store_into(&mut builder, context));
ok!(builder.build_ext(context))
};
builder.store_reference(cell)
} else {
self.state_update.store_into(builder, context)
});
self.extra.store_into(builder, context)
}
}
impl<'a> Load<'a> for Block {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
#[cfg(not(feature = "tycho"))]
let with_out_msg_queue_updates = match ok!(slice.load_u32()) {
Self::TAG_V1 => false,
Self::TAG_V2 => true,
_ => return Err(Error::InvalidTag),
};
#[cfg(feature = "tycho")]
if ok!(slice.load_u32()) != Self::TAG_V2 {
return Err(Error::InvalidTag);
}
let global_id = ok!(slice.load_u32()) as i32;
let info = ok!(Lazy::load_from(slice));
let value_flow = ok!(Lazy::load_from(slice));
#[cfg(not(feature = "tycho"))]
let (state_update, out_msg_queue_updates) = if with_out_msg_queue_updates {
let slice = &mut ok!(slice.load_reference_as_slice());
(
ok!(Lazy::load_from(slice)),
Some(ok!(Dict::load_from(slice))),
)
} else {
(ok!(Lazy::load_from(slice)), None)
};
#[cfg(feature = "tycho")]
let (state_update, out_msg_queue_updates) = {
let slice = &mut ok!(slice.load_reference_as_slice());
(ok!(<_>::load_from(slice)), ok!(<_>::load_from(slice)))
};
Ok(Self {
global_id,
info,
value_flow,
state_update,
out_msg_queue_updates,
extra: ok!(<_>::load_from(slice)),
})
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BlockInfo {
pub version: u32,
pub after_merge: bool,
pub before_split: bool,
pub after_split: bool,
pub want_split: bool,
pub want_merge: bool,
pub key_block: bool,
pub flags: u8,
pub seqno: u32,
pub vert_seqno: u32,
pub shard: ShardIdent,
pub gen_utime: u32,
#[cfg(any(feature = "venom", feature = "tycho"))]
pub gen_utime_ms: u16,
pub start_lt: u64,
pub end_lt: u64,
pub gen_validator_list_hash_short: u32,
pub gen_catchain_seqno: u32,
pub min_ref_mc_seqno: u32,
pub prev_key_block_seqno: u32,
pub gen_software: GlobalVersion,
pub master_ref: Option<Lazy<BlockRef>>,
#[cfg_attr(feature = "serde", serde(with = "serde_prev_block_ref"))]
pub prev_ref: Cell,
pub prev_vert_ref: Option<Lazy<BlockRef>>,
}
#[cfg(feature = "sync")]
impl Default for BlockInfo {
fn default() -> Self {
Self {
version: 0,
after_merge: false,
before_split: false,
after_split: false,
want_split: false,
want_merge: false,
key_block: false,
flags: 0,
seqno: 0,
vert_seqno: 0,
shard: ShardIdent::MASTERCHAIN,
gen_utime: 0,
#[cfg(any(feature = "venom", feature = "tycho"))]
gen_utime_ms: 0,
start_lt: 0,
end_lt: 0,
gen_validator_list_hash_short: 0,
gen_catchain_seqno: 0,
min_ref_mc_seqno: 0,
prev_key_block_seqno: 0,
gen_software: Default::default(),
master_ref: None,
prev_ref: PrevBlockRef::empty_single_ref().clone(),
prev_vert_ref: None,
}
}
}
impl BlockInfo {
const TAG_V1: u32 = 0x9bc7a987;
#[cfg(any(feature = "venom", feature = "tycho"))]
const TAG_V2: u32 = 0x9bc7a988;
const FLAG_WITH_GEN_SOFTWARE: u8 = 0x1;
pub fn set_gen_software(&mut self, gen_software: Option<GlobalVersion>) {
if let Some(gen_software) = gen_software {
self.gen_software = gen_software;
self.flags |= BlockInfo::FLAG_WITH_GEN_SOFTWARE;
} else {
self.gen_software = Default::default();
self.flags &= !BlockInfo::FLAG_WITH_GEN_SOFTWARE;
}
}
pub fn load_master_ref(&self) -> Result<Option<BlockRef>, Error> {
match &self.master_ref {
Some(master_ref) => master_ref.load().map(Some),
None => Ok(None),
}
}
pub fn load_prev_ref(&self) -> Result<PrevBlockRef, Error> {
PrevBlockRef::load_from_cell(&self.prev_ref, self.after_merge)
}
pub fn set_prev_ref(&mut self, prev_ref: &PrevBlockRef) {
match prev_ref {
PrevBlockRef::Single(prev_ref) => self.set_prev_ref_single(prev_ref),
PrevBlockRef::AfterMerge { left, right } => self.set_prev_ref_after_merge(left, right),
}
}
pub fn set_prev_ref_single(&mut self, prev_ref: &BlockRef) {
self.prev_ref = CellBuilder::build_from(prev_ref).unwrap();
}
pub fn set_prev_ref_after_merge(&mut self, left: &BlockRef, right: &BlockRef) {
fn store_split_ref(left: &BlockRef, right: &BlockRef) -> Result<Cell, Error> {
let cx = &mut Cell::empty_context();
let mut builder = CellBuilder::new();
ok!(builder.store_reference(ok!(CellBuilder::build_from_ext(left, cx))));
ok!(builder.store_reference(ok!(CellBuilder::build_from_ext(right, cx))));
builder.build_ext(cx)
}
self.prev_ref = store_split_ref(left, right).unwrap();
}
}
impl Store for BlockInfo {
fn store_into(
&self,
builder: &mut CellBuilder,
context: &mut dyn CellContext,
) -> Result<(), Error> {
let packed_flags = ((self.master_ref.is_some() as u8) << 7)
| ((self.after_merge as u8) << 6)
| ((self.before_split as u8) << 5)
| ((self.after_split as u8) << 4)
| ((self.want_split as u8) << 3)
| ((self.want_merge as u8) << 2)
| ((self.key_block as u8) << 1)
| (self.prev_vert_ref.is_some() as u8);
#[cfg(not(any(feature = "venom", feature = "tycho")))]
ok!(builder.store_u32(Self::TAG_V1));
#[cfg(any(feature = "venom", feature = "tycho"))]
ok!(builder.store_u32(Self::TAG_V2));
ok!(builder.store_u32(self.version));
ok!(builder.store_u16(u16::from_be_bytes([packed_flags, self.flags])));
ok!(builder.store_u32(self.seqno));
ok!(builder.store_u32(self.vert_seqno));
ok!(self.shard.store_into(builder, context));
ok!(builder.store_u32(self.gen_utime));
#[cfg(any(feature = "venom", feature = "tycho"))]
ok!(builder.store_u16(self.gen_utime_ms));
ok!(builder.store_u64(self.start_lt));
ok!(builder.store_u64(self.end_lt));
ok!(builder.store_u32(self.gen_validator_list_hash_short));
ok!(builder.store_u32(self.gen_catchain_seqno));
ok!(builder.store_u32(self.min_ref_mc_seqno));
ok!(builder.store_u32(self.prev_key_block_seqno));
if self.flags & Self::FLAG_WITH_GEN_SOFTWARE != 0 {
ok!(self.gen_software.store_into(builder, context));
}
if let Some(master_ref) = &self.master_ref {
ok!(builder.store_reference(master_ref.cell.clone()));
}
ok!(builder.store_reference(self.prev_ref.clone()));
if let Some(prev_vert_ref) = &self.prev_vert_ref {
builder.store_reference(prev_vert_ref.cell.clone())
} else {
Ok(())
}
}
}
impl<'a> Load<'a> for BlockInfo {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
let with_ms = match slice.load_u32() {
Ok(Self::TAG_V1) => false,
#[cfg(any(feature = "venom", feature = "tycho"))]
Ok(Self::TAG_V2) => true,
Ok(_) => return Err(Error::InvalidTag),
Err(e) => return Err(e),
};
#[cfg(not(any(feature = "venom", feature = "tycho")))]
let _ = with_ms;
let version = ok!(slice.load_u32());
let [packed_flags, flags] = ok!(slice.load_u16()).to_be_bytes();
let seqno = ok!(slice.load_u32());
if seqno == 0 {
return Err(Error::InvalidData);
}
let vert_seqno = ok!(slice.load_u32());
let shard = ok!(ShardIdent::load_from(slice));
let gen_utime = ok!(slice.load_u32());
#[cfg(any(feature = "venom", feature = "tycho"))]
let gen_utime_ms = if with_ms { ok!(slice.load_u16()) } else { 0 };
let start_lt = ok!(slice.load_u64());
let end_lt = ok!(slice.load_u64());
let gen_validator_list_hash_short = ok!(slice.load_u32());
let gen_catchain_seqno = ok!(slice.load_u32());
let min_ref_mc_seqno = ok!(slice.load_u32());
let prev_key_block_seqno = ok!(slice.load_u32());
let gen_software = if flags & Self::FLAG_WITH_GEN_SOFTWARE != 0 {
ok!(GlobalVersion::load_from(slice))
} else {
GlobalVersion::default()
};
let master_ref = if packed_flags & 0b10000000 != 0 {
Some(ok!(Lazy::<BlockRef>::load_from(slice)))
} else {
None
};
let prev_ref = ok!(slice.load_reference_cloned());
let prev_vert_ref = if packed_flags & 0b00000001 != 0 {
Some(ok!(Lazy::<BlockRef>::load_from(slice)))
} else {
None
};
if vert_seqno < prev_vert_ref.is_some() as u32 {
return Err(Error::InvalidData);
}
Ok(Self {
version,
after_merge: packed_flags & 0b01000000 != 0,
before_split: packed_flags & 0b00100000 != 0,
after_split: packed_flags & 0b00010000 != 0,
want_split: packed_flags & 0b00001000 != 0,
want_merge: packed_flags & 0b00000100 != 0,
key_block: packed_flags & 0b00000010 != 0,
flags,
seqno,
vert_seqno,
shard,
gen_utime,
#[cfg(any(feature = "venom", feature = "tycho"))]
gen_utime_ms,
start_lt,
end_lt,
gen_validator_list_hash_short,
gen_catchain_seqno,
min_ref_mc_seqno,
prev_key_block_seqno,
gen_software,
master_ref,
prev_ref,
prev_vert_ref,
})
}
}
#[cfg(feature = "serde")]
mod serde_prev_block_ref {
use super::*;
pub fn serialize<S: serde::Serializer>(value: &Cell, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::{Error, Serialize};
if serializer.is_human_readable() {
let prev_block_ref = ok!(PrevBlockRef::load_from_cell(
value,
value.reference_count() > 0
)
.map_err(Error::custom));
prev_block_ref.serialize(serializer)
} else {
crate::boc::Boc::serialize(value, serializer)
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Cell, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{Deserialize, Error};
if deserializer.is_human_readable() {
PrevBlockRef::deserialize(deserializer).and_then(|prev_block_ref| {
CellBuilder::build_from(prev_block_ref).map_err(Error::custom)
})
} else {
crate::boc::Boc::deserialize(deserializer)
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", content = "id"))]
pub enum PrevBlockRef {
Single(BlockRef),
AfterMerge {
left: BlockRef,
right: BlockRef,
},
}
impl PrevBlockRef {
#[cfg(feature = "sync")]
pub fn empty_single_ref() -> &'static Cell {
static CELL: OnceLock<Cell> = OnceLock::new();
CELL.get_or_init(|| {
CellBuilder::build_from(&BlockRef {
end_lt: 0,
seqno: 0,
root_hash: HashBytes::ZERO,
file_hash: HashBytes::ZERO,
})
.unwrap()
})
}
fn load_from_cell(value: &Cell, after_merge: bool) -> Result<Self, Error> {
let mut s = ok!(value.as_slice());
Ok(if unlikely(after_merge) {
let left = ok!(BlockRef::load_from(&mut ok!(s.load_reference_as_slice())));
let right = ok!(BlockRef::load_from(&mut ok!(s.load_reference_as_slice())));
PrevBlockRef::AfterMerge { left, right }
} else {
PrevBlockRef::Single(ok!(BlockRef::load_from(&mut s)))
})
}
}
impl Store for PrevBlockRef {
fn store_into(&self, builder: &mut CellBuilder, cx: &mut dyn CellContext) -> Result<(), Error> {
match self {
PrevBlockRef::Single(block_ref) => block_ref.store_into(builder, cx),
PrevBlockRef::AfterMerge { left, right } => {
ok!(builder.store_reference(ok!(CellBuilder::build_from_ext(left, cx))));
builder.store_reference(ok!(CellBuilder::build_from_ext(right, cx)))
}
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BlockRef {
pub end_lt: u64,
pub seqno: u32,
pub root_hash: HashBytes,
pub file_hash: HashBytes,
}
impl BlockRef {
pub fn as_block_id(&self, shard: ShardIdent) -> BlockId {
BlockId {
shard,
seqno: self.seqno,
root_hash: self.root_hash,
file_hash: self.file_hash,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ValueFlow {
pub from_prev_block: CurrencyCollection,
pub to_next_block: CurrencyCollection,
pub imported: CurrencyCollection,
pub exported: CurrencyCollection,
pub fees_collected: CurrencyCollection,
pub fees_imported: CurrencyCollection,
pub recovered: CurrencyCollection,
pub created: CurrencyCollection,
pub minted: CurrencyCollection,
pub copyleft_rewards: Dict<HashBytes, Tokens>,
}
impl ValueFlow {
const TAG_V1: u32 = 0xb8e48dfb;
const TAG_V2: u32 = 0xe0864f6d;
}
impl Store for ValueFlow {
fn store_into(
&self,
builder: &mut CellBuilder,
context: &mut dyn CellContext,
) -> Result<(), Error> {
let tag = if self.copyleft_rewards.is_empty() {
Self::TAG_V1
} else {
Self::TAG_V2
};
let cell1 = {
let mut builder = CellBuilder::new();
ok!(self.from_prev_block.store_into(&mut builder, context));
ok!(self.to_next_block.store_into(&mut builder, context));
ok!(self.imported.store_into(&mut builder, context));
ok!(self.exported.store_into(&mut builder, context));
ok!(builder.build_ext(context))
};
ok!(builder.store_u32(tag));
ok!(builder.store_reference(cell1));
ok!(self.fees_collected.store_into(builder, context));
let cell2 = {
let mut builder = CellBuilder::new();
ok!(self.fees_imported.store_into(&mut builder, context));
ok!(self.recovered.store_into(&mut builder, context));
ok!(self.created.store_into(&mut builder, context));
ok!(self.minted.store_into(&mut builder, context));
ok!(builder.build_ext(context))
};
ok!(builder.store_reference(cell2));
if !self.copyleft_rewards.is_empty() {
self.copyleft_rewards.store_into(builder, context)
} else {
Ok(())
}
}
}
impl<'a> Load<'a> for ValueFlow {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
let with_copyleft_rewards = match ok!(slice.load_u32()) {
Self::TAG_V1 => false,
Self::TAG_V2 => true,
_ => return Err(Error::InvalidTag),
};
let fees_collected = ok!(CurrencyCollection::load_from(slice));
let slice1 = &mut ok!(slice.load_reference_as_slice());
let slice2 = &mut ok!(slice.load_reference_as_slice());
let copyleft_rewards = if with_copyleft_rewards {
ok!(Dict::load_from(slice))
} else {
Dict::new()
};
Ok(Self {
from_prev_block: ok!(CurrencyCollection::load_from(slice1)),
to_next_block: ok!(CurrencyCollection::load_from(slice1)),
imported: ok!(CurrencyCollection::load_from(slice1)),
exported: ok!(CurrencyCollection::load_from(slice1)),
fees_collected,
fees_imported: ok!(CurrencyCollection::load_from(slice2)),
recovered: ok!(CurrencyCollection::load_from(slice2)),
created: ok!(CurrencyCollection::load_from(slice2)),
minted: ok!(CurrencyCollection::load_from(slice2)),
copyleft_rewards,
})
}
}
#[cfg(feature = "tycho")]
#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[tlb(tag = "#1")]
pub struct OutMsgQueueUpdates {
pub diff_hash: HashBytes,
}