use crate::cell::*;
use crate::dict::{self, Dict};
use crate::error::*;
use crate::num::*;
use crate::models::account::AccountStatus;
use crate::models::currency::CurrencyCollection;
use crate::models::message::Message;
use crate::models::Lazy;
pub use self::phases::*;
mod phases;
#[cfg(test)]
mod tests;
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Transaction {
pub account: HashBytes,
pub lt: u64,
pub prev_trans_hash: HashBytes,
pub prev_trans_lt: u64,
pub now: u32,
pub out_msg_count: Uint15,
pub orig_status: AccountStatus,
pub end_status: AccountStatus,
#[cfg_attr(feature = "serde", serde(with = "serde_in_msg"))]
pub in_msg: Option<Cell>,
#[cfg_attr(feature = "serde", serde(with = "serde_out_msgs"))]
pub out_msgs: Dict<Uint15, Cell>,
pub total_fees: CurrencyCollection,
pub state_update: Lazy<HashUpdate>,
pub info: Lazy<TxInfo>,
}
impl Transaction {
pub fn load_in_msg(&self) -> Result<Option<Message<'_>>, Error> {
match &self.in_msg {
Some(in_msg) => match in_msg.parse::<Message>() {
Ok(message) => Ok(Some(message)),
Err(e) => Err(e),
},
None => Ok(None),
}
}
pub fn load_info(&self) -> Result<TxInfo, Error> {
self.info.load()
}
}
impl Transaction {
pub fn iter_out_msgs(&'_ self) -> TxOutMsgIter<'_> {
TxOutMsgIter {
inner: self.out_msgs.raw_values(),
}
}
}
#[cfg(feature = "serde")]
mod serde_in_msg {
use super::*;
pub fn serialize<S>(in_msg: &Option<Cell>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
match in_msg {
Some(in_msg) => {
let message = ok!(in_msg.parse::<Message>().map_err(serde::ser::Error::custom));
serializer.serialize_some(&message)
}
None => serializer.serialize_none(),
}
} else {
crate::boc::Boc::serialize(in_msg, serializer)
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Cell>, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{Deserialize, Error};
if deserializer.is_human_readable() {
match ok!(Option::<crate::models::OwnedMessage>::deserialize(
deserializer
)) {
Some(message) => CellBuilder::build_from(&message)
.map_err(Error::custom)
.map(Some),
None => Ok(None),
}
} else {
crate::boc::Boc::deserialize(deserializer)
}
}
}
#[cfg(feature = "serde")]
mod serde_out_msgs {
use super::*;
pub fn serialize<S>(out_msgs: &Dict<Uint15, Cell>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::{Error, SerializeMap};
if serializer.is_human_readable() {
let mut map = ok!(serializer.serialize_map(None));
for entry in out_msgs.iter() {
match entry {
Ok((key, value)) => {
let message = ok!(value.parse::<Message>().map_err(Error::custom));
ok!(map.serialize_entry(&key, &message));
}
Err(e) => return Err(Error::custom(e)),
}
}
map.end()
} else {
crate::boc::BocRepr::serialize(out_msgs, serializer)
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Dict<Uint15, Cell>, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{Deserialize, Error};
if deserializer.is_human_readable() {
let messages = ok!(
ahash::HashMap::<Uint15, crate::models::OwnedMessage>::deserialize(deserializer)
);
let cx = &mut Cell::empty_context();
let mut dict = Dict::new();
for (key, value) in &messages {
let cell = ok!(CellBuilder::build_from(value).map_err(Error::custom));
ok!(dict.set_ext(*key, cell, cx).map_err(Error::custom));
}
Ok(dict)
} else {
crate::boc::BocRepr::deserialize(deserializer)
}
}
}
#[derive(Clone)]
pub struct TxOutMsgIter<'a> {
inner: dict::RawValues<'a>,
}
impl<'a> Iterator for TxOutMsgIter<'a> {
type Item = Result<Message<'a>, Error>;
fn next(&mut self) -> Option<Self::Item> {
match self.inner.next()? {
Ok(mut value) => {
let e = match value.load_reference_as_slice() {
Ok(mut value) => match Message::<'a>::load_from(&mut value) {
Ok(message) => return Some(Ok(message)),
Err(e) => e,
},
Err(e) => e,
};
Some(Err(self.inner.finish(e)))
}
Err(e) => Some(Err(e)),
}
}
}
impl Transaction {
const TAG: u8 = 0b0111;
}
impl Store for Transaction {
fn store_into(
&self,
builder: &mut CellBuilder,
context: &mut dyn CellContext,
) -> Result<(), Error> {
let messages = {
let mut builder = CellBuilder::new();
ok!(self.in_msg.store_into(&mut builder, context));
ok!(self.out_msgs.store_into(&mut builder, context));
ok!(builder.build_ext(context))
};
ok!(builder.store_small_uint(Self::TAG, 4));
ok!(builder.store_u256(&self.account));
ok!(builder.store_u64(self.lt));
ok!(builder.store_u256(&self.prev_trans_hash));
ok!(builder.store_u64(self.prev_trans_lt));
ok!(builder.store_u32(self.now));
ok!(self.out_msg_count.store_into(builder, context));
ok!(self.orig_status.store_into(builder, context));
ok!(self.end_status.store_into(builder, context));
ok!(builder.store_reference(messages));
ok!(self.total_fees.store_into(builder, context));
ok!(self.state_update.store_into(builder, context));
self.info.store_into(builder, context)
}
}
impl<'a> Load<'a> for Transaction {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
match slice.load_small_uint(4) {
Ok(Self::TAG) => {}
Ok(_) => return Err(Error::InvalidTag),
Err(e) => return Err(e),
}
let (in_msg, out_msgs) = {
let slice = &mut ok!(slice.load_reference_as_slice());
let in_msg = ok!(Option::<Cell>::load_from(slice));
let out_msgs = ok!(Dict::load_from(slice));
(in_msg, out_msgs)
};
Ok(Self {
account: ok!(slice.load_u256()),
lt: ok!(slice.load_u64()),
prev_trans_hash: ok!(slice.load_u256()),
prev_trans_lt: ok!(slice.load_u64()),
now: ok!(slice.load_u32()),
out_msg_count: ok!(Uint15::load_from(slice)),
orig_status: ok!(AccountStatus::load_from(slice)),
end_status: ok!(AccountStatus::load_from(slice)),
in_msg,
out_msgs,
total_fees: ok!(CurrencyCollection::load_from(slice)),
state_update: ok!(Lazy::<HashUpdate>::load_from(slice)),
info: ok!(Lazy::<TxInfo>::load_from(slice)),
})
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "ty"))]
pub enum TxInfo {
Ordinary(OrdinaryTxInfo),
TickTock(TickTockTxInfo),
}
impl Store for TxInfo {
fn store_into(
&self,
builder: &mut CellBuilder,
context: &mut dyn CellContext,
) -> Result<(), Error> {
match self {
Self::Ordinary(info) => {
ok!(builder.store_small_uint(0b0000, 4));
info.store_into(builder, context)
}
Self::TickTock(info) => {
ok!(builder.store_small_uint(0b001, 3));
info.store_into(builder, context)
}
}
}
}
impl<'a> Load<'a> for TxInfo {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
let tag_part = ok!(slice.load_small_uint(3));
Ok(if tag_part == 0b001 {
match TickTockTxInfo::load_from(slice) {
Ok(info) => Self::TickTock(info),
Err(e) => return Err(e),
}
} else if tag_part == 0b000 && !ok!(slice.load_bit()) {
match OrdinaryTxInfo::load_from(slice) {
Ok(info) => Self::Ordinary(info),
Err(e) => return Err(e),
}
} else {
return Err(Error::InvalidTag);
})
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OrdinaryTxInfo {
pub credit_first: bool,
pub storage_phase: Option<StoragePhase>,
pub credit_phase: Option<CreditPhase>,
pub compute_phase: ComputePhase,
pub action_phase: Option<ActionPhase>,
pub aborted: bool,
pub bounce_phase: Option<BouncePhase>,
pub destroyed: bool,
}
impl Store for OrdinaryTxInfo {
fn store_into(
&self,
builder: &mut CellBuilder,
context: &mut dyn CellContext,
) -> Result<(), Error> {
let action_phase = match &self.action_phase {
Some(action_phase) => {
let mut builder = CellBuilder::new();
ok!(action_phase.store_into(&mut builder, context));
Some(ok!(builder.build_ext(context)))
}
None => None,
};
ok!(builder.store_bit(self.credit_first));
ok!(self.storage_phase.store_into(builder, context));
ok!(self.credit_phase.store_into(builder, context));
ok!(self.compute_phase.store_into(builder, context));
ok!(action_phase.store_into(builder, context));
ok!(builder.store_bit(self.aborted));
ok!(self.bounce_phase.store_into(builder, context));
builder.store_bit(self.destroyed)
}
}
impl<'a> Load<'a> for OrdinaryTxInfo {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
Ok(Self {
credit_first: ok!(slice.load_bit()),
storage_phase: ok!(Option::<StoragePhase>::load_from(slice)),
credit_phase: ok!(Option::<CreditPhase>::load_from(slice)),
compute_phase: ok!(ComputePhase::load_from(slice)),
action_phase: match ok!(Option::<Cell>::load_from(slice)) {
Some(cell) => Some(ok!(cell.as_ref().parse::<ActionPhase>())),
None => None,
},
aborted: ok!(slice.load_bit()),
bounce_phase: ok!(Option::<BouncePhase>::load_from(slice)),
destroyed: ok!(slice.load_bit()),
})
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TickTockTxInfo {
pub kind: TickTock,
pub storage_phase: StoragePhase,
pub compute_phase: ComputePhase,
pub action_phase: Option<ActionPhase>,
pub aborted: bool,
pub destroyed: bool,
}
impl Store for TickTockTxInfo {
fn store_into(
&self,
builder: &mut CellBuilder,
context: &mut dyn CellContext,
) -> Result<(), Error> {
let action_phase = match &self.action_phase {
Some(action_phase) => {
let mut builder = CellBuilder::new();
ok!(action_phase.store_into(&mut builder, context));
Some(ok!(builder.build_ext(context)))
}
None => None,
};
let flags = ((self.aborted as u8) << 1) | (self.destroyed as u8);
ok!(self.kind.store_into(builder, context));
ok!(self.storage_phase.store_into(builder, context));
ok!(self.compute_phase.store_into(builder, context));
ok!(action_phase.store_into(builder, context));
builder.store_small_uint(flags, 2)
}
}
impl<'a> Load<'a> for TickTockTxInfo {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
let kind = ok!(TickTock::load_from(slice));
let storage_phase = ok!(StoragePhase::load_from(slice));
let compute_phase = ok!(ComputePhase::load_from(slice));
let action_phase = match ok!(Option::<Cell>::load_from(slice)) {
Some(cell) => Some(ok!(cell.as_ref().parse::<ActionPhase>())),
None => None,
};
let flags = ok!(slice.load_small_uint(2));
Ok(Self {
kind,
storage_phase,
compute_phase,
action_phase,
aborted: flags & 0b10 != 0,
destroyed: flags & 0b01 != 0,
})
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum TickTock {
Tick = 0,
Tock = 1,
}
impl Store for TickTock {
#[inline]
fn store_into(&self, builder: &mut CellBuilder, _: &mut dyn CellContext) -> Result<(), Error> {
builder.store_bit(*self == Self::Tock)
}
}
impl<'a> Load<'a> for TickTock {
#[inline]
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
match slice.load_bit() {
Ok(false) => Ok(Self::Tick),
Ok(true) => Ok(Self::Tock),
Err(e) => Err(e),
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[tlb(tag = "#72")]
pub struct HashUpdate {
pub old: HashBytes,
pub new: HashBytes,
}