Skip to content

Commit

Permalink
Add models for output actions
Browse files Browse the repository at this point in the history
  • Loading branch information
Rexagon committed Aug 7, 2023
1 parent a9c2273 commit 85c49b4
Show file tree
Hide file tree
Showing 6 changed files with 340 additions and 2 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ members = ["proc"]
[dependencies]
ahash = "0.8"
base64 = { version = "0.21.0", optional = true }
bitflags = "2.3"
crc32c = "0.6"
hex = "0.4"
once_cell = "1.16"
Expand All @@ -41,8 +42,8 @@ anyhow = "1.0"
base64 = "0.21"
criterion = "0.5"
libc = "0.2"
rand_xorshift = "0.3"
rand = "0.8"
rand_xorshift = "0.3"
serde = { version = "1", features = ["derive"] }
serde_json = "1"

Expand Down
64 changes: 64 additions & 0 deletions src/models/message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,70 @@ mod address;
#[cfg(test)]
mod tests;

/// Lazy-loaded message model.
#[derive(Debug, Clone, Eq)]
#[repr(transparent)]
pub struct LazyMessage(Cell);

impl LazyMessage {
/// Serializes the provided data and returns the typed wrapper around it.
pub fn new(data: &Message) -> Result<Self, Error> {
let mut builder = CellBuilder::new();
let finalizer = &mut Cell::default_finalizer();
ok!(data.store_into(&mut builder, finalizer));
Ok(Self::from_raw(ok!(builder.build_ext(finalizer))))
}

/// Wraps the cell in a typed wrapper.
#[inline]
pub fn from_raw(cell: Cell) -> Self {
Self(cell)
}

/// Converts into the underlying cell.
#[inline]
pub fn into_inner(self) -> Cell {
self.0
}

/// Returns the underlying cell.
#[inline]
pub fn inner(&self) -> &Cell {
&self.0
}

/// Loads inner data from cell.
pub fn load(&self) -> Result<Message<'_>, Error> {
self.0.as_ref().parse::<Message>()
}
}

impl PartialEq for LazyMessage {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.0.as_ref() == other.0.as_ref()
}
}

impl PartialEq<Cell> for LazyMessage {
#[inline]
fn eq(&self, other: &Cell) -> bool {
self.0.as_ref() == other.as_ref()
}
}

impl Store for LazyMessage {
fn store_into(&self, builder: &mut CellBuilder, _: &mut dyn Finalizer) -> Result<(), Error> {
builder.store_reference(self.0.clone())
}
}

impl<'a> Load<'a> for LazyMessage {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
slice.load_reference_cloned().map(Self)
}
}

/// Blockchain message.
#[derive(Debug, Clone)]
pub struct Message<'a> {
Expand Down
2 changes: 2 additions & 0 deletions src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub use global_version::*;
pub use message::*;
pub use shard::*;
pub use transaction::*;
pub use vm::*;

pub mod account;
pub mod block;
Expand All @@ -23,6 +24,7 @@ pub mod global_version;
pub mod message;
pub mod shard;
pub mod transaction;
pub mod vm;

#[cfg(feature = "sync")]
#[doc(hidden)]
Expand Down
2 changes: 1 addition & 1 deletion src/models/transaction/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Message models.
//! Transaction models.

use crate::cell::*;
use crate::dict::{self, Dict};
Expand Down
5 changes: 5 additions & 0 deletions src/models/vm/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//! VM related models.

pub use self::out_actions::*;

mod out_actions;
266 changes: 266 additions & 0 deletions src/models/vm/out_actions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
use bitflags::bitflags;

use crate::cell::*;
use crate::error::Error;
use crate::models::currency::CurrencyCollection;
use crate::models::message::LazyMessage;

/// Out actions list reverse iterator.
pub struct OutActionsRevIter<'a> {
slice: CellSlice<'a>,
}

impl<'a> OutActionsRevIter<'a> {
/// Creates a new output actions list iterator from the list rev head.
pub fn new(slice: CellSlice<'a>) -> Self {
Self { slice }
}
}

impl<'a> Iterator for OutActionsRevIter<'a> {
type Item = Result<OutAction, Error>;

fn next(&mut self) -> Option<Self::Item> {
let prev_cell = match self.slice.load_reference() {
Ok(cell) => cell,
Err(_) => {
return if self.slice.is_data_empty() && self.slice.is_refs_empty() {
None
} else {
Some(Err(Error::InvalidData))
}
}
};

let action = match OutAction::load_from(&mut self.slice) {
Ok(action) => action,
Err(e) => return Some(Err(e)),
};
self.slice = match prev_cell.as_slice() {
Ok(slice) => slice,
Err(e) => return Some(Err(e)),
};
Some(Ok(action))
}
}

bitflags! {
/// Mode flags for `SendMsg` output action.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct SendMsgFlags: u8 {
/// The sender will pay transfer fees separately.
const PAY_FEE_SEPARATELY = 1;
/// Any errors arising while processing this message during
/// the action phase should be ignored.
const IGNORE_ERROR = 2;
/// The current account must be destroyed if its resulting balance is zero.
const DELETE_IF_EMPTY = 32;
/// Message will carry all the remaining value of the inbound message
/// in addition to the value initially indicated in the new message
/// (if bit 0 is not set, the gas fees are deducted from this amount).
const WITH_REMAINING_BALANCE = 64;
/// Message will carry all the remaining balance of the current smart contract
/// (instead of the value originally indicated in the message).
const ALL_BALANCE = 128;
}
}

impl Store for SendMsgFlags {
fn store_into(&self, builder: &mut CellBuilder, _: &mut dyn Finalizer) -> Result<(), Error> {
builder.store_u8(self.bits())
}
}

