use std::hash::BuildHasher;
use std::num::NonZeroU64;
use std::rc::Rc;
use std::sync::Arc;
use ahash::HashSet;
use tycho_types::cell::{CellParts, LoadMode};
use tycho_types::error::Error;
use tycho_types::models::{LibDescr, SimpleLib};
use tycho_types::prelude::*;
use crate::error::VmResult;
use crate::saferc::SafeRc;
use crate::stack::Stack;
use crate::util::OwnedCellSlice;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GasParams {
pub max: u64,
pub limit: u64,
pub credit: u64,
pub price: u64,
}
impl GasParams {
pub const MAX_GAS: u64 = i64::MAX as u64;
const STUB_GAS_PRICE: u64 = 1000 << 16;
pub const fn unlimited() -> Self {
Self {
max: Self::MAX_GAS,
limit: Self::MAX_GAS,
credit: 0,
price: Self::STUB_GAS_PRICE,
}
}
pub const fn getter() -> Self {
Self {
max: 1000000,
limit: 1000000,
credit: 0,
price: Self::STUB_GAS_PRICE,
}
}
}
impl Default for GasParams {
#[inline]
fn default() -> Self {
Self::unlimited()
}
}
pub trait LibraryProvider {
fn find(&self, library_hash: &HashBytes) -> Result<Option<Cell>, Error>;
fn find_ref<'a>(&'a self, library_hash: &HashBytes) -> Result<Option<&'a DynCell>, Error>;
}
impl<T: LibraryProvider> LibraryProvider for &'_ T {
fn find(&self, library_hash: &HashBytes) -> Result<Option<Cell>, Error> {
T::find(self, library_hash)
}
fn find_ref<'a>(&'a self, library_hash: &HashBytes) -> Result<Option<&'a DynCell>, Error> {
T::find_ref(self, library_hash)
}
}
impl<T: LibraryProvider> LibraryProvider for Option<T> {
fn find(&self, library_hash: &HashBytes) -> Result<Option<Cell>, Error> {
match self {
Some(this) => T::find(this, library_hash),
None => Ok(None),
}
}
fn find_ref<'a>(&'a self, library_hash: &HashBytes) -> Result<Option<&'a DynCell>, Error> {
match self {
Some(this) => T::find_ref(this, library_hash),
None => Ok(None),
}
}
}
impl<T1, T2> LibraryProvider for (T1, T2)
where
T1: LibraryProvider,
T2: LibraryProvider,
{
fn find(&self, library_hash: &HashBytes) -> Result<Option<Cell>, Error> {
if let res @ Some(_) = ok!(T1::find(&self.0, library_hash)) {
return Ok(res);
}
T2::find(&self.1, library_hash)
}
fn find_ref<'a>(&'a self, library_hash: &HashBytes) -> Result<Option<&'a DynCell>, Error> {
if let res @ Some(_) = ok!(T1::find_ref(&self.0, library_hash)) {
return Ok(res);
}
T2::find_ref(&self.1, library_hash)
}
}
impl<T1, T2, T3> LibraryProvider for (T1, T2, T3)
where
T1: LibraryProvider,
T2: LibraryProvider,
T3: LibraryProvider,
{
fn find(&self, library_hash: &HashBytes) -> Result<Option<Cell>, Error> {
if let res @ Some(_) = ok!(T1::find(&self.0, library_hash)) {
return Ok(res);
}
if let res @ Some(_) = ok!(T2::find(&self.1, library_hash)) {
return Ok(res);
}
T3::find(&self.2, library_hash)
}
fn find_ref<'a>(&'a self, library_hash: &HashBytes) -> Result<Option<&'a DynCell>, Error> {
if let res @ Some(_) = ok!(T1::find_ref(&self.0, library_hash)) {
return Ok(res);
}
if let res @ Some(_) = ok!(T2::find_ref(&self.1, library_hash)) {
return Ok(res);
}
T3::find_ref(&self.2, library_hash)
}
}
impl<T: LibraryProvider> LibraryProvider for Box<T> {
fn find(&self, library_hash: &HashBytes) -> Result<Option<Cell>, Error> {
T::find(self, library_hash)
}
fn find_ref<'a>(&'a self, library_hash: &HashBytes) -> Result<Option<&'a DynCell>, Error> {
T::find_ref(self, library_hash)
}
}
impl<T: LibraryProvider> LibraryProvider for Rc<T> {
fn find(&self, library_hash: &HashBytes) -> Result<Option<Cell>, Error> {
T::find(self, library_hash)
}
fn find_ref<'a>(&'a self, library_hash: &HashBytes) -> Result<Option<&'a DynCell>, Error> {
T::find_ref(self, library_hash)
}
}
impl<T: LibraryProvider> LibraryProvider for Arc<T> {
fn find(&self, library_hash: &HashBytes) -> Result<Option<Cell>, Error> {
T::find(self, library_hash)
}
fn find_ref<'a>(&'a self, library_hash: &HashBytes) -> Result<Option<&'a DynCell>, Error> {
T::find_ref(self, library_hash)
}
}
#[derive(Default, Debug, Clone, Copy)]
pub struct NoLibraries;
impl LibraryProvider for NoLibraries {
#[inline]
fn find(&self, _library_hash: &HashBytes) -> Result<Option<Cell>, Error> {
Ok(None)
}
fn find_ref<'a>(&'a self, _library_hash: &HashBytes) -> Result<Option<&'a DynCell>, Error> {
Ok(None)
}
}
impl LibraryProvider for Dict<HashBytes, SimpleLib> {
fn find(&self, library_hash: &HashBytes) -> Result<Option<Cell>, Error> {
match self.get(library_hash)? {
Some(lib) if lib.root.repr_hash() == library_hash => Ok(Some(lib.root)),
_ => Ok(None),
}
}
fn find_ref<'a>(&'a self, library_hash: &HashBytes) -> Result<Option<&'a DynCell>, Error> {
match self
.cast_ref::<HashBytes, SimpleLibRef<'_>>()
.get(library_hash)?
{
Some(lib) if lib.root.repr_hash() == library_hash => Ok(Some(lib.root)),
_ => Ok(None),
}
}
}
impl LibraryProvider for Vec<Dict<HashBytes, SimpleLib>> {
fn find(&self, library_hash: &HashBytes) -> Result<Option<Cell>, Error> {
for lib in self {
match lib.get(library_hash)? {
Some(lib) if lib.root.repr_hash() == library_hash => return Ok(Some(lib.root)),
_ => continue,
}
}
Ok(None)
}
fn find_ref<'a>(&'a self, library_hash: &HashBytes) -> Result<Option<&'a DynCell>, Error> {
for lib in self {
match lib
.cast_ref::<HashBytes, SimpleLibRef<'_>>()
.get(library_hash)?
{
Some(lib) if lib.root.repr_hash() == library_hash => return Ok(Some(lib.root)),
_ => continue,
}
}
Ok(None)
}
}
impl LibraryProvider for Dict<HashBytes, LibDescr> {
fn find(&self, library_hash: &HashBytes) -> Result<Option<Cell>, Error> {
Ok(self.get(library_hash)?.and_then(|descr| {
if descr.lib.repr_hash() == library_hash {
Some(descr.lib.clone())
} else {
None
}
}))
}
fn find_ref<'a>(&'a self, library_hash: &HashBytes) -> Result<Option<&'a DynCell>, Error> {
struct LibDescrRef<'tlb> {
lib: &'tlb DynCell,
}
impl<'a> Load<'a> for LibDescrRef<'a> {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
if slice.load_small_uint(2)? != 0 {
return Err(Error::InvalidTag);
}
Ok(Self {
lib: slice.load_reference()?,
})
}
}
impl EquivalentRepr<LibDescr> for LibDescrRef<'_> {}
Ok(self
.cast_ref::<HashBytes, LibDescrRef<'a>>()
.get(library_hash)?
.and_then(|descr| {
if descr.lib.repr_hash() == library_hash {
Some(descr.lib)
} else {
None
}
}))
}
}
impl<S: BuildHasher> LibraryProvider for std::collections::HashMap<HashBytes, SimpleLib, S> {
fn find(&self, library_hash: &HashBytes) -> Result<Option<Cell>, Error> {
Ok(self.get(library_hash).and_then(|lib| {
if lib.root.repr_hash() == library_hash {
Some(lib.root.clone())
} else {
None
}
}))
}
fn find_ref<'a>(&'a self, library_hash: &HashBytes) -> Result<Option<&'a DynCell>, Error> {
Ok(self.get(library_hash).and_then(|lib| {
if lib.root.repr_hash() == library_hash {
Some(lib.root.as_ref())
} else {
None
}
}))
}
}
struct SimpleLibRef<'tlb> {
root: &'tlb DynCell,
}
impl<'a> Load<'a> for SimpleLibRef<'a> {
fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
slice.load_bit()?;
Ok(Self {
root: slice.load_reference()?,
})
}
}
impl EquivalentRepr<SimpleLib> for SimpleLibRef<'_> {}
pub struct GasConsumer<'l> {
gas_max: u64,
gas_limit: std::cell::Cell<u64>,
gas_credit: std::cell::Cell<u64>,
gas_base: std::cell::Cell<u64>,
gas_remaining: std::cell::Cell<i64>,
gas_price: NonZeroU64,
loaded_cells: std::cell::UnsafeCell<HashSet<HashBytes>>,
libraries: &'l dyn LibraryProvider,
chksign_counter: std::cell::Cell<usize>,
free_gas_consumed: std::cell::Cell<u64>,
get_extra_balance_counter: std::cell::Cell<usize>,
missing_library: std::cell::Cell<Option<HashBytes>>,
}
impl<'l> GasConsumer<'l> {
pub const BUILD_CELL_GAS: u64 = 500;
pub const NEW_CELL_GAS: u64 = 100;
pub const OLD_CELL_GAS: u64 = 25;
pub const FREE_STACK_DEPTH: usize = 32;
pub const FREE_SIGNATURE_CHECKS: usize = 10;
pub const CHEAP_GET_BALANCE_CALLS: usize = 5;
pub const CHEAP_GET_BALANCE_GAS_THRESHOLD: u64 = 200;
pub const FREE_NESTED_CONT_JUMP: usize = 8;
pub const STACK_VALUE_GAS_PRICE: u64 = 1;
pub const TUPLE_ENTRY_GAS_PRICE: u64 = 1;
pub const HASH_EXT_ENTRY_GAS_PRICE: u64 = 1;
pub const CHK_SGN_GAS_PRICE: u64 = 4000;
pub const IMPLICIT_JMPREF_GAS_PRICE: u64 = 10;
pub const IMPLICIT_RET_GAS_PRICE: u64 = 5;
pub const EXCEPTION_GAS_PRICE: u64 = 50;
pub const RUNVM_GAS_PRICE: u64 = 40;
pub fn new(params: GasParams) -> Self {
static NO_LIBRARIES: NoLibraries = NoLibraries;
Self::with_libraries(params, &NO_LIBRARIES)
}
pub fn with_libraries(params: GasParams, libraries: &'l dyn LibraryProvider) -> Self {
let gas_remaining = truncate_gas(params.limit.saturating_add(params.credit));
Self {
gas_max: truncate_gas(params.max),
gas_limit: std::cell::Cell::new(truncate_gas(params.limit)),
gas_credit: std::cell::Cell::new(truncate_gas(params.credit)),
gas_base: std::cell::Cell::new(gas_remaining),
gas_remaining: std::cell::Cell::new(gas_remaining as i64),
gas_price: NonZeroU64::new(params.price).unwrap_or(NonZeroU64::MIN),
loaded_cells: Default::default(),
libraries,
chksign_counter: std::cell::Cell::new(0),
free_gas_consumed: std::cell::Cell::new(0),
get_extra_balance_counter: std::cell::Cell::new(0),
missing_library: std::cell::Cell::new(None),
}
}
pub fn limited(&'l self, remaining: u64) -> LimitedGasConsumer<'l> {
LimitedGasConsumer::<'l> {
gas: self,
remaining: std::cell::Cell::new(remaining),
}
}
pub fn derive(&mut self, params: GasConsumerDeriveParams) -> VmResult<ParentGasConsumer<'l>> {
use std::cell::Cell;
Ok(if params.isolate {
self.try_consume(self.free_gas_consumed.get())?;
let gas_remaining = self.remaining();
vm_ensure!(gas_remaining >= 0, OutOfGas);
self.chksign_counter.set(0);
self.free_gas_consumed.set(0);
self.get_extra_balance_counter.set(0);
let params = GasParams {
max: std::cmp::min(params.gas_max, gas_remaining as u64),
limit: std::cmp::min(params.gas_limit, gas_remaining as u64),
credit: 0,
price: self.price(),
};
let libraries = self.libraries;
ParentGasConsumer::Isolated(std::mem::replace(
self,
Self::with_libraries(params, libraries),
))
} else {
let gas_remaining = self.remaining();
vm_ensure!(gas_remaining >= 0, OutOfGas);
let gas_max = std::cmp::min(params.gas_max, gas_remaining as u64);
let gas_limit = std::cmp::min(params.gas_limit, gas_remaining as u64);
ParentGasConsumer::Shared(GasConsumer {
gas_max: std::mem::replace(&mut self.gas_max, gas_max),
gas_limit: std::mem::replace(&mut self.gas_limit, Cell::new(gas_limit)),
gas_credit: std::mem::replace(&mut self.gas_credit, Cell::new(0)),
gas_base: std::mem::replace(&mut self.gas_base, Cell::new(gas_limit)),
gas_remaining: std::mem::replace(
&mut self.gas_remaining,
Cell::new(gas_limit as i64),
),
gas_price: self.gas_price,
loaded_cells: Default::default(),
libraries: self.libraries,
chksign_counter: self.chksign_counter.clone(),
free_gas_consumed: self.free_gas_consumed.clone(),
get_extra_balance_counter: self.get_extra_balance_counter.clone(),
missing_library: self.missing_library.clone(),
})
})
}
pub fn restore(&mut self, mut parent: ParentGasConsumer<'l>) -> RestoredGasConsumer {
let meta = RestoredGasConsumer {
gas_consumed: self.consumed(),
gas_limit: self.limit(),
};
if let ParentGasConsumer::Shared(parent) = &mut parent {
parent.loaded_cells = std::mem::take(&mut self.loaded_cells);
}
match parent {
ParentGasConsumer::Isolated(mut parent) | ParentGasConsumer::Shared(mut parent) => {
let missing_lib = self.missing_library.get_mut();
let parent_lib = parent.missing_library.get_mut();
if parent_lib.is_none() && missing_lib.is_some() {
*parent_lib = *missing_lib;
}
parent.chksign_counter = self.chksign_counter.clone();
parent.free_gas_consumed = self.free_gas_consumed.clone();
parent.get_extra_balance_counter = self.get_extra_balance_counter.clone();
*self = parent
}
}
meta
}
pub fn libraries(&self) -> &'l dyn LibraryProvider {
self.libraries
}
pub fn credit(&self) -> u64 {
self.gas_credit.get()
}
pub fn consumed(&self) -> u64 {
(self.gas_base.get() as i64).saturating_sub(self.gas_remaining.get()) as u64
}
pub fn free_gas_consumed(&self) -> u64 {
self.free_gas_consumed.get()
}
pub fn remaining(&self) -> i64 {
self.gas_remaining.get()
}
pub fn base(&self) -> u64 {
self.gas_base.get()
}
pub fn limit(&self) -> u64 {
self.gas_limit.get()
}
pub fn set_limit(&self, limit: u64) {
let limit = std::cmp::min(limit, self.gas_max);
vm_log_trace!("changing gas limit: new_limit={limit}");
self.gas_credit.set(0);
self.gas_limit.set(limit);
self.set_base(limit);
}
fn set_base(&self, mut base: u64) {
base = truncate_gas(base);
let diff = base as i64 - self.gas_base.get() as i64;
self.gas_remaining.set(self.gas_remaining.get() + diff);
self.gas_base.set(base);
}
pub fn price(&self) -> u64 {
self.gas_price.get()
}
pub fn try_get_extra_balance_consumer(&'l self) -> Option<LimitedGasConsumer<'l>> {
self.get_extra_balance_counter
.set(self.get_extra_balance_counter.get() + 1);
if self.get_extra_balance_counter.get() <= Self::CHEAP_GET_BALANCE_CALLS {
return Some(self.limited(Self::CHEAP_GET_BALANCE_GAS_THRESHOLD));
}
None
}
pub fn try_consume_exception_gas(&self) -> Result<(), Error> {
self.try_consume(Self::EXCEPTION_GAS_PRICE)
}
pub fn try_consume_implicit_jmpref_gas(&self) -> Result<(), Error> {
self.try_consume(Self::IMPLICIT_JMPREF_GAS_PRICE)
}
pub fn try_consume_implicit_ret_gas(&self) -> Result<(), Error> {
self.try_consume(Self::IMPLICIT_RET_GAS_PRICE)
}
pub fn try_consume_check_signature_gas(&self) -> Result<(), Error> {
self.chksign_counter.set(self.chksign_counter.get() + 1);
if self.chksign_counter.get() > Self::FREE_SIGNATURE_CHECKS {
self.try_consume(Self::CHK_SGN_GAS_PRICE)?;
} else {
self.consume_free_gas(Self::CHK_SGN_GAS_PRICE);
}
Ok(())
}
pub fn try_consume_stack_gas(&self, stack: Option<&SafeRc<Stack>>) -> Result<(), Error> {
if let Some(stack) = stack {
self.try_consume_stack_depth_gas(stack.depth())?;
}
Ok(())
}
pub fn try_consume_tuple_gas(&self, tuple_len: u64) -> Result<(), Error> {
self.try_consume(tuple_len * Self::TUPLE_ENTRY_GAS_PRICE)?;
Ok(())
}
pub fn try_consume_stack_depth_gas(&self, depth: usize) -> Result<(), Error> {
self.try_consume(
(std::cmp::max(depth, Self::FREE_STACK_DEPTH) - Self::FREE_STACK_DEPTH) as u64
* Self::STACK_VALUE_GAS_PRICE,
)
}
pub fn try_consume(&self, amount: u64) -> Result<(), Error> {
let remaining = self
.gas_remaining
.get()
.saturating_sub(truncate_gas(amount) as i64);
self.gas_remaining.set(remaining);
if remaining >= 0 {
Ok(())
} else {
Err(Error::Cancelled)
}
}
pub fn consume_free_gas(&self, amount: u64) {
let consumed = truncate_gas(self.free_gas_consumed.get().saturating_add(amount));
self.free_gas_consumed.set(consumed);
}
pub fn missing_library(&self) -> Option<HashBytes> {
self.missing_library.get()
}
pub fn set_missing_library(&self, hash: &HashBytes) {
self.missing_library.set(Some(*hash));
}
pub fn load_cell_as_slice(&self, cell: Cell, mode: LoadMode) -> Result<OwnedCellSlice, Error> {
let cell = ok!(self.load_cell_impl(cell, mode));
Ok(OwnedCellSlice::new_allow_exotic(cell))
}
fn load_cell_impl<'s: 'a, 'a, T: LoadLibrary<'a>>(
&'s self,
mut cell: T,
mode: LoadMode,
) -> Result<T, Error> {
let mut library_loaded = false;
loop {
if mode.use_gas() && !library_loaded {
let is_new =
unsafe { (*self.loaded_cells.get()).insert(*cell.as_ref().repr_hash()) };
ok!(self.try_consume(if is_new {
GasConsumer::NEW_CELL_GAS
} else {
GasConsumer::OLD_CELL_GAS
}));
}
if !mode.resolve() {
return Ok(cell);
}
match cell.as_ref().cell_type() {
CellType::Ordinary => return Ok(cell),
CellType::LibraryReference if !library_loaded => {
debug_assert_eq!(cell.as_ref().bit_len(), 8 + 256);
let mut library_hash = HashBytes::ZERO;
ok!(cell
.as_ref()
.as_slice_allow_exotic()
.get_raw(8, &mut library_hash.0, 256));
let Some(library_cell) = ok!(T::load_library(self, &library_hash)) else {
self.missing_library.set(Some(library_hash));
return Err(Error::CellUnderflow);
};
cell = library_cell;
library_loaded = true;
}
_ => return Err(Error::CellUnderflow),
}
}
}
}
impl CellContext for GasConsumer<'_> {
fn finalize_cell(&self, cell: CellParts<'_>) -> Result<Cell, Error> {
ok!(self.try_consume(GasConsumer::BUILD_CELL_GAS));
Cell::empty_context().finalize_cell(cell)
}
fn load_cell(&self, cell: Cell, mode: LoadMode) -> Result<Cell, Error> {
self.load_cell_impl(cell, mode)
}
fn load_dyn_cell<'s: 'a, 'a>(
&'s self,
cell: &'a DynCell,
mode: LoadMode,
) -> Result<&'a DynCell, Error> {
self.load_cell_impl(cell, mode)
}
}
pub struct LimitedGasConsumer<'l> {
gas: &'l GasConsumer<'l>,
remaining: std::cell::Cell<u64>,
}
impl CellContext for LimitedGasConsumer<'_> {
fn finalize_cell(&self, _: CellParts<'_>) -> Result<Cell, Error> {
panic!("limited gas consumer must not be used for making new cells")
}
fn load_cell(&self, cell: Cell, mode: LoadMode) -> Result<Cell, Error> {
self.load_cell_impl(cell, mode)
}
fn load_dyn_cell<'s: 'a, 'a>(
&'s self,
cell: &'a DynCell,
mode: LoadMode,
) -> Result<&'a DynCell, Error> {
self.load_cell_impl(cell, mode)
}
}
impl LimitedGasConsumer<'_> {
fn load_cell_impl<'s: 'a, 'a, T: LoadLibrary<'a>>(
&'s self,
mut cell: T,
mode: LoadMode,
) -> Result<T, Error> {
let mut library_loaded = false;
loop {
if mode.use_gas() && !library_loaded {
let is_new =
unsafe { (*self.gas.loaded_cells.get()).insert(*cell.as_ref().repr_hash()) };
ok!(self.try_consume(if is_new {
GasConsumer::NEW_CELL_GAS
} else {
GasConsumer::OLD_CELL_GAS
}));
}
if !mode.resolve() {
return Ok(cell);
}
match cell.as_ref().cell_type() {
CellType::Ordinary => return Ok(cell),
CellType::LibraryReference if !library_loaded => {
debug_assert_eq!(cell.as_ref().bit_len(), 8 + 256);
let mut library_hash = HashBytes::ZERO;
ok!(cell
.as_ref()
.as_slice_allow_exotic()
.get_raw(8, &mut library_hash.0, 256));
let Some(library_cell) = ok!(T::load_library(self.gas, &library_hash)) else {
self.gas.missing_library.set(Some(library_hash));
return Err(Error::CellUnderflow);
};
cell = library_cell;
library_loaded = true;
}
_ => return Err(Error::CellUnderflow),
}
}
}
pub fn try_consume(&self, mut amount: u64) -> Result<(), Error> {
amount = truncate_gas(amount);
let mut remaining = self.remaining.get();
let consumed = std::cmp::min(amount, remaining);
self.gas.try_consume(consumed)?;
remaining -= consumed;
self.remaining.set(remaining);
if remaining == 0 {
self.gas.consume_free_gas(amount - consumed);
}
Ok(())
}
}
trait LoadLibrary<'a>: AsRef<DynCell> + 'a {
fn load_library(gas: &'a GasConsumer, library_hash: &HashBytes) -> Result<Option<Self>, Error>
where
Self: Sized;
}
impl<'a> LoadLibrary<'a> for &'a DynCell {
fn load_library(gas: &'a GasConsumer, library_hash: &HashBytes) -> Result<Option<Self>, Error>
where
Self: Sized,
{
gas.libraries.find_ref(library_hash)
}
}
impl LoadLibrary<'_> for Cell {
fn load_library(gas: &'_ GasConsumer, library_hash: &HashBytes) -> Result<Option<Self>, Error>
where
Self: Sized,
{
gas.libraries.find(library_hash)
}
}
#[derive(Debug, Clone, Copy)]
pub struct GasConsumerDeriveParams {
pub gas_max: u64,
pub gas_limit: u64,
pub isolate: bool,
}
pub enum ParentGasConsumer<'l> {
Isolated(GasConsumer<'l>),
Shared(GasConsumer<'l>),
}
#[derive(Debug, Clone, Copy)]
pub struct RestoredGasConsumer {
pub gas_consumed: u64,
pub gas_limit: u64,
}
const fn truncate_gas(gas: u64) -> u64 {
if gas <= i64::MAX as u64 {
gas
} else {
i64::MAX as u64
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn find_lib_dict_ref() {
let lib1 = Boc::decode(tvmasm!("NOP")).unwrap();
let lib2 = Boc::decode(tvmasm!("NOP NOP")).unwrap();
let mut libraries = vec![
(*lib1.repr_hash(), SimpleLib {
public: true,
root: lib1.clone(),
}),
(*lib2.repr_hash(), SimpleLib {
public: true,
root: lib2.clone(),
}),
];
libraries.sort_unstable_by_key(|(l, _)| *l);
let libraries = Dict::<HashBytes, SimpleLib>::try_from_sorted_slice(&libraries).unwrap();
assert!(libraries.find(&HashBytes::ZERO).unwrap().is_none());
assert!(libraries.find_ref(&HashBytes::ZERO).unwrap().is_none());
assert_eq!(
libraries.find(lib1.repr_hash()).unwrap().unwrap().as_ref(),
libraries.find_ref(lib1.repr_hash()).unwrap().unwrap()
);
assert_eq!(
libraries.find(lib2.repr_hash()).unwrap().unwrap().as_ref(),
libraries.find_ref(lib2.repr_hash()).unwrap().unwrap()
);
let mut publishers = Dict::new();
publishers.add(HashBytes::ZERO, ()).unwrap();
{
let lib = LibDescr {
lib: lib1.clone(),
publishers: publishers.clone(),
};
let c = CellBuilder::build_from(&lib).unwrap();
let parsed = c.parse::<LibDescr>().unwrap();
assert_eq!(lib, parsed);
}
let mut libraries = vec![
(*lib1.repr_hash(), LibDescr {
lib: lib1.clone(),
publishers: publishers.clone(),
}),
(*lib2.repr_hash(), LibDescr {
lib: lib2.clone(),
publishers,
}),
];
libraries.sort_unstable_by_key(|(l, _)| *l);
let libraries = Dict::<HashBytes, LibDescr>::try_from_sorted_slice(&libraries).unwrap();
assert!(libraries.find(&HashBytes::ZERO).unwrap().is_none());
assert!(libraries.find_ref(&HashBytes::ZERO).unwrap().is_none());
assert_eq!(
libraries.find(lib1.repr_hash()).unwrap().unwrap().as_ref(),
libraries.find_ref(lib1.repr_hash()).unwrap().unwrap()
);
assert_eq!(
libraries.find(lib2.repr_hash()).unwrap().unwrap().as_ref(),
libraries.find_ref(lib2.repr_hash()).unwrap().unwrap()
);
}
}