use num_bigint::{BigInt, Sign};
use tycho_types::cell::LoadMode;
use tycho_types::error::Error;
use tycho_types::num::SplitDepth;
use tycho_types::prelude::*;
use tycho_vm_proc::vm_module;
use crate::error::VmResult;
use crate::gas::GasConsumer;
use crate::instr::cellops::{finish_store_ok, finish_store_overflow};
use crate::saferc::SafeRc;
use crate::smc_info::VmVersion;
use crate::stack::{RcStackValue, Stack, StackValue, Tuple};
use crate::state::VmState;
use crate::util::{OwnedCellSlice, load_uint_leq};
pub struct CurrencyOps;
#[vm_module]
impl CurrencyOps {
#[op(code = "fa00", fmt = "LDGRAMS", args(len_bits = 4, signed = false))]
#[op(code = "fa01", fmt = "LDVARINT16", args(len_bits = 4, signed = true))]
#[op(code = "fa04", fmt = "LDVARUINT32", args(len_bits = 5, signed = false))]
#[op(code = "fa05", fmt = "LDVARINT32", args(len_bits = 5, signed = true))]
fn exec_load_var_integer(st: &mut VmState, len_bits: u16, signed: bool) -> VmResult<i32> {
let stack = SafeRc::make_mut(&mut st.stack);
let mut csr = ok!(stack.pop_cs());
let int;
let cs_range = {
let mut cs = csr.apply();
int = cs.load_var_bigint(len_bits, signed)?;
cs.range()
};
SafeRc::make_mut(&mut csr).set_range(cs_range);
ok!(stack.push_int(int));
ok!(stack.push_raw(csr));
Ok(0)
}
#[op(code = "fa02", fmt = "STGRAMS", args(len_bits = 4, signed = false))]
#[op(code = "fa03", fmt = "STVARINT16", args(len_bits = 4, signed = true))]
#[op(code = "fa06", fmt = "STVARUINT32", args(len_bits = 5, signed = false))]
#[op(code = "fa07", fmt = "STVARINT32", args(len_bits = 5, signed = true))]
fn exec_store_var_integer(st: &mut VmState, len_bits: u16, signed: bool) -> VmResult<i32> {
let stack = SafeRc::make_mut(&mut st.stack);
let int = ok!(stack.pop_int());
let mut builder = ok!(stack.pop_builder());
SafeRc::make_mut(&mut builder).store_var_bigint(&int, len_bits, signed)?;
ok!(stack.push_raw(builder));
Ok(0)
}
#[op(code = "fa40", fmt = "LDMSGADDR", args(quiet = false))]
#[op(code = "fa41", fmt = "LDMSGADDRQ", args(quiet = true))]
fn exec_load_message_addr(st: &mut VmState, quiet: bool) -> VmResult<i32> {
let stack = SafeRc::make_mut(&mut st.stack);
let mut csr = ok!(stack.pop_cs());
let mut cs = csr.apply();
let mut addr = cs;
match skip_message_addr(&mut cs, &st.version) {
Ok(()) => {
addr.skip_last(cs.size_bits(), cs.size_refs())?;
let addr_cs = OwnedCellSlice::from((addr.range(), csr.cell().clone()));
let range = cs.range();
SafeRc::make_mut(&mut csr).set_range(range);
ok!(stack.push(addr_cs));
ok!(stack.push_raw(csr));
if quiet {
ok!(stack.push_bool(true));
}
}
Err(_) if quiet => {
ok!(stack.push_raw(csr));
ok!(stack.push_bool(false));
}
Err(e) => vm_bail!(CellError(e)),
}
Ok(0)
}
#[op(code = "fa42", fmt = "PARSEMSGADDR", args(quiet = false))]
#[op(code = "fa43", fmt = "PARSEMSGADDRQ", args(quiet = true))]
fn exec_parse_message_addr(st: &mut VmState, quiet: bool) -> VmResult<i32> {
let stack = SafeRc::make_mut(&mut st.stack);
let csr = ok!(stack.pop_cs());
let mut range = csr.range();
match parse_message_addr(csr.cell(), &mut range, &st.version) {
Ok(parts) => {
ok!(stack.push(parts.into_tuple()));
if quiet {
ok!(stack.push_bool(true));
}
}
Err(_) if quiet => ok!(stack.push_bool(false)),
Err(e) => vm_bail!(CellError(e)),
}
Ok(0)
}
#[op(code = "fa44", fmt = "REWRITESTDADDR", args(var = false, q = false))]
#[op(code = "fa45", fmt = "REWRITESTDADDRQ", args(var = false, q = true))]
#[op(code = "fa46", fmt = "REWRITEVARADDR", args(var = true, q = false))]
#[op(code = "fa47", fmt = "REWRITEVARADDR", args(var = true, q = true))]
fn exec_rewrite_message_addr(st: &mut VmState, var: bool, q: bool) -> VmResult<i32> {
let handle_error = |stack: &mut Stack, e: Error| {
if q {
ok!(stack.push_bool(false));
Ok(0)
} else {
vm_bail!(CellError(e));
}
};
let stack = SafeRc::make_mut(&mut st.stack);
let csr = ok!(stack.pop_cs());
let mut range = csr.range();
let parts = match parse_message_addr(csr.cell(), &mut range, &st.version) {
Ok(parts) => parts,
Err(e) => return handle_error(stack, e),
};
let (pfx, wc, addr) = match parts {
AddrParts::Std {
pfx,
workchain,
addr,
} => (pfx, workchain as i32, addr),
AddrParts::Var {
pfx,
workchain,
addr,
} => (pfx, workchain, addr),
_ => return handle_error(stack, Error::CellUnderflow),
};
let addr = if var {
match rewrite_addr_var(addr, pfx, &st.gas) {
Ok(addr) => SafeRc::new_dyn_value(addr),
Err(e) => return handle_error(stack, e),
}
} else {
match rewrite_addr_std(addr, pfx) {
Ok(addr) => SafeRc::new_dyn_value(addr),
Err(e) => return handle_error(stack, e),
}
};
ok!(stack.push_int(wc));
ok!(stack.push_raw(addr));
if q {
ok!(stack.push_bool(true));
}
Ok(0)
}
#[op(code = "fa48", fmt = "LDSTDADDR", args(quiet = false))]
#[op(code = "fa49", fmt = "LDSTDADDRQ", args(quiet = true))]
fn exec_load_std_message_addr(st: &mut VmState, quiet: bool) -> VmResult<i32> {
let stack = SafeRc::make_mut(&mut st.stack);
let mut csr = ok!(stack.pop_cs());
let mut cs = csr.apply();
let mut addr = cs;
match skip_std_message_addr(&mut cs, &st.version) {
Ok(()) => {
addr.skip_last(cs.size_bits(), cs.size_refs())?;
let addr_cs = OwnedCellSlice::from((addr.range(), csr.cell().clone()));
let range = cs.range();
SafeRc::make_mut(&mut csr).set_range(range);
ok!(stack.push(addr_cs));
ok!(stack.push_raw(csr));
if quiet {
ok!(stack.push_bool(true));
}
}
Err(_) if quiet => {
ok!(stack.push_raw(csr));
ok!(stack.push_bool(false));
}
Err(e) => vm_bail!(CellError(e)),
}
Ok(0)
}
#[op(code = "fa50", fmt = "LDOPTSTDADDR", args(quiet = false))]
#[op(code = "fa51", fmt = "LDOPTSTDADDRQ", args(quiet = true))]
fn exec_load_opt_std_message_addr(st: &mut VmState, quiet: bool) -> VmResult<i32> {
let stack = SafeRc::make_mut(&mut st.stack);
let mut csr: SafeRc<OwnedCellSlice> = ok!(stack.pop_cs());
let mut cs = csr.apply();
let mut addr = cs;
match skip_opt_std_message_addr(&mut cs, &st.version) {
Ok(has_addr) => {
if has_addr {
addr.skip_last(cs.size_bits(), cs.size_refs())?;
let addr = OwnedCellSlice::from((addr.range(), csr.cell().clone()));
ok!(stack.push(addr));
} else {
ok!(stack.push_null());
}
let range = cs.range();
SafeRc::make_mut(&mut csr).set_range(range);
ok!(stack.push_raw(csr));
if quiet {
ok!(stack.push_bool(true));
}
}
Err(_) if quiet => {
ok!(stack.push_raw(csr));
ok!(stack.push_bool(false));
}
Err(e) => vm_bail!(CellError(e)),
}
Ok(0)
}
#[op(code = "fa52", fmt = "STSTDADDR", args(quiet = false))]
#[op(code = "fa53", fmt = "STSTDADDRQ", args(quiet = true))]
fn exec_store_std_address(st: &mut VmState, quiet: bool) -> VmResult<i32> {
let stack = SafeRc::make_mut(&mut st.stack);
let mut builder: SafeRc<CellBuilder> = ok!(stack.pop_builder());
let csr = ok!(stack.pop_cs());
let cs = csr.apply();
if !csr.fits_into(&builder) || !is_valid_std_address(&cs, &st.version) {
finish_store_overflow(stack, csr, builder, quiet)
} else {
SafeRc::make_mut(&mut builder).store_slice(cs)?;
finish_store_ok(stack, builder, quiet)
}
}
#[op(code = "fa54", fmt = "STOPTSTDADDR", args(quiet = false))]
#[op(code = "fa55", fmt = "STOPTSTDADDRQ", args(quiet = true))]
fn exec_store_opt_std_address(st: &mut VmState, quiet: bool) -> VmResult<i32> {
let stack = SafeRc::make_mut(&mut st.stack);
let mut builder: SafeRc<CellBuilder> = ok!(stack.pop_builder());
match ok!(stack.pop_cs_opt()) {
None => {
if !builder.has_capacity(2, 0) {
return finish_store_overflow(stack, SafeRc::new(()), builder, quiet);
}
SafeRc::make_mut(&mut builder).store_zeros(2)?;
ok!(stack.push_raw(builder));
if quiet {
ok!(stack.push_bool(false));
}
Ok(0)
}
Some(csr) => {
let cs = csr.apply();
if !csr.fits_into(&builder) || !is_valid_std_address(&cs, &st.version) {
finish_store_overflow(stack, csr, builder, quiet)
} else {
SafeRc::make_mut(&mut builder).store_slice(cs)?;
finish_store_ok(stack, builder, quiet)
}
}
}
}
}
fn is_valid_std_address(cs: &CellSlice, version: &VmVersion) -> bool {
let mut cs = *cs;
skip_std_message_addr(&mut cs, version).is_ok() && cs.is_empty()
}
fn rewrite_addr_var(
addr: OwnedCellSlice,
pfx: Option<OwnedCellSlice>,
gas: &GasConsumer,
) -> Result<OwnedCellSlice, Error> {
let Some(pfx) = pfx else {
return Ok(addr);
};
if pfx.range().size_bits() == addr.range().size_bits() {
return Ok(pfx);
}
let mut cs = addr.apply();
let pfx = pfx.apply();
cs.skip_first(pfx.size_bits(), 0)?;
let mut cb = CellBuilder::new();
cb.store_slice(pfx)?;
cb.store_slice(cs)?;
let cell = cb.build_ext(gas)?;
let cell = gas.load_cell(cell, LoadMode::UseGas)?;
Ok(OwnedCellSlice::new_allow_exotic(cell))
}
fn rewrite_addr_std(addr: OwnedCellSlice, pfx: Option<OwnedCellSlice>) -> Result<BigInt, Error> {
let mut addr = addr.apply().load_u256()?.0;
if let Some(pfx) = pfx {
let mut pfx = pfx.apply();
let pfx_len = pfx.size_bits();
let mut buffer = [0u8; 4];
pfx.load_raw(&mut buffer, pfx_len)?;
let bytes = (pfx_len / 8) as usize;
addr[..bytes].copy_from_slice(&buffer[..bytes]);
let bits = pfx_len % 8;
if bits != 0 {
addr[bytes] &= (1 << (8 - bits)) - 1;
addr[bytes] |= buffer[bytes];
}
}
Ok(BigInt::from_bytes_be(Sign::Plus, &addr))
}
fn parse_message_addr(
cell: &Cell,
range: &mut CellSliceRange,
version: &VmVersion,
) -> Result<AddrParts, Error> {
let mut cs = range.apply(cell)?;
match cs.load_small_uint(2)? {
0b00 => Ok(AddrParts::None),
0b01 => {
let len = cs.load_uint(9)? as u16;
let addr = cs.load_prefix(len, 0)?;
Ok(AddrParts::Ext {
addr: OwnedCellSlice::from((addr.range(), cell.clone())),
})
}
0b10 => {
let pfx = parse_maybe_anycast(cell, &mut cs, version)?;
let workchain = cs.load_u8()? as i8;
let addr = cs.load_prefix(256, 0)?;
Ok(AddrParts::Std {
pfx,
workchain,
addr: OwnedCellSlice::from((addr.range(), cell.clone())),
})
}
0b11 => {
if version.is_ton(10..) {
return Err(Error::CellUnderflow);
}
let pfx = parse_maybe_anycast(cell, &mut cs, version)?;
let len = cs.load_uint(9)? as u16;
let workchain = cs.load_u32()? as i32;
let addr = cs.load_prefix(len, 0)?;
Ok(AddrParts::Var {
pfx,
workchain,
addr: OwnedCellSlice::from((addr.range(), cell.clone())),
})
}
_ => Err(Error::InvalidData),
}
}
fn parse_maybe_anycast(
cell: &Cell,
cs: &mut CellSlice<'_>,
version: &VmVersion,
) -> Result<Option<OwnedCellSlice>, Error> {
Ok(if cs.load_bit()? {
if version.is_ton(10..) {
return Err(Error::CellUnderflow);
}
let depth = SplitDepth::new(load_uint_leq(cs, 30)? as u8)?;
let pfx = cs.load_prefix(depth.into_bit_len(), 0)?;
Some(OwnedCellSlice::from((pfx.range(), cell.clone())))
} else {
None
})
}
enum AddrParts {
None,
Ext {
addr: OwnedCellSlice,
},
Std {
pfx: Option<OwnedCellSlice>,
workchain: i8,
addr: OwnedCellSlice,
},
Var {
pfx: Option<OwnedCellSlice>,
workchain: i32,
addr: OwnedCellSlice,
},
}
impl AddrParts {
fn into_tuple(self) -> Tuple {
fn opt_to_value<T: StackValue + 'static>(value: Option<T>) -> RcStackValue {
match value {
None => Stack::make_null(),
Some(value) => SafeRc::new_dyn_value(value),
}
}
match self {
Self::None => vec![Stack::make_zero()],
Self::Ext { addr } => vec![Stack::make_one(), SafeRc::new_dyn_value(addr)],
Self::Std {
pfx,
workchain,
addr,
} => vec![
SafeRc::new_dyn_value(BigInt::from(2)),
opt_to_value(pfx),
SafeRc::new_dyn_value(BigInt::from(workchain)),
SafeRc::new_dyn_value(addr),
],
Self::Var {
pfx,
workchain,
addr,
} => vec![
SafeRc::new_dyn_value(BigInt::from(3)),
opt_to_value(pfx),
SafeRc::new_dyn_value(BigInt::from(workchain)),
SafeRc::new_dyn_value(addr),
],
}
}
}
fn skip_message_addr(cs: &mut CellSlice, version: &VmVersion) -> Result<(), Error> {
match cs.load_small_uint(2)? {
0b00 => Ok(()),
0b01 => {
let len = cs.load_uint(9)? as u16;
cs.skip_first(len, 0)
}
0b10 => {
skip_maybe_anycast(cs, version)?;
cs.skip_first(8 + 256, 0)?;
Ok(())
}
0b11 => {
if version.is_ton(10..) {
return Err(Error::CellUnderflow);
}
skip_maybe_anycast(cs, version)?;
let len = cs.load_uint(9)? as u16;
cs.skip_first(32 + len, 0)
}
_ => Err(Error::InvalidData),
}
}
fn skip_std_message_addr(cs: &mut CellSlice, version: &VmVersion) -> Result<(), Error> {
match cs.load_small_uint(2)? {
0b10 => {
skip_maybe_anycast(cs, version)?;
cs.skip_first(8 + 256, 0)?;
Ok(())
}
_ => Err(Error::InvalidData),
}
}
fn skip_opt_std_message_addr(cs: &mut CellSlice, version: &VmVersion) -> Result<bool, Error> {
match cs.load_small_uint(2)? {
0b00 => Ok(false),
0b10 => {
skip_maybe_anycast(cs, version)?;
cs.skip_first(8 + 256, 0)?;
Ok(true)
}
_ => Err(Error::InvalidData),
}
}
fn skip_maybe_anycast(cs: &mut CellSlice, version: &VmVersion) -> Result<(), Error> {
if cs.load_bit()? {
if version.is_ton(10..) {
return Err(Error::CellUnderflow);
}
let depth = SplitDepth::new(load_uint_leq(cs, 30)? as u8)?;
cs.skip_first(depth.into_bit_len(), 0)?;
}
Ok(())
}
#[cfg(test)]
mod test {
use std::str::FromStr;
use tracing_test::traced_test;
use tycho_types::models::{Anycast, StdAddr};
use super::*;
#[test]
#[traced_test]
fn load_std_address_test() -> anyhow::Result<()> {
let address = StdAddr::from_str(
"0:5aa09496fdce333b5208125396cda8d9c83194b25ee694e5aa37fdc70aa11b61",
)?;
let mut builder = CellBuilder::new();
address.store_into(&mut builder, Cell::empty_context())?;
let mut slice = OwnedCellSlice::new_allow_exotic(builder.build()?);
let value = SafeRc::new_dyn_value(slice.clone());
let mut cs = slice.apply();
cs.skip_first(cs.size_bits(), 0)?;
slice.set_range(cs.range());
assert_run_vm!("LDSTDADDR", [raw value.clone()] => [raw value.clone(), slice slice.clone()]);
assert_run_vm!("LDSTDADDRQ", [raw value.clone()] => [raw value, slice slice, int -1]);
Ok(())
}
#[test]
#[traced_test]
fn load_bad_std_address_test() -> anyhow::Result<()> {
let builder = CellBuilder::new();
let mut slice = OwnedCellSlice::new_allow_exotic(builder.build()?);
let value = SafeRc::new_dyn_value(slice.clone());
let mut cs = slice.apply();
cs.skip_first(cs.size_bits(), 0)?;
slice.set_range(cs.range());
assert_run_vm!("LDSTDADDR", [raw value.clone()] => [int 0], exit_code: 9);
assert_run_vm!("LDSTDADDRQ", [raw value.clone()] => [raw value, int 0]);
Ok(())
}
#[test]
#[traced_test]
fn load_opt_std_address_test() -> anyhow::Result<()> {
let address = StdAddr::from_str(
"0:5aa09496fdce333b5208125396cda8d9c83194b25ee694e5aa37fdc70aa11b61",
)?;
let mut builder = CellBuilder::new();
address.store_into(&mut builder, Cell::empty_context())?;
let mut slice = OwnedCellSlice::new_allow_exotic(builder.build()?);
let value = SafeRc::new_dyn_value(slice.clone());
let mut cs = slice.apply();
cs.skip_first(cs.size_bits(), 0)?;
slice.set_range(cs.range());
assert_run_vm!("LDOPTSTDADDR", [raw value.clone()] => [raw value.clone(), slice slice.clone()]);
assert_run_vm!("LDOPTSTDADDRQ", [raw value.clone()] => [raw value, slice slice, int -1]);
let mut builder = CellBuilder::new();
builder.store_zeros(2)?;
let mut slice = OwnedCellSlice::new_allow_exotic(builder.build()?);
let value = SafeRc::new_dyn_value(slice.clone());
let mut cs = slice.apply();
cs.skip_first(cs.size_bits(), 0)?;
slice.set_range(cs.range());
assert_run_vm!("LDOPTSTDADDR", [raw value.clone()] => [null, slice slice.clone()]);
assert_run_vm!("LDOPTSTDADDRQ", [raw value.clone()] => [null, slice slice, int -1]);
Ok(())
}
#[test]
#[traced_test]
fn load_bad_std_opt_address_test() -> anyhow::Result<()> {
let builder = CellBuilder::new();
let mut slice = OwnedCellSlice::new_allow_exotic(builder.build()?);
let value = SafeRc::new_dyn_value(slice.clone());
let mut cs = slice.apply();
cs.skip_first(cs.size_bits(), 0)?;
slice.set_range(cs.range());
assert_run_vm!("LDOPTSTDADDR", [raw value.clone()] => [int 0], exit_code: 9);
assert_run_vm!("LDOPTSTDADDRQ", [raw value.clone()] => [raw value, int 0]);
Ok(())
}
#[test]
#[traced_test]
fn store_std_address_test() -> anyhow::Result<()> {
let address = StdAddr::from_str(
"0:5aa09496fdce333b5208125396cda8d9c83194b25ee694e5aa37fdc70aa11b61",
)?;
let mut builder = CellBuilder::new();
address.store_into(&mut builder, Cell::empty_context())?;
let filled_builder = builder.clone();
let slice = OwnedCellSlice::new_allow_exotic(builder.build()?);
let value = SafeRc::new_dyn_value(slice.clone());
let builder = CellBuilder::new();
let rc_buider = SafeRc::new_dyn_value(builder.clone());
let filler_rc_builder = SafeRc::new_dyn_value(filled_builder);
assert_run_vm!("STSTDADDR", [raw value.clone(), raw rc_buider.clone()] => [raw filler_rc_builder.clone()]);
assert_run_vm!("STSTDADDRQ", [raw value.clone(), raw rc_buider] => [raw filler_rc_builder, int 0]);
Ok(())
}
#[test]
#[traced_test]
fn store_bad_std_address_test() -> anyhow::Result<()> {
let builder = CellBuilder::new();
let slice = OwnedCellSlice::new_allow_exotic(builder.build()?);
let value = SafeRc::new_dyn_value(slice.clone());
let builder = CellBuilder::new();
let rc_buider = SafeRc::new_dyn_value(builder.clone());
assert_run_vm!("STSTDADDR", [raw value.clone(), raw rc_buider.clone()] => [int 0], exit_code: 8);
assert_run_vm!("STSTDADDRQ", [raw value.clone(), raw rc_buider.clone()] => [raw value.clone(), raw rc_buider, int -1]);
Ok(())
}
#[test]
#[traced_test]
fn store_std_opt_address_test() -> anyhow::Result<()> {
let address = StdAddr::from_str(
"0:5aa09496fdce333b5208125396cda8d9c83194b25ee694e5aa37fdc70aa11b61",
)?;
let mut builder = CellBuilder::new();
address.store_into(&mut builder, Cell::empty_context())?;
let filled_builder = builder.clone();
let slice = OwnedCellSlice::new_allow_exotic(builder.build()?);
let value = SafeRc::new_dyn_value(slice.clone());
let builder = CellBuilder::new();
let rc_buider = SafeRc::new_dyn_value(builder.clone());
let filler_rc_builder = SafeRc::new_dyn_value(filled_builder);
assert_run_vm!("STOPTSTDADDR", [raw value.clone(), raw rc_buider.clone()] => [raw filler_rc_builder.clone()]);
assert_run_vm!("STOPTSTDADDRQ", [raw value.clone(), raw rc_buider] => [raw filler_rc_builder, int 0]);
Ok(())
}
#[test]
#[traced_test]
fn load_varint_u16_test() -> anyhow::Result<()> {
let int = BigInt::from(5);
let mut builder = CellBuilder::new();
builder.store_var_bigint(&int, 4, true)?;
let mut slice = OwnedCellSlice::new_allow_exotic(builder.build()?);
let value = SafeRc::new_dyn_value(slice.clone());
let mut cs = slice.apply();
cs.skip_first(12, 0)?;
slice.set_range(cs.range());
assert_run_vm!("LDVARUINT16", [raw value] => [int 5, slice slice]);
Ok(())
}
#[test]
#[traced_test]
fn load_varint_u32_test() -> anyhow::Result<()> {
let int = BigInt::from(5);
let mut builder = CellBuilder::new();
builder.store_var_bigint(&int, 5, true)?;
let mut slice = OwnedCellSlice::new_allow_exotic(builder.build()?);
let value = SafeRc::new_dyn_value(slice.clone());
let mut cs = slice.apply();
cs.skip_first(13, 0)?;
slice.set_range(cs.range());
assert_run_vm!("LDVARUINT32", [raw value] => [int 5, slice slice]);
Ok(())
}
#[test]
#[traced_test]
fn parse_message_address() -> anyhow::Result<()> {
let addr = "0:6301b2c75596e6e569a6d13ae4ec70c94f177ece0be19f968ddce73d44e7afc7"
.parse::<StdAddr>()?;
let mut addr = OwnedCellSlice::new_allow_exotic(CellBuilder::build_from(addr)?);
let value = SafeRc::new_dyn_value(addr.clone());
let mut cs = addr.apply();
cs.skip_first(11, 0).unwrap();
addr.set_range(cs.range());
let tuple = SafeRc::new_dyn_value(tuple![
int 2,
null,
int 0,
slice addr,
]);
assert_run_vm!("PARSEMSGADDR", [raw value.clone()] => [raw tuple.clone()]);
assert_run_vm!("PARSEMSGADDRQ", [raw value.clone()] => [raw tuple, int -1]);
Ok(())
}
#[test]
#[traced_test]
fn test_anycast_error() -> anyhow::Result<()> {
let mut addr = "0:6301b2c75596e6e569a6d13ae4ec70c94f177ece0be19f968ddce73d44e7afc7"
.parse::<StdAddr>()?;
addr.anycast = Some(Box::new(Anycast {
depth: SplitDepth::MIN,
rewrite_prefix: vec![],
}));
let addr = OwnedCellSlice::new_allow_exotic(CellBuilder::build_from(addr)?);
let value = SafeRc::new_dyn_value(addr.clone());
assert_run_vm!("PARSEMSGADDR", [raw value.clone()] => [int 0], exit_code: 9);
assert_run_vm!("PARSEMSGADDRQ", [raw value.clone()] => [int 0]);
Ok(())
}
}