use std::num::{NonZeroU16, NonZeroU32, NonZeroU8};
use crate::cell::*;
use crate::dict::Dict;
use crate::error::Error;
use crate::num::{Tokens, Uint12};
use crate::util::*;
use crate::models::block::ShardIdent;
use crate::models::Lazy;
#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
#[tlb(tag = "#91")]
pub struct ConfigVotingSetup {
pub normal_params: Lazy<ConfigProposalSetup>,
pub critical_params: Lazy<ConfigProposalSetup>,
}
#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
#[tlb(tag = "#36")]
pub struct ConfigProposalSetup {
pub min_total_rounds: u8,
pub max_total_rounds: u8,
pub min_wins: u8,
pub max_losses: u8,
pub min_store_sec: u32,
pub max_store_sec: u32,
pub bit_price: u32,
pub cell_price: u32,
}
#[derive(CustomDebug, Clone, Eq, PartialEq)]
pub struct WorkchainDescription {
pub enabled_since: u32,
pub actual_min_split: u8,
pub min_split: u8,
pub max_split: u8,
pub active: bool,
pub accept_msgs: bool,
#[debug(with = "DisplayHash")]
pub zerostate_root_hash: CellHash,
#[debug(with = "DisplayHash")]
pub zerostate_file_hash: CellHash,
pub version: u32,
pub format: WorkchainFormat,
}
impl WorkchainDescription {
const TAG: u8 = 0xa6;
pub fn is_valid(&self) -> bool {
self.min_split <= self.max_split
&& self.max_split <= ShardIdent::MAX_SPLIT_DEPTH
&& self.format.is_valid()
}
}
impl Store for WorkchainDescription {
fn store_into(
&self,
builder: &mut CellBuilder,
finalizer: &mut dyn Finalizer,
) -> Result<(), Error> {
if !self.is_valid() {
return Err(Error::InvalidData);
}
let flags: u16 = ((self.format.is_basic() as u16) << 15)
| ((self.active as u16) << 14)
| ((self.accept_msgs as u16) << 13);
ok!(builder.store_u8(Self::TAG));
ok!(builder.store_u32(self.enabled_since));
ok!(builder.store_u8(self.actual_min_split));
ok!(builder.store_u8(self.min_split));
ok!(builder.store_u8(self.max_split));
ok!(builder.store_u16(flags));
ok!(builder.store_u256(&self.zerostate_root_hash));
ok!(builder.store_u256(&self.zerostate_file_hash));
ok!(builder.store_u32(self.version));
self.format.store_into(builder, finalizer)
}
}
impl<'a> Load<'a> for WorkchainDescription {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
match slice.load_u8() {
Ok(Self::TAG) => {}
Ok(_) => return Err(Error::InvalidTag),
Err(e) => return Err(e),
}
let enabled_since = ok!(slice.load_u32());
let actual_min_split = ok!(slice.load_u8());
let min_split = ok!(slice.load_u8());
let max_split = ok!(slice.load_u8());
let flags = ok!(slice.load_u16());
if flags << 3 != 0 {
return Err(Error::InvalidData);
}
let result = Self {
enabled_since,
actual_min_split,
min_split,
max_split,
active: flags & 0b0100_0000_0000_0000 != 0,
accept_msgs: flags & 0b0010_0000_0000_0000 != 0,
zerostate_root_hash: ok!(slice.load_u256()),
zerostate_file_hash: ok!(slice.load_u256()),
version: ok!(slice.load_u32()),
format: ok!(WorkchainFormat::load_from(slice)),
};
let basic = flags & 0b1000_0000_0000_0000 != 0;
if basic != result.format.is_basic() {
return Err(Error::InvalidData);
}
Ok(result)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum WorkchainFormat {
Basic(WorkchainFormatBasic),
Extended(WorkchainFormatExtended),
}
impl WorkchainFormat {
pub fn is_valid(&self) -> bool {
match self {
Self::Basic(_) => true,
Self::Extended(format) => format.is_valid(),
}
}
pub fn is_basic(&self) -> bool {
matches!(self, Self::Basic(_))
}
}
impl Store for WorkchainFormat {
fn store_into(
&self,
builder: &mut CellBuilder,
finalizer: &mut dyn Finalizer,
) -> Result<(), Error> {
match self {
Self::Basic(value) => {
ok!(builder.store_small_uint(0x1, 4));
value.store_into(builder, finalizer)
}
Self::Extended(value) => {
ok!(builder.store_small_uint(0x0, 4));
value.store_into(builder, finalizer)
}
}
}
}
impl<'a> Load<'a> for WorkchainFormat {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
Ok(match ok!(slice.load_small_uint(4)) {
0x1 => Self::Basic(ok!(WorkchainFormatBasic::load_from(slice))),
0x0 => Self::Extended(ok!(WorkchainFormatExtended::load_from(slice))),
_ => return Err(Error::InvalidTag),
})
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
pub struct WorkchainFormatBasic {
pub vm_version: i32,
pub vm_mode: u64,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
#[tlb(validate_with = "Self::is_valid")]
pub struct WorkchainFormatExtended {
pub min_addr_len: Uint12,
pub max_addr_len: Uint12,
pub addr_len_step: Uint12,
pub workchain_type_id: NonZeroU32,
}
impl WorkchainFormatExtended {
pub fn is_valid(&self) -> bool {
self.min_addr_len >= Uint12::new(64)
&& self.min_addr_len <= self.max_addr_len
&& self.max_addr_len <= Uint12::new(1023)
&& self.addr_len_step <= Uint12::new(1023)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
#[tlb(tag = "#6b")]
pub struct BlockCreationRewards {
pub masterchain_block_fee: Tokens,
pub basechain_block_fee: Tokens,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
pub struct ElectionTimings {
pub validators_elected_for: u32,
pub elections_start_before: u32,
pub elections_end_before: u32,
pub stake_held_for: u32,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
pub struct ValidatorCountParams {
pub max_validators: u16,
pub max_main_validators: u16,
pub min_validators: u16,
}
#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
pub struct ValidatorStakeParams {
pub min_stake: Tokens,
pub max_stake: Tokens,
pub min_total_stake: Tokens,
pub max_stake_factor: u32,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
#[tlb(tag = "#cc")]
pub struct StoragePrices {
pub utime_since: u32,
pub bit_price_ps: u64,
pub cell_price_ps: u64,
pub mc_bit_price_ps: u64,
pub mc_cell_price_ps: u64,
}
#[derive(Default, Debug, Clone, Eq, PartialEq)]
pub struct GasLimitsPrices {
pub gas_price: u64,
pub gas_limit: u64,
pub special_gas_limit: u64,
pub gas_credit: u64,
pub block_gas_limit: u64,
pub freeze_due_limit: u64,
pub delete_due_limit: u64,
pub flat_gas_limit: u64,
pub flat_gas_price: u64,
}
impl GasLimitsPrices {
const TAG_BASE: u8 = 0xdd;
const TAG_EXT: u8 = 0xde;
const TAG_FLAT_PFX: u8 = 0xd1;
}
impl Store for GasLimitsPrices {
fn store_into(&self, builder: &mut CellBuilder, _: &mut dyn Finalizer) -> Result<(), Error> {
ok!(builder.store_u8(Self::TAG_FLAT_PFX));
ok!(builder.store_u64(self.flat_gas_limit));
ok!(builder.store_u64(self.flat_gas_price));
ok!(builder.store_u8(Self::TAG_EXT));
ok!(builder.store_u64(self.gas_price));
ok!(builder.store_u64(self.gas_limit));
ok!(builder.store_u64(self.special_gas_limit));
ok!(builder.store_u64(self.gas_credit));
ok!(builder.store_u64(self.block_gas_limit));
ok!(builder.store_u64(self.freeze_due_limit));
builder.store_u64(self.delete_due_limit)
}
}
impl<'a> Load<'a> for GasLimitsPrices {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
let mut result = Self::default();
loop {
match slice.load_u8() {
Ok(Self::TAG_FLAT_PFX) => {
result.flat_gas_limit = ok!(slice.load_u64());
result.flat_gas_price = ok!(slice.load_u64());
}
Ok(Self::TAG_EXT) => {
result.gas_price = ok!(slice.load_u64());
result.gas_limit = ok!(slice.load_u64());
result.special_gas_limit = ok!(slice.load_u64());
result.gas_credit = ok!(slice.load_u64());
result.block_gas_limit = ok!(slice.load_u64());
result.freeze_due_limit = ok!(slice.load_u64());
result.delete_due_limit = ok!(slice.load_u64());
return Ok(result);
}
Ok(Self::TAG_BASE) => {
result.gas_price = ok!(slice.load_u64());
result.gas_limit = ok!(slice.load_u64());
result.gas_credit = ok!(slice.load_u64());
result.block_gas_limit = ok!(slice.load_u64());
result.freeze_due_limit = ok!(slice.load_u64());
result.delete_due_limit = ok!(slice.load_u64());
return Ok(result);
}
Ok(_) => return Err(Error::InvalidTag),
Err(e) => return Err(e),
}
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
#[tlb(tag = "#c3", validate_with = "Self::is_valid")]
pub struct BlockParamLimits {
pub underload: u32,
pub soft_limit: u32,
pub hard_limit: u32,
}
impl BlockParamLimits {
pub fn is_valid(&self) -> bool {
self.underload <= self.soft_limit && self.soft_limit <= self.hard_limit
}
}
#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
#[tlb(tag = "#5d")]
pub struct BlockLimits {
pub bytes: BlockParamLimits,
pub gas: BlockParamLimits,
pub lt_delta: BlockParamLimits,
}
#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
#[tlb(tag = "#ea")]
pub struct MsgForwardPrices {
pub lump_price: u64,
pub bit_price: u64,
pub cell_price: u64,
pub ihr_price_factor: u32,
pub first_frac: u16,
pub next_frac: u16,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct CatchainConfig {
pub isolate_mc_validators: bool,
pub shuffle_mc_validators: bool,
pub mc_catchain_lifetime: u32,
pub shard_catchain_lifetime: u32,
pub shard_validators_lifetime: u32,
pub shard_validators_num: u32,
}
impl CatchainConfig {
const TAG_V1: u8 = 0xc1;
const TAG_V2: u8 = 0xc2;
}
impl Store for CatchainConfig {
fn store_into(&self, builder: &mut CellBuilder, _: &mut dyn Finalizer) -> Result<(), Error> {
let flags = ((self.isolate_mc_validators as u8) << 1) | (self.shuffle_mc_validators as u8);
ok!(builder.store_u8(Self::TAG_V2));
ok!(builder.store_u8(flags));
ok!(builder.store_u32(self.mc_catchain_lifetime));
ok!(builder.store_u32(self.shard_catchain_lifetime));
ok!(builder.store_u32(self.shard_validators_lifetime));
builder.store_u32(self.shard_validators_num)
}
}
impl<'a> Load<'a> for CatchainConfig {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
let flags = match slice.load_u8() {
Ok(Self::TAG_V1) => 0,
Ok(Self::TAG_V2) => ok!(slice.load_u8()),
Ok(_) => return Err(Error::InvalidTag),
Err(e) => return Err(e),
};
if flags >> 2 != 0 {
return Err(Error::InvalidData);
}
Ok(Self {
isolate_mc_validators: flags & 0b10 != 0,
shuffle_mc_validators: flags & 0b01 != 0,
mc_catchain_lifetime: ok!(slice.load_u32()),
shard_catchain_lifetime: ok!(slice.load_u32()),
shard_validators_lifetime: ok!(slice.load_u32()),
shard_validators_num: ok!(slice.load_u32()),
})
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ConsensusConfig {
pub new_catchain_ids: bool,
pub round_candidates: NonZeroU32,
pub next_candidate_delay_ms: u32,
pub consensus_timeout_ms: u32,
pub fast_attempts: u32,
pub attempt_duration: u32,
pub catchain_max_deps: u32,
pub max_block_bytes: u32,
pub max_collated_bytes: u32,
}
impl ConsensusConfig {
const TAG_V1: u8 = 0xd6;
const TAG_V2: u8 = 0xd7;
}
impl Store for ConsensusConfig {
fn store_into(&self, builder: &mut CellBuilder, _: &mut dyn Finalizer) -> Result<(), Error> {
let flags = self.new_catchain_ids as u8;
ok!(builder.store_u8(Self::TAG_V2));
ok!(builder.store_u8(flags));
ok!(builder.store_u8(self.round_candidates.get() as u8));
ok!(builder.store_u32(self.next_candidate_delay_ms));
ok!(builder.store_u32(self.consensus_timeout_ms));
ok!(builder.store_u32(self.fast_attempts));
ok!(builder.store_u32(self.attempt_duration));
ok!(builder.store_u32(self.catchain_max_deps));
ok!(builder.store_u32(self.max_block_bytes));
builder.store_u32(self.max_collated_bytes)
}
}
impl<'a> Load<'a> for ConsensusConfig {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
let (flags, round_candidates) = match slice.load_u8() {
Ok(Self::TAG_V1) => (0, ok!(NonZeroU32::load_from(slice))),
Ok(Self::TAG_V2) => {
let flags = ok!(slice.load_u8());
if flags >> 1 != 0 {
return Err(Error::InvalidData);
}
(0, ok!(NonZeroU8::load_from(slice)).into())
}
Ok(_) => return Err(Error::InvalidTag),
Err(e) => return Err(e),
};
Ok(Self {
new_catchain_ids: flags & 0b1 != 0,
round_candidates,
next_candidate_delay_ms: ok!(slice.load_u32()),
consensus_timeout_ms: ok!(slice.load_u32()),
fast_attempts: ok!(slice.load_u32()),
attempt_duration: ok!(slice.load_u32()),
catchain_max_deps: ok!(slice.load_u32()),
max_block_bytes: ok!(slice.load_u32()),
max_collated_bytes: ok!(slice.load_u32()),
})
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ValidatorSet {
pub utime_since: u32,
pub utime_until: u32,
pub main: NonZeroU16,
pub total_weight: u64,
pub list: Vec<ValidatorDescription>,
}
impl ValidatorSet {
const TAG_V1: u8 = 0x11;
const TAG_V2: u8 = 0x12;
}
impl Store for ValidatorSet {
fn store_into(
&self,
builder: &mut CellBuilder,
finalizer: &mut dyn Finalizer,
) -> Result<(), Error> {
let Ok(total) = u16::try_from(self.list.len()) else {
return Err(Error::InvalidData)
};
let mut validators = Dict::<u16, ValidatorDescription>::new();
for (i, item) in self.list.iter().enumerate() {
ok!(validators.set_ext(i as u16, item, finalizer));
}
ok!(builder.store_u8(Self::TAG_V2));
ok!(builder.store_u32(self.utime_since));
ok!(builder.store_u32(self.utime_until));
ok!(builder.store_u16(total));
ok!(builder.store_u16(self.main.get()));
ok!(builder.store_u64(self.total_weight));
validators.store_into(builder, finalizer)
}
}
impl<'a> Load<'a> for ValidatorSet {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
let with_total_weight = match slice.load_u8() {
Ok(Self::TAG_V1) => false,
Ok(Self::TAG_V2) => true,
Ok(_) => return Err(Error::InvalidTag),
Err(e) => return Err(e),
};
let utime_since = ok!(slice.load_u32());
let utime_until = ok!(slice.load_u32());
let total = ok!(slice.load_u16()) as usize;
let main = ok!(NonZeroU16::load_from(slice));
if main.get() as usize > total {
return Err(Error::InvalidData);
}
let finalizer = &mut Cell::default_finalizer();
let (mut total_weight, validators) = if with_total_weight {
let total_weight = ok!(slice.load_u64());
let dict = ok!(Dict::<u16, ValidatorDescription>::load_from(slice));
(total_weight, dict)
} else {
let dict = ok!(Dict::<u16, ValidatorDescription>::load_from_root_ext(
slice, finalizer
));
(0, dict)
};
let mut computed_total_weight = 0u64;
let mut list = Vec::with_capacity(std::cmp::min(total, 512));
for (i, entry) in validators.iter().enumerate().take(total) {
let descr = match entry {
Ok((idx, descr)) if idx as usize == i => descr,
Ok(_) => return Err(Error::InvalidData),
Err(e) => return Err(e),
};
computed_total_weight += descr.weight;
list.push(descr);
}
if list.is_empty() {
return Err(Error::InvalidData);
}
if with_total_weight {
if total_weight != computed_total_weight {
return Err(Error::InvalidData);
}
} else {
total_weight = computed_total_weight;
}
Ok(Self {
utime_since,
utime_until,
main,
total_weight,
list,
})
}
}
#[derive(CustomDebug, Clone, Eq, PartialEq)]
pub struct ValidatorDescription {
#[debug(with = "DisplayHash")]
pub public_key: CellHash, pub weight: u64,
#[debug(with = "DisplayOptionalHash")]
pub adnl_addr: Option<CellHash>,
pub mc_seqno_since: u32,
}
impl ValidatorDescription {
const TAG_BASIC: u8 = 0x53;
const TAG_WITH_ADNL: u8 = 0x73;
const TAG_WITH_MC_SEQNO: u8 = 0x93;
const PUBKEY_TAG: u32 = 0x8e81278a;
}
impl Store for ValidatorDescription {
fn store_into(&self, builder: &mut CellBuilder, _: &mut dyn Finalizer) -> Result<(), Error> {
let with_mc_seqno = self.mc_seqno_since != 0;
let tag = if with_mc_seqno {
Self::TAG_WITH_MC_SEQNO
} else if self.adnl_addr.is_some() {
Self::TAG_WITH_ADNL
} else {
Self::TAG_BASIC
};
ok!(builder.store_u8(tag));
ok!(builder.store_u32(Self::PUBKEY_TAG));
ok!(builder.store_u256(&self.public_key));
ok!(builder.store_u64(self.weight));
let mut adnl = self.adnl_addr.as_ref();
if with_mc_seqno {
adnl = Some(&[0; 32]);
}
if let Some(adnl) = adnl {
ok!(builder.store_u256(adnl));
}
if with_mc_seqno {
builder.store_u32(self.mc_seqno_since)
} else {
Ok(())
}
}
}
impl<'a> Load<'a> for ValidatorDescription {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
let (with_adnl, with_mc_seqno) = match slice.load_u8() {
Ok(Self::TAG_BASIC) => (false, false),
Ok(Self::TAG_WITH_ADNL) => (true, false),
Ok(Self::TAG_WITH_MC_SEQNO) => (true, true),
Ok(_) => return Err(Error::InvalidTag),
Err(e) => return Err(e),
};
Ok(Self {
public_key: {
match slice.load_u32() {
Ok(Self::PUBKEY_TAG) => ok!(slice.load_u256()),
Ok(_) => return Err(Error::InvalidTag),
Err(e) => return Err(e),
}
},
weight: ok!(slice.load_u64()),
adnl_addr: if with_adnl {
Some(ok!(slice.load_u256()))
} else {
None
},
mc_seqno_since: if with_mc_seqno {
ok!(slice.load_u32())
} else {
0
},
})
}
}