Skip to content

Commit

Permalink
feat(abi): add support for tvm getters
Browse files Browse the repository at this point in the history
  • Loading branch information
Rexagon committed Aug 9, 2024
1 parent 511d311 commit 63e4dfc
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 30 deletions.
101 changes: 97 additions & 4 deletions nekoton-abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ use ton_block::{
};
use ton_executor::{BlockchainConfig, OrdinaryTransactionExecutor, TransactionExecutor};
use ton_types::{SliceData, UInt256};
use ton_vm::executor::BehaviorModifiers;

#[cfg(feature = "derive")]
pub use {
Expand All @@ -86,8 +87,8 @@ pub use self::models::*;
pub use self::token_packer::*;
pub use self::token_unpacker::*;
pub use self::tokens_json::*;
pub use self::tvm::BriefBlockchainConfig;
pub use transaction_parser::TransactionParser;
pub use self::transaction_parser::TransactionParser;
pub use self::tvm::{BriefBlockchainConfig, StackItem, VmGetterOutput};

mod abi_helpers;
mod code_salt;
Expand All @@ -100,7 +101,7 @@ mod token_packer;
mod token_unpacker;
mod tokens_json;
pub mod transaction_parser;
mod tvm;
pub mod tvm;

pub fn read_function_id(data: &SliceData) -> Result<u32> {
let mut value: u32 = 0;
Expand Down Expand Up @@ -582,6 +583,76 @@ impl<'a> ExecutionContext<'a> {
) -> Result<ExecutionOutput> {
function.run_local_responsible(self.clock, self.account_stuff.clone(), input)
}

pub fn run_getter<M>(
&self,
method_id: &M,
args: &[StackItem],
) -> Result<VmGetterOutput, tvm::ExecutionError>
where
M: AsGetterMethodId + ?Sized,
{
self.run_getter_ext(
method_id,
args,
&BriefBlockchainConfig::default(),
&Default::default(),
)
}

pub fn run_getter_ext<M>(
&self,
method_id: &M,
args: &[StackItem],
config: &BriefBlockchainConfig,
modifier: &BehaviorModifiers,
) -> Result<VmGetterOutput, tvm::ExecutionError>
where
M: AsGetterMethodId + ?Sized,
{
let BlockStats {
gen_utime, gen_lt, ..
} = get_block_stats(self.clock, None, self.account_stuff.storage.last_trans_lt);

tvm::call_getter(
gen_utime,
gen_lt,
self.account_stuff,
method_id.as_getter_method_id(),
args,
config,
modifier,
)
}
}

pub trait AsGetterMethodId {
fn as_getter_method_id(&self) -> u32;
}

impl<T: AsGetterMethodId + ?Sized> AsGetterMethodId for &T {
fn as_getter_method_id(&self) -> u32 {
T::as_getter_method_id(*self)
}
}

impl<T: AsGetterMethodId + ?Sized> AsGetterMethodId for &mut T {
fn as_getter_method_id(&self) -> u32 {
T::as_getter_method_id(*self)
}
}

impl AsGetterMethodId for u32 {
fn as_getter_method_id(&self) -> u32 {
*self
}
}

impl AsGetterMethodId for str {
fn as_getter_method_id(&self) -> u32 {
let crc = crc_16(self.as_bytes());
crc as u32 | 0x10000
}
}

pub trait FunctionExt {
Expand Down Expand Up @@ -739,7 +810,14 @@ impl<'a> FunctionAbi<'a> {
let tvm::ActionPhaseOutput {
messages,
exit_code: result_code,
} = tvm::call_msg(gen_utime, gen_lt, account_stuff, &msg, config)?;
} = tvm::call_msg(
gen_utime,
gen_lt,
account_stuff,
&msg,
config,
&Default::default(),
)?;

let tokens = if let Some(answer_id) = answer_id {
messages.map(|messages| {
Expand Down Expand Up @@ -1172,6 +1250,21 @@ mod tests {
assert_eq!(decoded_comment, comment);
}

#[test]
fn execute_getter() {
let cell = ton_types::deserialize_tree_of_cells(&mut base64::decode("te6ccgEBAwEA1wACcIAStWnZig414CoO3Ix5SSSgxF+4p0D15b9rxM7Q6hTG2AQNApWGauQIQAABez2Soaga3FkkG3ymAgEAUAAACtJLqS2Krp5U49k0sATqkF/7CPTREi6T4gLBqodDaVGp3w9YHEEA3v8AIN0gggFMl7ohggEznLqxn3Gw7UTQ0x/THzHXC//jBOCk8mCDCNcYINMf0x/TH/gjE7vyY+1E0NMf0x/T/9FRMrryoVFEuvKiBPkBVBBV+RDyo/gAkyDXSpbTB9QC+wDo0QGkyMsfyx/L/8ntVA==").unwrap().as_slice()).unwrap();
let state = nekoton_utils::deserialize_account_stuff(cell).unwrap();

let res = ExecutionContext {
clock: &SimpleClock,
account_stuff: &state,
}
.run_getter("seqno", &[])
.unwrap();

println!("{res:?}");
}

#[test]
fn test_encode_cell() {
let expected = "te6ccgEBAQEAIgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADA5";
Expand Down
47 changes: 42 additions & 5 deletions nekoton-abi/src/tvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ use ton_block::{
use ton_types::SliceData;
use ton_vm::executor::gas::gas_state::Gas;
use ton_vm::stack::integer::IntegerData;
use ton_vm::stack::{savelist::SaveList, Stack, StackItem};
use ton_vm::stack::{savelist::SaveList, Stack};

pub type BehaviorModifiers = ton_vm::executor::BehaviorModifiers;
pub type StackItem = ton_vm::stack::StackItem;

#[derive(Debug, Copy, Clone)]
pub struct BriefBlockchainConfig {
Expand Down Expand Up @@ -43,11 +46,12 @@ impl From<ton_executor::BlockchainConfig> for BriefBlockchainConfig {
pub fn call(
utime: u32,
lt: u64,
account: &mut AccountStuff,
account: &AccountStuff,
stack: Stack,
config: &BriefBlockchainConfig,
modifiers: &BehaviorModifiers,
) -> Result<(ton_vm::executor::Engine, i32, bool), ExecutionError> {
let state = match &mut account.storage.state {
let state = match &account.storage.state {
ton_block::AccountState::AccountActive { state_init, .. } => Ok(state_init),
_ => Err(ExecutionError::AccountIsNotActive),
}?;
Expand Down Expand Up @@ -85,6 +89,7 @@ pub fn call(
Some(gas),
);
engine.set_signature_id(config.global_id);
engine.modify_behavior(modifiers.clone());

let result = engine.execute();

Expand Down Expand Up @@ -112,6 +117,7 @@ pub fn call_msg(
account: &mut AccountStuff,
msg: &Message,
config: &BriefBlockchainConfig,
modifiers: &ton_vm::executor::BehaviorModifiers,
) -> Result<ActionPhaseOutput, ExecutionError> {
let msg_cell = msg
.write_to_new_cell()
Expand All @@ -135,7 +141,7 @@ pub fn call_msg(
.push(StackItem::Slice(msg.body().unwrap_or_default())) // message body
.push(function_selector); // function selector

let (engine, exit_code, success) = call(utime, lt, account, stack, config)?;
let (engine, exit_code, success) = call(utime, lt, account, stack, config, modifiers)?;
if !success {
return Ok(ActionPhaseOutput {
messages: None,
Expand Down Expand Up @@ -166,6 +172,37 @@ pub fn call_msg(
})
}

pub fn call_getter(
utime: u32,
lt: u64,
account: &AccountStuff,
method_id: u32,
args: &[ton_vm::stack::StackItem],
config: &BriefBlockchainConfig,
modifiers: &ton_vm::executor::BehaviorModifiers,
) -> Result<VmGetterOutput, ExecutionError> {
let mut stack = Stack::new();
for arg in args {
stack.push(arg.clone());
}
stack.push(ton_vm::int!(method_id));

let (mut engine, exit_code, is_ok) = call(utime, lt, account, stack, config, modifiers)?;

Ok(VmGetterOutput {
stack: engine.withdraw_stack().storage,
exit_code,
is_ok,
})
}

#[derive(Debug, Clone)]
pub struct VmGetterOutput {
pub stack: Vec<ton_vm::stack::StackItem>,
pub exit_code: i32,
pub is_ok: bool,
}

fn build_contract_info(
address: &MsgAddressInt,
balance: &CurrencyCollection,
Expand All @@ -192,7 +229,7 @@ pub struct ActionPhaseOutput {
pub exit_code: i32,
}

#[derive(thiserror::Error, Debug)]
#[derive(thiserror::Error, Debug, Clone, Copy)]
pub enum ExecutionError {
#[error("Failed to serialize message")]
FailedToSerializeMessage,
Expand Down
19 changes: 19 additions & 0 deletions nekoton-utils/src/cell.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anyhow::Result;
use ton_block::{Deserializable, MaybeDeserialize};
use ton_types::{BuilderData, Cell, CellType, IBitstring, LevelMask, UInt256};

const EMPTY_CELL_HASH: [u8; 32] = [
Expand Down Expand Up @@ -56,3 +57,21 @@ pub fn make_pruned_branch_cell(cell: &Cell, merkle_depth: u8) -> Result<Cell> {
}
result.into_cell()
}

pub fn deserialize_account_stuff(cell: Cell) -> Result<ton_block::AccountStuff> {
let slice = &mut ton_types::SliceData::load_cell(cell)?;
Ok(ton_block::AccountStuff {
addr: Deserializable::construct_from(slice)?,
storage_stat: Deserializable::construct_from(slice)?,
storage: ton_block::AccountStorage {
last_trans_lt: Deserializable::construct_from(slice)?,
balance: Deserializable::construct_from(slice)?,
state: Deserializable::construct_from(slice)?,
init_code_hash: if slice.remaining_bits() > 0 {
UInt256::read_maybe_from(slice)?
} else {
None
},
},
})
}
1 change: 1 addition & 0 deletions nekoton-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
pub use self::address::*;
pub use self::cell::*;
pub use self::clock::*;
pub use self::crc::crc_16;
#[cfg(feature = "encryption")]
pub use self::encryption::*;
pub use self::serde_helpers::*;
Expand Down
22 changes: 1 addition & 21 deletions nekoton-utils/src/serde_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -697,8 +697,6 @@ pub mod serde_ton_block {
}

pub mod serde_account_stuff {
use ton_block::Deserializable;

use super::*;

#[inline(always)]
Expand All @@ -713,28 +711,10 @@ pub mod serde_account_stuff {
where
D: serde::Deserializer<'de>,
{
use ton_block::MaybeDeserialize;

let data = String::deserialize(deserializer)?;
let bytes = base64::decode(data).map_err(D::Error::custom)?;
ton_types::deserialize_tree_of_cells(&mut bytes.as_slice())
.and_then(|cell| {
let slice = &mut ton_types::SliceData::load_cell(cell)?;
Ok(ton_block::AccountStuff {
addr: Deserializable::construct_from(slice)?,
storage_stat: Deserializable::construct_from(slice)?,
storage: ton_block::AccountStorage {
last_trans_lt: Deserializable::construct_from(slice)?,
balance: Deserializable::construct_from(slice)?,
state: Deserializable::construct_from(slice)?,
init_code_hash: if slice.remaining_bits() > 0 {
UInt256::read_maybe_from(slice)?
} else {
None
},
},
})
})
.and_then(crate::deserialize_account_stuff)
.map_err(D::Error::custom)
}
}
Expand Down

0 comments on commit 63e4dfc

Please sign in to comment.