impl<'a> Load<'a> for SendMsgFlags {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
Ok(Self::from_bits_retain(ok!(slice.load_u8())))
}
}

bitflags! {
/// Mode flags for `ReserveCurrency` output action.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ReserveCurrencyFlags: u8 {
/// Output action will reserve all but x nanograms.
const ALL_BUT = 1;
/// The external action does not fail if the specified amount cannot bereserved.
/// Instead, all remaining balance is reserve.
const IGNORE_ERROR = 2;
/// x is increased by the original balance of the current account (before the
/// compute phase), including all extra currencies, before performing any
/// other checks and actions.
const WITH_ORIGINAL_BALANCE = 4;
/// `x = −x` before performing any further action.
const REVERSE = 8;
}
}

impl Store for ReserveCurrencyFlags {
fn store_into(&self, builder: &mut CellBuilder, _: &mut dyn Finalizer) -> Result<(), Error> {
builder.store_u8(self.bits())
}
}

impl<'a> Load<'a> for ReserveCurrencyFlags {
#[inline]
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
Ok(Self::from_bits_retain(ok!(slice.load_u8())))
}
}

bitflags! {
/// Mode flags for `ChangeLibrary` output action.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ChangeLibraryFlags: u8 {
/// Output action will reserve all but x nanograms.
const REMOVE = 1;
/// Adds library as private.
const PRIVATE = 2 + 1;
/// Adds library as public.
const PUBLIC = 4 + 1;
}
}

impl Store for ChangeLibraryFlags {
fn store_into(&self, builder: &mut CellBuilder, _: &mut dyn Finalizer) -> Result<(), Error> {
builder.store_u8(self.bits())
}
}

impl<'a> Load<'a> for ChangeLibraryFlags {
#[inline]
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
Ok(Self::from_bits_retain(ok!(slice.load_u8())))
}
}

/// Output action.
pub enum OutAction {
/// Sends a raw message contained in cell.
SendMsg {
/// Behavior flags.
mode: SendMsgFlags,
/// A cell with a message.
out_msg: LazyMessage,
},
/// Creates an output action that would change this smart contract code
/// to that given by cell.
SetCode {
/// A cell with new code.
new_code: Cell,
},
/// Creates an output action which would reserve exactly some balance.
ReserveCurrency {
/// Behavior flags.
mode: ReserveCurrencyFlags,
/// Reserved value.
value: CurrencyCollection,
},
/// Creates an output action that would modify the collection of this
/// smart contract libraries by adding or removing library with code given in cell.
ChangeLibrary {
/// Behavior flags.
mode: ChangeLibraryFlags,
/// Cell library hash.
hash: Option<HashBytes>,
/// Cell library code.
code: Option<Cell>,
},
/// Copyleft action.
CopyLeft {
/// License number.
license: u8,
/// Owner address.
address: HashBytes,
},
}

impl OutAction {
const TAG_SEND_MSG: u32 = 0x0ec3c86d;
const TAG_SET_CODE: u32 = 0xad4de08e;
const TAG_RESERVE: u32 = 0x36e6b809;
const TAG_CHANGE_LIB: u32 = 0x26fa1dd4;
const TAG_COPYLEFT: u32 = 0x24486f7a;
}

impl Store for OutAction {
fn store_into(
&self,
builder: &mut CellBuilder,
finalizer: &mut dyn Finalizer,
) -> Result<(), Error> {
match self {
Self::SendMsg { mode, out_msg } => {
ok!(builder.store_u32(Self::TAG_SEND_MSG));
ok!(builder.store_u8(mode.bits()));
builder.store_reference(out_msg.inner().clone())
}
Self::SetCode { new_code } => {
ok!(builder.store_u32(Self::TAG_SET_CODE));
builder.store_reference(new_code.clone())
}
Self::ReserveCurrency { mode, value } => {
ok!(builder.store_u32(Self::TAG_RESERVE));
ok!(builder.store_u8(mode.bits()));
value.store_into(builder, finalizer)
}
Self::ChangeLibrary { mode, code, hash } => {
ok!(builder.store_u32(Self::TAG_CHANGE_LIB));
ok!(builder.store_u8(mode.bits()));
if let Some(hash) = hash {
ok!(builder.store_u256(hash));
}
if let Some(code) = code {
ok!(builder.store_reference(code.clone()))
}
Ok(())
}
Self::CopyLeft { license, address } => {
ok!(builder.store_u32(Self::TAG_COPYLEFT));
ok!(builder.store_u8(*license));
builder.store_u256(address)
}
}
}
}

impl<'a> Load<'a> for OutAction {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
let tag = ok!(slice.load_u32());
Ok(match tag {
Self::TAG_SEND_MSG => Self::SendMsg {
mode: ok!(SendMsgFlags::load_from(slice)),
out_msg: ok!(LazyMessage::load_from(slice)),
},
Self::TAG_SET_CODE => Self::SetCode {
new_code: ok!(slice.load_reference_cloned()),
},
Self::TAG_RESERVE => Self::ReserveCurrency {
mode: ok!(ReserveCurrencyFlags::load_from(slice)),
value: ok!(CurrencyCollection::load_from(slice)),
},
Self::TAG_CHANGE_LIB => {
let mode = ok!(ChangeLibraryFlags::load_from(slice));
let load_hash = mode.is_empty();
Self::ChangeLibrary {
mode,
hash: if load_hash {
Some(ok!(slice.load_u256()))
} else {
None
},
code: if !load_hash {
Some(ok!(slice.load_reference_cloned()))
} else {
None
},
}
}
Self::TAG_COPYLEFT => Self::CopyLeft {
license: ok!(slice.load_u8()),
address: ok!(slice.load_u256()),
},
_ => return Err(Error::InvalidTag),
})
}
}

0 comments on commit 85c49b4

Please sign in to comment.