use crate::cell::*;
use crate::dict::*;
use crate::error::*;
use crate::models::currency::CurrencyCollection;
use crate::models::message::IntAddr;
use crate::num::*;
#[derive(Debug, Default, Clone, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct StorageUsed {
pub cells: VarUint56,
pub bits: VarUint56,
}
impl StorageUsed {
pub const ZERO: Self = Self {
cells: VarUint56::ZERO,
bits: VarUint56::ZERO,
};
pub fn compute(account: &Account, cell_limit: usize) -> Result<Self, Error> {
let cell = {
let cx = Cell::empty_context();
let mut storage = CellBuilder::new();
storage.store_u64(account.last_trans_lt)?;
account.balance.store_into(&mut storage, cx)?;
account.state.store_into(&mut storage, cx)?;
storage.build_ext(cx)?
};
let Some(res) = cell.compute_unique_stats(cell_limit) else {
return Err(Error::Cancelled);
};
let res = Self {
cells: VarUint56::new(res.cell_count),
bits: VarUint56::new(res.bit_count),
};
if res.cells.is_valid() || !res.bits.is_valid() {
return Err(Error::IntOverflow);
}
Ok(res)
}
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct StorageUsedShort {
pub cells: VarUint56,
pub bits: VarUint56,
}
impl StorageUsedShort {
pub const ZERO: Self = Self {
cells: VarUint56::ZERO,
bits: VarUint56::ZERO,
};
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum StorageExtra {
#[default]
None,
DictHash(HashBytes),
}
impl Store for StorageExtra {
fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
match self {
StorageExtra::None => builder.store_zeros(3)?,
StorageExtra::DictHash(dict_hash) => {
builder.store_small_uint(1, 3)?;
builder.store_u256(dict_hash)?;
}
}
Ok(())
}
}
impl<'a> Load<'a> for StorageExtra {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
match slice.load_small_uint(3)? {
0b000 => Ok(Self::None),
0b001 => Ok(Self::DictHash(slice.load_u256()?)),
_ => Err(Error::InvalidTag),
}
}
}
#[derive(Debug, Default, Clone, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct StorageInfo {
pub used: StorageUsed,
pub storage_extra: StorageExtra,
pub last_paid: u32,
pub due_payment: Option<Tokens>,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum AccountStatus {
Uninit = 0b00,
Frozen = 0b01,
Active = 0b10,
NotExists = 0b11,
}
impl AccountStatus {
pub const BITS: u16 = 2;
}
impl Store for AccountStatus {
#[inline]
fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
builder.store_small_uint(*self as u8, 2)
}
}
impl<'a> Load<'a> for AccountStatus {
#[inline]
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
match slice.load_small_uint(2) {
Ok(ty) => Ok(match ty {
0b00 => Self::Uninit,
0b01 => Self::Frozen,
0b10 => Self::Active,
0b11 => Self::NotExists,
_ => {
debug_assert!(false, "unexpected small uint");
unsafe { std::hint::unreachable_unchecked() }
}
}),
Err(e) => Err(e),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ShardAccount {
pub account: Lazy<OptionalAccount>,
pub last_trans_hash: HashBytes,
pub last_trans_lt: u64,
}
impl ShardAccount {
pub fn load_account(&self) -> Result<Option<Account>, Error> {
let OptionalAccount(account) = ok!(self.account.load());
Ok(account)
}
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for ShardAccount {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let account = u.arbitrary::<OptionalAccount>()?;
Ok(Self {
account: Lazy::new(&account).unwrap(),
last_trans_hash: u.arbitrary()?,
last_trans_lt: u.arbitrary()?,
})
}
#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
Self::try_size_hint(depth).unwrap_or_default()
}
fn try_size_hint(
depth: usize,
) -> arbitrary::Result<(usize, Option<usize>), arbitrary::MaxRecursionReached> {
Ok(arbitrary::size_hint::and_all(&[
<OptionalAccount as arbitrary::Arbitrary>::try_size_hint(depth)?,
<HashBytes as arbitrary::Arbitrary>::try_size_hint(depth)?,
<u64 as arbitrary::Arbitrary>::try_size_hint(depth)?,
]))
}
}
#[derive(Default, Debug, Clone, Eq, PartialEq, Store, Load)]
#[repr(transparent)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OptionalAccount(pub Option<Account>);
impl OptionalAccount {
pub const EMPTY: Self = Self(None);
pub fn status(&self) -> AccountStatus {
match &self.0 {
None => AccountStatus::NotExists,
Some(account) => account.state.status(),
}
}
pub fn last_trans_lt(&self) -> u64 {
match &self.0 {
None => 0,
Some(account) => account.last_trans_lt,
}
}
#[cfg(feature = "sync")]
pub fn balance(&self) -> &CurrencyCollection {
static DEFAULT_VALANCE: CurrencyCollection = CurrencyCollection::ZERO;
match &self.0 {
None => &DEFAULT_VALANCE,
Some(account) => &account.balance,
}
}
pub fn state(&self) -> Option<&AccountState> {
Some(&self.0.as_ref()?.state)
}
}
impl AsRef<Option<Account>> for OptionalAccount {
#[inline]
fn as_ref(&self) -> &Option<Account> {
&self.0
}
}
impl AsMut<Option<Account>> for OptionalAccount {
#[inline]
fn as_mut(&mut self) -> &mut Option<Account> {
&mut self.0
}
}
impl From<Account> for OptionalAccount {
#[inline]
fn from(value: Account) -> Self {
Self(Some(value))
}
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for OptionalAccount {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
u.ratio(9u8, 10u8)?
.then(|| u.arbitrary())
.transpose()
.map(Self)
}
#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
Self::try_size_hint(depth).unwrap_or_default()
}
#[inline]
fn try_size_hint(
depth: usize,
) -> arbitrary::Result<(usize, Option<usize>), arbitrary::MaxRecursionReached> {
<Option<Account>>::try_size_hint(depth)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Account {
pub address: IntAddr,
pub storage_stat: StorageInfo,
pub last_trans_lt: u64,
pub balance: CurrencyCollection,
pub state: AccountState,
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "status"))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum AccountState {
Uninit,
Active(StateInit),
Frozen(HashBytes),
}
impl AccountState {
pub fn status(&self) -> AccountStatus {
match self {
Self::Uninit => AccountStatus::Uninit,
Self::Active(_) => AccountStatus::Active,
Self::Frozen(_) => AccountStatus::Frozen,
}
}
}
impl Store for AccountState {
fn store_into(
&self,
builder: &mut CellBuilder,
context: &dyn CellContext,
) -> Result<(), Error> {
match self {
Self::Uninit => builder.store_small_uint(0b00, 2),
Self::Active(state) => {
ok!(builder.store_bit_one());
state.store_into(builder, context)
}
Self::Frozen(hash) => {
ok!(builder.store_small_uint(0b01, 2));
builder.store_u256(hash)
}
}
}
}
impl<'a> Load<'a> for AccountState {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
Ok(if ok!(slice.load_bit()) {
match StateInit::load_from(slice) {
Ok(state) => Self::Active(state),
Err(e) => return Err(e),
}
} else if ok!(slice.load_bit()) {
match slice.load_u256() {
Ok(state) => Self::Frozen(state),
Err(e) => return Err(e),
}
} else {
Self::Uninit
})
}
}
#[derive(Debug, Clone, Default, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StateInit {
pub split_depth: Option<SplitDepth>,
pub special: Option<SpecialFlags>,
#[cfg_attr(feature = "serde", serde(with = "crate::boc::Boc"))]
pub code: Option<Cell>,
#[cfg_attr(feature = "serde", serde(with = "crate::boc::Boc"))]
pub data: Option<Cell>,
pub libraries: Dict<HashBytes, SimpleLib>,
}
impl StateInit {
pub const fn exact_size_const(&self) -> Size {
Size {
bits: self.bit_len(),
refs: self.reference_count(),
}
}
pub const fn bit_len(&self) -> u16 {
(1 + self.split_depth.is_some() as u16 * SplitDepth::BITS)
+ (1 + self.special.is_some() as u16 * SpecialFlags::BITS)
+ 3
}
pub const fn reference_count(&self) -> u8 {
self.code.is_some() as u8 + self.data.is_some() as u8 + !self.libraries.is_empty() as u8
}
}
impl ExactSize for StateInit {
#[inline]
fn exact_size(&self) -> Size {
self.exact_size_const()
}
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for StateInit {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let split_depth = u.ratio(1u8, 50u8)?.then(|| u.arbitrary()).transpose()?;
let special = u.ratio(1u8, 50u8)?.then(|| u.arbitrary()).transpose()?;
let code = u.ratio(9u8, 10u8)?.then(|| u.arbitrary()).transpose()?;
let data = u.ratio(9u8, 10u8)?.then(|| u.arbitrary()).transpose()?;
let mut libraries = Dict::new();
match u.arbitrary::<u8>()? {
0..=128 => {}
n => {
for _ in 128..n {
let lib = u.arbitrary::<SimpleLib>()?;
if lib.root.level() != 0 || lib.root.has_max_depth() {
return Err(arbitrary::Error::IncorrectFormat);
}
libraries.set(u.arbitrary::<HashBytes>()?, lib).unwrap();
}
}
}
Ok(Self {
split_depth,
special,
code,
data,
libraries,
})
}
#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
Self::try_size_hint(depth).unwrap_or_default()
}
fn try_size_hint(
depth: usize,
) -> arbitrary::Result<(usize, Option<usize>), arbitrary::MaxRecursionReached> {
Ok(arbitrary::size_hint::and_all(&[
<Option<SplitDepth>>::try_size_hint(depth)?,
<Option<SpecialFlags>>::try_size_hint(depth)?,
<Option<Cell>>::try_size_hint(depth)?,
<Option<Cell>>::try_size_hint(depth)?,
(1, None),
]))
}
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct SpecialFlags {
pub tick: bool,
pub tock: bool,
}
impl SpecialFlags {
pub const BITS: u16 = 2;
}
impl Store for SpecialFlags {
fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
builder.store_small_uint(((self.tick as u8) << 1) | self.tock as u8, 2)
}
}
impl<'a> Load<'a> for SpecialFlags {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
match slice.load_small_uint(2) {
Ok(data) => Ok(Self {
tick: data & 0b10 != 0,
tock: data & 0b01 != 0,
}),
Err(e) => Err(e),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct SimpleLib {
pub public: bool,
#[cfg_attr(feature = "serde", serde(with = "crate::boc::Boc"))]
pub root: Cell,
}