#[cfg(feature = "p7")]
use crate::vec;
use crate::{
cell::UnsafeCell,
convert::{self, TryInto},
fmt,
marker::PhantomData,
mem::{self, MaybeUninit},
num,
num::NonZeroU32,
prims, state_btree,
traits::*,
types::*,
vec::Vec,
String,
};
pub(crate) use concordium_contracts_common::*;
impl convert::From<()> for Reject {
#[inline(always)]
fn from(_: ()) -> Self { unsafe { num::NonZeroI32::new_unchecked(i32::MIN + 1) }.into() }
}
impl convert::From<ParseError> for Reject {
#[inline(always)]
fn from(_: ParseError) -> Self {
unsafe { num::NonZeroI32::new_unchecked(i32::MIN + 2) }.into()
}
}
impl From<LogError> for Reject {
#[inline(always)]
fn from(le: LogError) -> Self {
match le {
LogError::Full => unsafe { crate::num::NonZeroI32::new_unchecked(i32::MIN + 3) }.into(),
LogError::Malformed => {
unsafe { crate::num::NonZeroI32::new_unchecked(i32::MIN + 4) }.into()
}
}
}
}
impl From<NewContractNameError> for Reject {
fn from(nre: NewContractNameError) -> Self {
match nre {
NewContractNameError::MissingInitPrefix => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 5).into()
},
NewContractNameError::TooLong => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 6).into()
},
NewContractNameError::ContainsDot => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 9).into()
},
NewContractNameError::InvalidCharacters => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 10).into()
},
}
}
}
impl From<NewReceiveNameError> for Reject {
fn from(nre: NewReceiveNameError) -> Self {
match nre {
NewReceiveNameError::MissingDotSeparator => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 7).into()
},
NewReceiveNameError::TooLong => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 8).into()
},
NewReceiveNameError::InvalidCharacters => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 11).into()
},
}
}
}
impl From<NotPayableError> for Reject {
#[inline(always)]
fn from(_: NotPayableError) -> Self {
unsafe { crate::num::NonZeroI32::new_unchecked(i32::MIN + 12) }.into()
}
}
impl From<TransferError> for Reject {
#[inline(always)]
fn from(te: TransferError) -> Self {
match te {
TransferError::AmountTooLarge => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 13).into()
},
TransferError::MissingAccount => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 14).into()
},
}
}
}
impl<T> From<CallContractError<T>> for Reject {
#[inline(always)]
fn from(cce: CallContractError<T>) -> Self {
match cce {
CallContractError::AmountTooLarge => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 15).into()
},
CallContractError::MissingAccount => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 16).into()
},
CallContractError::MissingContract => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 17).into()
},
CallContractError::MissingEntrypoint => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 18).into()
},
CallContractError::MessageFailed => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 19).into()
},
CallContractError::LogicReject {
..
} => unsafe { crate::num::NonZeroI32::new_unchecked(i32::MIN + 20).into() },
CallContractError::Trap => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 21).into()
},
}
}
}
impl From<UpgradeError> for Reject {
#[inline(always)]
fn from(te: UpgradeError) -> Self {
match te {
UpgradeError::MissingModule => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 22).into()
},
UpgradeError::MissingContract => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 23).into()
},
UpgradeError::UnsupportedModuleVersion => unsafe {
crate::num::NonZeroI32::new_unchecked(i32::MIN + 24).into()
},
}
}
}
impl From<QueryAccountBalanceError> for Reject {
#[inline(always)]
fn from(_: QueryAccountBalanceError) -> Self {
unsafe { crate::num::NonZeroI32::new_unchecked(i32::MIN + 25).into() }
}
}
impl From<QueryContractBalanceError> for Reject {
#[inline(always)]
fn from(_: QueryContractBalanceError) -> Self {
unsafe { crate::num::NonZeroI32::new_unchecked(i32::MIN + 26).into() }
}
}
impl Write for ExternReturnValue {
type Err = ();
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Err> {
let len: u32 = {
match buf.len().try_into() {
Ok(v) => v,
_ => return Err(()),
}
};
if self.current_position.checked_add(len).is_none() {
return Err(());
}
let num_bytes = unsafe { prims::write_output(buf.as_ptr(), len, self.current_position) };
self.current_position += num_bytes; Ok(num_bytes as usize)
}
}
impl ExternReturnValue {
#[inline(always)]
pub fn open() -> Self {
Self {
current_position: 0,
}
}
}
impl StateEntry {
pub(crate) fn open(state_entry_id: StateEntryId, key: Vec<u8>) -> Self {
Self {
state_entry_id,
key,
current_position: 0,
}
}
}
impl HasStateEntry for StateEntry {
type Error = ();
type StateEntryData = ();
type StateEntryKey = ();
#[inline(always)]
fn move_to_start(&mut self) { self.current_position = 0; }
#[inline(always)]
fn size(&self) -> Result<u32, Self::Error> {
let res = unsafe { prims::state_entry_size(self.state_entry_id) };
match res {
u32::MAX => Err(()),
_ => Ok(res),
}
}
fn truncate(&mut self, new_size: u32) -> Result<(), Self::Error> {
let cur_size = self.size()?;
if cur_size > new_size {
self.resize(new_size)?;
}
Ok(())
}
fn get_key(&self) -> &[u8] { &self.key }
fn resize(&mut self, new_size: u32) -> Result<(), Self::Error> {
let res = unsafe { prims::state_entry_resize(self.state_entry_id, new_size) };
match res {
1 => {
if self.current_position > new_size {
self.current_position = new_size;
}
Ok(())
}
_ => Err(()),
}
}
}
impl Seek for StateEntry {
type Err = ();
#[inline]
fn seek(&mut self, pos: SeekFrom) -> Result<u32, Self::Err> {
use SeekFrom::*;
let end = self.size()?;
match pos {
Start(offset) => {
if offset <= end {
self.current_position = offset;
Ok(offset)
} else {
Err(())
}
}
End(delta) => {
if delta > 0 {
Err(()) } else {
let new_offset = end.wrapping_add(delta as u32);
if new_offset <= end {
self.current_position = new_offset;
Ok(new_offset)
} else {
Err(())
}
}
}
Current(delta) => {
let new_offset = self.current_position + delta as u32;
if new_offset <= end {
self.current_position = new_offset;
Ok(new_offset)
} else {
Err(())
}
}
}
}
#[inline(always)]
fn cursor_position(&self) -> u32 { self.current_position }
}
impl Read for StateEntry {
fn read(&mut self, buf: &mut [u8]) -> ParseResult<usize> {
let len: u32 = buf.len().try_into().map_err(|_| ParseError::default())?;
let num_read = unsafe {
prims::state_entry_read(
self.state_entry_id,
buf.as_mut_ptr(),
len,
self.current_position,
)
};
if num_read == u32::MAX {
return Err(ParseError::default()); }
self.current_position += num_read;
Ok(num_read as usize)
}
fn read_u64(&mut self) -> ParseResult<u64> {
let mut bytes: MaybeUninit<[u8; 8]> = MaybeUninit::uninit();
let num_read = unsafe {
prims::state_entry_read(
self.state_entry_id,
bytes.as_mut_ptr() as *mut u8,
8,
self.current_position,
)
};
if num_read == u32::MAX {
return Err(ParseError::default()); }
self.current_position += num_read;
if num_read == 8 {
unsafe { Ok(u64::from_le_bytes(bytes.assume_init())) }
} else {
Err(ParseError::default())
}
}
fn read_u32(&mut self) -> ParseResult<u32> {
let mut bytes: MaybeUninit<[u8; 4]> = MaybeUninit::uninit();
let num_read = unsafe {
prims::state_entry_read(
self.state_entry_id,
bytes.as_mut_ptr() as *mut u8,
4,
self.current_position,
)
};
if num_read == u32::MAX {
return Err(ParseError::default()); }
self.current_position += num_read;
if num_read == 4 {
unsafe { Ok(u32::from_le_bytes(bytes.assume_init())) }
} else {
Err(ParseError::default())
}
}
fn read_u8(&mut self) -> ParseResult<u8> {
let mut bytes: MaybeUninit<[u8; 1]> = MaybeUninit::uninit();
let num_read = unsafe {
prims::state_entry_read(
self.state_entry_id,
bytes.as_mut_ptr() as *mut u8,
1,
self.current_position,
)
};
if num_read == u32::MAX {
return Err(ParseError::default()); }
self.current_position += num_read;
if num_read == 1 {
unsafe { Ok(bytes.assume_init()[0]) }
} else {
Err(ParseError::default())
}
}
}
impl Write for StateEntry {
type Err = ();
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Err> {
let len: u32 = {
match buf.len().try_into() {
Ok(v) => v,
_ => return Err(()),
}
};
if self.current_position.checked_add(len).is_none() {
return Err(());
}
let num_bytes = unsafe {
prims::state_entry_write(self.state_entry_id, buf.as_ptr(), len, self.current_position)
};
if num_bytes == u32::MAX {
return Err(()); }
self.current_position += num_bytes; Ok(num_bytes as usize)
}
}
impl<StateApi: HasStateApi> VacantEntryRaw<StateApi> {
pub(crate) fn new(key: Key, state_api: StateApi) -> Self {
Self {
key,
state_api,
}
}
#[inline(always)]
pub fn key(&self) -> &[u8] { &self.key }
pub fn insert_raw(mut self, value: &[u8]) -> Result<StateApi::EntryType, StateError> {
let mut entry = self.state_api.create_entry(&self.key)?;
entry.write_all(value).unwrap_abort(); entry.move_to_start(); Ok(entry)
}
pub fn insert<V: Serial>(mut self, value: &V) -> Result<StateApi::EntryType, StateError> {
let mut entry = self.state_api.create_entry(&self.key)?;
value.serial(&mut entry).unwrap_abort();
entry.move_to_start(); Ok(entry)
}
}
impl<StateApi: HasStateApi> OccupiedEntryRaw<StateApi> {
pub(crate) fn new(state_entry: StateApi::EntryType) -> Self {
Self {
state_entry,
}
}
#[inline(always)]
pub fn key(&self) -> &[u8] { self.state_entry.get_key() }
#[inline(always)]
pub fn get_ref(&self) -> &StateApi::EntryType { &self.state_entry }
#[inline(always)]
pub fn get(self) -> StateApi::EntryType { self.state_entry }
#[inline(always)]
pub fn get_mut(&mut self) -> &mut StateApi::EntryType { &mut self.state_entry }
pub fn insert_raw(&mut self, value: &[u8]) {
self.state_entry.move_to_start();
self.state_entry.write_all(value).unwrap_abort();
self.state_entry.truncate(value.len() as u32).unwrap_abort();
}
pub fn insert<V: Serial>(&mut self, value: &V) {
self.state_entry.truncate(0).unwrap_abort();
self.state_entry.move_to_start();
value.serial(&mut self.state_entry).unwrap_abort()
}
}
impl<StateApi: HasStateApi> EntryRaw<StateApi> {
pub fn or_insert_raw(self, default: &[u8]) -> Result<StateApi::EntryType, StateError> {
match self {
EntryRaw::Vacant(vac) => vac.insert_raw(default),
EntryRaw::Occupied(occ) => Ok(occ.get()),
}
}
pub fn or_insert<V: Serial>(self, default: &V) -> StateApi::EntryType {
match self {
EntryRaw::Vacant(vac) => vac.insert(default).unwrap_abort(),
EntryRaw::Occupied(occ) => occ.get(),
}
}
pub fn key(&self) -> &[u8] {
match self {
EntryRaw::Vacant(vac) => vac.key(),
EntryRaw::Occupied(occ) => occ.key(),
}
}
}
impl<'a, K, V, StateApi> VacantEntry<'a, K, V, StateApi>
where
K: Serial,
V: Serial,
StateApi: HasStateApi,
{
pub(crate) fn new(key: K, key_bytes: Vec<u8>, state_api: StateApi) -> Self {
Self {
key,
key_bytes,
state_api,
_lifetime_marker: PhantomData,
}
}
#[inline(always)]
pub fn key(&self) -> &K { &self.key }
#[inline(always)]
pub fn into_key(self) -> K { self.key }
pub fn insert(mut self, value: V) -> OccupiedEntry<'a, K, V, StateApi> {
let mut state_entry = self.state_api.create_entry(&self.key_bytes).unwrap_abort();
value.serial(&mut state_entry).unwrap_abort();
state_entry.move_to_start(); OccupiedEntry {
key: self.key,
value,
modified: false,
state_entry,
_lifetime_marker: self._lifetime_marker,
}
}
}
impl<'a, K, V, StateApi> OccupiedEntry<'a, K, V, StateApi>
where
K: Serial,
V: Serial,
StateApi: HasStateApi,
{
pub(crate) fn new(key: K, value: V, state_entry: StateApi::EntryType) -> Self {
Self {
key,
value,
modified: false,
state_entry,
_lifetime_marker: PhantomData,
}
}
#[inline(always)]
pub fn key(&self) -> &K { &self.key }
#[inline(always)]
pub fn get_ref(&self) -> &V { &self.value }
#[inline]
pub fn modify<F, A>(&mut self, f: F) -> A
where
F: FnOnce(&mut V) -> A, {
let res = f(&mut self.value);
self.store_value();
res
}
pub fn try_modify<F, A, E>(&mut self, f: F) -> Result<A, E>
where
F: FnOnce(&mut V) -> Result<A, E>, {
let res = f(&mut self.value)?;
self.store_value();
Ok(res)
}
}
impl<'a, K, V, StateApi> OccupiedEntry<'a, K, V, StateApi>
where
V: Serial,
StateApi: HasStateApi,
{
pub(crate) fn store_value(&mut self) {
self.state_entry.truncate(0).unwrap_abort();
self.value.serial(&mut self.state_entry).unwrap_abort();
}
}
impl<'a, K, V, StateApi> Entry<'a, K, V, StateApi>
where
K: Serial,
V: Serial,
StateApi: HasStateApi,
{
#[inline(always)]
pub fn is_vacant(&self) -> bool { matches!(self, Entry::Vacant(_)) }
#[inline(always)]
pub fn is_occupied(&self) -> bool { matches!(self, Entry::Occupied(_)) }
#[inline]
pub fn occupied_or<E>(self, e: E) -> Result<OccupiedEntry<'a, K, V, StateApi>, E> {
match self {
Entry::Vacant(_) => Err(e),
Entry::Occupied(oe) => Ok(oe),
}
}
#[inline]
pub fn vacant_or<E>(self, e: E) -> Result<VacantEntry<'a, K, V, StateApi>, E> {
match self {
Entry::Vacant(vac) => Ok(vac),
Entry::Occupied(_) => Err(e),
}
}
pub fn or_insert(self, value: V) -> OccupiedEntry<'a, K, V, StateApi> {
match self {
Entry::Vacant(vac) => vac.insert(value),
Entry::Occupied(oe) => oe,
}
}
pub fn or_insert_with<F>(self, default: F) -> OccupiedEntry<'a, K, V, StateApi>
where
F: FnOnce() -> V, {
match self {
Entry::Vacant(vac) => vac.insert(default()),
Entry::Occupied(oe) => oe,
}
}
pub fn and_try_modify<F, E>(mut self, f: F) -> Result<Entry<'a, K, V, StateApi>, E>
where
F: FnOnce(&mut V) -> Result<(), E>, {
if let Entry::Occupied(ref mut occ) = self {
occ.try_modify(f)?;
}
Ok(self)
}
pub fn and_modify<F>(mut self, f: F) -> Entry<'a, K, V, StateApi>
where
F: FnOnce(&mut V), {
if let Entry::Occupied(ref mut occ) = self {
occ.modify(f);
}
self
}
pub fn key(&self) -> &K {
match self {
Entry::Vacant(vac) => vac.key(),
Entry::Occupied(occ) => occ.key(),
}
}
}
impl<'a, K, V, StateApi> Entry<'a, K, V, StateApi>
where
K: Serial,
V: Serial + Default,
StateApi: HasStateApi,
{
#[allow(clippy::unwrap_or_default)]
pub fn or_default(self) -> OccupiedEntry<'a, K, V, StateApi> {
self.or_insert_with(Default::default)
}
}
const NEXT_ITEM_PREFIX_KEY: [u8; 8] = 0u64.to_le_bytes();
pub(crate) const INITIAL_NEXT_ITEM_PREFIX: [u8; 8] = 2u64.to_le_bytes();
impl HasStateApi for ExternStateApi {
type EntryType = StateEntry;
type IterType = ExternStateIter;
fn create_entry(&mut self, key: &[u8]) -> Result<Self::EntryType, StateError> {
let key_start = key.as_ptr();
let key_len = key.len() as u32; let entry_id = unsafe { prims::state_create_entry(key_start, key_len) };
if entry_id == u64::MAX {
return Err(StateError::SubtreeLocked);
}
Ok(StateEntry::open(entry_id, key.to_vec()))
}
fn lookup_entry(&self, key: &[u8]) -> Option<Self::EntryType> {
let key_start = key.as_ptr();
let key_len = key.len() as u32; let entry_id = unsafe { prims::state_lookup_entry(key_start, key_len) };
if entry_id == u64::MAX {
None
} else {
Some(StateEntry::open(entry_id, key.to_vec()))
}
}
fn delete_entry(&mut self, entry: Self::EntryType) -> Result<(), StateError> {
let key = entry.get_key();
let res = unsafe { prims::state_delete_entry(key.as_ptr(), key.len() as u32) };
match res {
0 => Err(StateError::SubtreeLocked),
1 => Err(StateError::EntryNotFound),
2 => Ok(()),
_ => crate::trap(), }
}
fn delete_prefix(&mut self, prefix: &[u8]) -> Result<bool, StateError> {
let res = unsafe { prims::state_delete_prefix(prefix.as_ptr(), prefix.len() as u32) };
match res {
0 => Err(StateError::SubtreeLocked),
1 => Ok(false),
2 => Ok(true),
_ => crate::trap(), }
}
fn iterator(&self, prefix: &[u8]) -> Result<Self::IterType, StateError> {
let prefix_start = prefix.as_ptr();
let prefix_len = prefix.len() as u32; let iterator_id = unsafe { prims::state_iterate_prefix(prefix_start, prefix_len) };
match iterator_id {
OK_NONE => Err(StateError::SubtreeWithPrefixNotFound),
ERR => Err(StateError::IteratorLimitForPrefixExceeded),
iterator_id => Ok(ExternStateIter {
iterator_id,
}),
}
}
fn delete_iterator(&mut self, iter: Self::IterType) {
unsafe { prims::state_iterator_delete(iter.iterator_id) };
}
}
const OK_NONE: u64 = u64::MAX;
const ERR: u64 = u64::MAX & !(1u64 << 62);
impl Iterator for ExternStateIter {
type Item = StateEntry;
fn next(&mut self) -> Option<Self::Item> {
let res = unsafe { prims::state_iterator_next(self.iterator_id) };
match res {
OK_NONE => None,
ERR => None,
_ => {
let key_size = unsafe { prims::state_iterator_key_size(self.iterator_id) };
let mut key = Vec::with_capacity(key_size as usize);
unsafe {
let num_read = prims::state_iterator_key_read(
self.iterator_id,
key.as_mut_ptr(),
key_size,
0,
);
key.set_len(num_read as usize);
};
Some(StateEntry::open(res, key))
}
}
}
}
impl<K, V, S> StateMap<K, V, S>
where
S: HasStateApi,
K: Serialize,
V: Serial + DeserialWithState<S>,
{
pub fn get(&self, key: &K) -> Option<StateRef<V>> {
let k = self.key_with_map_prefix(key);
self.state_api.lookup_entry(&k).map(|mut entry| {
StateRef::new(V::deserial_with_state(&self.state_api, &mut entry).unwrap_abort())
})
}
pub fn get_mut(&mut self, key: &K) -> Option<StateRefMut<V, S>> {
let k = self.key_with_map_prefix(key);
let entry = self.state_api.lookup_entry(&k)?;
Some(StateRefMut::new(entry, self.state_api.clone()))
}
pub(crate) fn insert_borrowed(&mut self, key: &K, value: V) -> Option<V> {
let key_bytes = self.key_with_map_prefix(key);
match self.state_api.entry(key_bytes) {
EntryRaw::Vacant(vac) => {
let _ = vac.insert(&value).unwrap_abort();
None
}
EntryRaw::Occupied(mut occ) => {
let old_value =
V::deserial_with_state(&self.state_api, occ.get_mut()).unwrap_abort();
occ.insert(&value);
Some(old_value)
}
}
}
#[must_use]
pub fn insert(&mut self, key: K, value: V) -> Option<V> { self.insert_borrowed(&key, value) }
pub fn entry(&mut self, key: K) -> Entry<'_, K, V, S> {
let key_bytes = self.key_with_map_prefix(&key);
match self.state_api.lookup_entry(&key_bytes) {
None => Entry::Vacant(VacantEntry::new(key, key_bytes, self.state_api.clone())),
Some(mut state_entry) => {
let value =
V::deserial_with_state(&self.state_api, &mut state_entry).unwrap_abort();
Entry::Occupied(OccupiedEntry::new(key, value, state_entry))
}
}
}
pub fn is_empty(&self) -> bool { self.state_api.lookup_entry(&self.prefix).is_none() }
pub fn clear(&mut self)
where
V: Deletable, {
for (_, value) in self.iter() {
value.value.delete()
}
self.state_api.delete_prefix(&self.prefix).unwrap_abort();
}
pub fn clear_flat(&mut self)
where
V: Deserial, {
self.state_api.delete_prefix(&self.prefix).unwrap_abort();
}
#[must_use]
pub fn remove_and_get(&mut self, key: &K) -> Option<V> {
let key_bytes = self.key_with_map_prefix(key);
let entry_raw = self.state_api.entry(key_bytes);
match entry_raw {
EntryRaw::Vacant(_) => None,
EntryRaw::Occupied(mut occ) => {
let old_value =
V::deserial_with_state(&self.state_api, occ.get_mut()).unwrap_abort();
let _existed = self.state_api.delete_entry(occ.state_entry);
Some(old_value)
}
}
}
pub fn remove(&mut self, key: &K)
where
V: Deletable, {
if let Some(v) = self.remove_and_get(key) {
v.delete()
}
}
fn key_with_map_prefix(&self, key: &K) -> Vec<u8> {
let mut key_with_prefix = self.prefix.to_vec();
key.serial(&mut key_with_prefix).unwrap_abort();
key_with_prefix
}
}
impl<'a, K, V, S: HasStateApi> Drop for StateMapIter<'a, K, V, S> {
fn drop(&mut self) {
if let Some(valid) = self.state_iter.take() {
self.state_api.delete_iterator(valid);
}
}
}
impl<K, V, S> StateMap<K, V, S>
where
S: HasStateApi,
{
pub(crate) fn open(state_api: S, prefix: [u8; 8]) -> Self {
Self {
_marker_key: PhantomData,
_marker_value: PhantomData,
prefix,
state_api,
}
}
pub fn iter(&self) -> StateMapIter<'_, K, V, S> {
match self.state_api.iterator(&self.prefix) {
Ok(state_iter) => StateMapIter {
state_iter: Some(state_iter),
state_api: self.state_api.clone(),
_lifetime_marker: PhantomData,
},
Err(StateError::SubtreeWithPrefixNotFound) => StateMapIter {
state_iter: None,
state_api: self.state_api.clone(),
_lifetime_marker: PhantomData,
},
_ => crate::trap(),
}
}
pub fn iter_mut(&mut self) -> StateMapIterMut<'_, K, V, S> {
match self.state_api.iterator(&self.prefix) {
Ok(state_iter) => StateMapIterMut {
state_iter: Some(state_iter),
state_api: self.state_api.clone(),
_lifetime_marker: PhantomData,
},
Err(StateError::SubtreeWithPrefixNotFound) => StateMapIterMut {
state_iter: None,
state_api: self.state_api.clone(),
_lifetime_marker: PhantomData,
},
_ => crate::trap(),
}
}
}
impl<'a, K, V, S: HasStateApi> Iterator for StateMapIter<'a, K, V, S>
where
K: Deserial + 'a,
V: DeserialWithState<S> + 'a,
{
type Item = (StateRef<'a, K>, StateRef<'a, V>);
fn next(&mut self) -> Option<Self::Item> {
let mut entry = self.state_iter.as_mut()?.next()?;
let key = entry.get_key();
let mut key_cursor = Cursor {
data: key,
offset: 8, };
let k = K::deserial(&mut key_cursor).unwrap_abort();
let v = V::deserial_with_state(&self.state_api, &mut entry).unwrap_abort();
Some((StateRef::new(k), StateRef::new(v)))
}
}
impl<'a, K, V: Serial, S: HasStateApi> Iterator for StateMapIterMut<'a, K, V, S>
where
K: Deserial + 'a,
V: DeserialWithState<S> + 'a,
S::EntryType: 'a,
{
type Item = (StateRef<'a, K>, StateRefMut<'a, V, S>);
fn next(&mut self) -> Option<Self::Item> {
let entry = self.state_iter.as_mut()?.next()?;
let key_bytes = entry.get_key();
let mut key_cursor = Cursor {
data: key_bytes,
offset: 8, };
let k = K::deserial(&mut key_cursor).unwrap_abort();
Some((StateRef::new(k), StateRefMut::new(entry, self.state_api.clone())))
}
}
impl<'a, S: HasStateApi, V: Serial + DeserialWithState<S>> crate::ops::Deref
for StateRefMut<'a, V, S>
{
type Target = V;
#[inline(always)]
fn deref(&self) -> &Self::Target { self.get() }
}
impl<'a, S: HasStateApi, V: Serial + DeserialWithState<S>> crate::ops::DerefMut
for StateRefMut<'a, V, S>
{
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target { self.get_mut() }
}
impl<'a, V: Serial, S: HasStateApi> Drop for StateRefMut<'a, V, S> {
fn drop(&mut self) { self.store_mutations() }
}
impl<'a, V, S> StateRefMut<'a, V, S>
where
V: Serial,
S: HasStateApi,
{
pub fn get(&self) -> &V
where
V: DeserialWithState<S>, {
let lv = unsafe { &mut *self.lazy_value.get() };
if let Some(v) = lv {
v
} else {
lv.insert(self.load_value())
}
}
pub fn get_mut(&mut self) -> &mut V
where
V: DeserialWithState<S>, {
let lv = unsafe { &mut *self.lazy_value.get() };
if let Some(v) = lv {
v
} else {
lv.insert(self.load_value())
}
}
fn load_value(&self) -> V
where
V: DeserialWithState<S>, {
let entry = unsafe { &mut *self.entry.get() };
entry.move_to_start();
V::deserial_with_state(&self.state_api, entry).unwrap_abort()
}
pub fn set(&mut self, new_val: V) {
let entry = self.entry.get_mut();
entry.move_to_start();
new_val.serial(entry).unwrap_abort();
let _ = self.lazy_value.get_mut().insert(new_val);
}
pub fn update<F>(&mut self, f: F)
where
V: DeserialWithState<S>,
F: FnOnce(&mut V), {
let lv = self.lazy_value.get_mut();
let entry = self.entry.get_mut();
let value = if let Some(v) = lv {
v
} else {
entry.move_to_start();
let value = V::deserial_with_state(&self.state_api, entry).unwrap_abort();
lv.insert(value)
};
f(value);
entry.move_to_start();
value.serial(entry).unwrap_abort()
}
pub(crate) fn store_mutations(&mut self) {
if let Some(value) = self.lazy_value.get_mut() {
let entry = self.entry.get_mut();
entry.move_to_start();
value.serial(entry).unwrap_abort();
}
}
pub(crate) fn drop_without_storing(mut self) { *self.lazy_value.get_mut() = None; }
}
impl<K, V, S> Serial for StateMap<K, V, S> {
fn serial<W: Write>(&self, out: &mut W) -> Result<(), W::Err> { out.write_all(&self.prefix) }
}
impl<T, S> StateSet<T, S>
where
T: Serialize,
S: HasStateApi,
{
pub fn insert(&mut self, value: T) -> bool {
let key_bytes = self.key_with_set_prefix(&value);
match self.state_api.entry(key_bytes) {
EntryRaw::Vacant(vac) => {
let _ = vac.insert_raw(&[]);
true
}
EntryRaw::Occupied(_) => false,
}
}
pub fn is_empty(&self) -> bool { self.state_api.lookup_entry(&self.prefix).is_none() }
pub fn contains(&self, value: &T) -> bool {
let key_bytes = self.key_with_set_prefix(value);
self.state_api.lookup_entry(&key_bytes).is_some()
}
pub fn clear(&mut self) {
self.state_api.delete_prefix(&self.prefix).unwrap_abort();
}
pub fn remove(&mut self, value: &T) -> bool {
let key_bytes = self.key_with_set_prefix(value);
match self.state_api.entry(key_bytes) {
EntryRaw::Vacant(_) => false,
EntryRaw::Occupied(occ) => {
self.state_api.delete_entry(occ.get()).unwrap_abort();
true
}
}
}
fn key_with_set_prefix(&self, key: &T) -> Vec<u8> {
let mut key_with_prefix = self.prefix.to_vec();
key.serial(&mut key_with_prefix).unwrap_abort();
key_with_prefix
}
}
impl<T, S: HasStateApi> StateSet<T, S> {
pub(crate) fn open(state_api: S, prefix: [u8; 8]) -> Self {
Self {
_marker: PhantomData,
prefix,
state_api,
}
}
pub fn iter(&self) -> StateSetIter<T, S> {
match self.state_api.iterator(&self.prefix) {
Ok(state_iter) => StateSetIter {
state_iter: Some(state_iter),
state_api: self.state_api.clone(),
_marker_lifetime: PhantomData,
},
Err(StateError::SubtreeWithPrefixNotFound) => StateSetIter {
state_iter: None,
state_api: self.state_api.clone(),
_marker_lifetime: PhantomData,
},
_ => crate::trap(),
}
}
}
impl<T: Serial, S: HasStateApi> StateBox<T, S> {
pub(crate) fn new(value: T, state_api: S, entry: S::EntryType) -> Self {
StateBox {
state_api,
inner: UnsafeCell::new(StateBoxInner::Loaded {
entry,
modified: true,
value,
}),
}
}
pub(crate) fn get_location(&self) -> &[u8] {
match unsafe { &*self.inner.get() } {
StateBoxInner::Loaded {
entry,
..
} => entry.get_key(),
StateBoxInner::Reference {
prefix,
} => &prefix[..],
}
}
}
impl<S: HasStateApi, T: Serial + DeserialWithState<S>> crate::ops::Deref for StateBox<T, S> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &Self::Target { self.get() }
}
impl<S: HasStateApi, T: Serial + DeserialWithState<S>> crate::ops::DerefMut for StateBox<T, S> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target { self.get_mut() }
}
impl<T: Serial, S: HasStateApi> Drop for StateBox<T, S> {
fn drop(&mut self) {
if let StateBoxInner::Loaded {
entry,
modified,
value,
} = self.inner.get_mut()
{
if *modified {
entry.move_to_start();
value.serial(entry).unwrap_abort();
}
}
}
}
fn get_with_inner<'a, T: Serial + DeserialWithState<S>, S: HasStateApi>(
state_api: &S,
inner: &'a mut StateBoxInner<T, S>,
) -> (&'a mut T, &'a mut bool) {
let (entry, value) = match inner {
StateBoxInner::Loaded {
value,
modified,
..
} => return (value, modified),
StateBoxInner::Reference {
prefix,
} => {
let mut entry = state_api.lookup_entry(prefix).unwrap_abort();
let value = T::deserial_with_state(state_api, &mut entry).unwrap_abort();
(entry, value)
}
};
*inner = StateBoxInner::Loaded {
entry,
modified: false,
value,
};
match inner {
StateBoxInner::Loaded {
value,
modified,
..
} => (value, modified),
StateBoxInner::Reference {
..
} => {
unsafe { crate::hint::unreachable_unchecked() }
}
}
}
impl<T, S> StateBox<T, S>
where
T: Serial + DeserialWithState<S>,
S: HasStateApi,
{
pub fn get(&self) -> &T {
let inner = unsafe { &mut *self.inner.get() };
get_with_inner(&self.state_api, inner).0
}
pub fn get_mut(&mut self) -> &mut T {
let inner = self.inner.get_mut();
let (value, modified) = get_with_inner(&self.state_api, inner);
*modified = true;
value
}
#[must_use]
pub fn replace(&mut self, new_val: T) -> T {
let (entry, value) = self.ensure_cached();
entry.move_to_start();
new_val.serial(entry).unwrap_abort();
mem::replace(value, new_val)
}
pub fn update<F, A>(&mut self, f: F) -> A
where
F: FnOnce(&mut T) -> A, {
let (entry, value) = self.ensure_cached();
let res = f(value);
entry.move_to_start();
value.serial(entry).unwrap_abort();
res
}
fn ensure_cached(&mut self) -> (&mut S::EntryType, &mut T) {
let inner = self.inner.get_mut();
let (entry, modified, value) = match inner {
StateBoxInner::Loaded {
entry,
value,
..
} => return (entry, value),
StateBoxInner::Reference {
prefix,
} => {
let mut entry = self.state_api.lookup_entry(prefix).unwrap_abort();
let value = T::deserial_with_state(&self.state_api, &mut entry).unwrap_abort();
(entry, false, value)
}
};
*inner = StateBoxInner::Loaded {
entry,
modified,
value,
};
match inner {
StateBoxInner::Loaded {
entry,
value,
..
} => (entry, value),
StateBoxInner::Reference {
..
} => {
unsafe { crate::hint::unreachable_unchecked() }
}
}
}
}
impl<T: Serial, S: HasStateApi> Serial for StateBox<T, S> {
fn serial<W: Write>(&self, out: &mut W) -> Result<(), W::Err> {
out.write_all(self.get_location())
}
}
impl<T, S> Serial for StateSet<T, S> {
fn serial<W: Write>(&self, out: &mut W) -> Result<(), W::Err> { out.write_all(&self.prefix) }
}
impl<'a, T, S: HasStateApi> Drop for StateSetIter<'a, T, S> {
#[inline]
fn drop(&mut self) {
if let Some(valid) = self.state_iter.take() {
self.state_api.delete_iterator(valid);
}
}
}
impl<'a, T, S: HasStateApi> Iterator for StateSetIter<'a, T, S>
where
T: DeserialWithState<S>,
{
type Item = StateRef<'a, T>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
let entry = self.state_iter.as_mut()?.next()?;
let key = entry.get_key();
let mut key_cursor = Cursor {
data: key,
offset: 8, };
let t = T::deserial_with_state(&self.state_api, &mut key_cursor).unwrap_abort();
Some(StateRef::new(t))
}
}
impl Default for ExternParameter {
#[inline(always)]
fn default() -> Self {
ExternParameter {
cursor: Cursor::new(ExternParameterDataPlaceholder {}),
}
}
}
impl Read for ExternParameter {
fn read(&mut self, buf: &mut [u8]) -> ParseResult<usize> {
let len: u32 = {
match buf.len().try_into() {
Ok(v) => v,
_ => return Err(ParseError::default()),
}
};
let num_read = unsafe {
prims::get_parameter_section(0, buf.as_mut_ptr(), len, self.cursor.offset as u32)
};
self.cursor.offset += num_read as usize;
Ok(num_read as usize)
}
}
impl HasSize for ExternParameterDataPlaceholder {
#[inline(always)]
fn size(&self) -> u32 { unsafe { prims::get_parameter_size(0) as u32 } }
}
impl HasSize for ExternParameter {
#[inline(always)]
fn size(&self) -> u32 { self.cursor.data.size() }
}
impl Seek for ExternParameter {
type Err = ();
#[inline(always)]
fn seek(&mut self, pos: SeekFrom) -> Result<u32, Self::Err> { self.cursor.seek(pos) }
#[inline(always)]
fn cursor_position(&self) -> u32 { self.cursor.cursor_position() }
}
impl HasParameter for ExternParameter {}
impl Read for ExternCallResponse {
fn read(&mut self, buf: &mut [u8]) -> ParseResult<usize> {
let len: u32 = {
match buf.len().try_into() {
Ok(v) => v,
_ => return Err(ParseError::default()),
}
};
let num_read = unsafe {
prims::get_parameter_section(
self.i.into(),
buf.as_mut_ptr(),
len,
self.current_position,
)
};
if num_read >= 0 {
self.current_position += num_read as u32;
Ok(num_read as usize)
} else {
Err(ParseError::default())
}
}
}
impl HasCallResponse for ExternCallResponse {
fn size(&self) -> u32 { unsafe { prims::get_parameter_size(self.i.get()) as u32 } }
}
impl HasChainMetadata for ExternChainMeta {
#[inline(always)]
fn slot_time(&self) -> SlotTime {
Timestamp::from_timestamp_millis(unsafe { prims::get_slot_time() })
}
}
impl AttributesCursor {
fn next_item(&mut self, buf: &mut [u8]) -> Option<(AttributeTag, u8)> {
if self.remaining_items == 0 {
return None;
}
let (tag_value_len, num_read) = unsafe {
let mut tag_value_len = MaybeUninit::<[u8; 2]>::uninit();
let num_read = prims::get_policy_section(
tag_value_len.as_mut_ptr() as *mut u8,
2,
self.current_position,
);
(tag_value_len.assume_init(), num_read)
};
self.current_position += num_read;
if tag_value_len[1] > 31 {
return None;
}
let num_read = unsafe {
prims::get_policy_section(
buf.as_mut_ptr(),
u32::from(tag_value_len[1]),
self.current_position,
)
};
self.current_position += num_read;
self.remaining_items -= 1;
Some((AttributeTag(tag_value_len[0]), tag_value_len[1]))
}
}
impl HasPolicy for Policy<AttributesCursor> {
type Iterator = PolicyAttributesIter;
fn identity_provider(&self) -> IdentityProvider { self.identity_provider }
fn created_at(&self) -> Timestamp { self.created_at }
fn valid_to(&self) -> Timestamp { self.valid_to }
#[inline(always)]
fn next_item(&mut self, buf: &mut [u8; 31]) -> Option<(AttributeTag, u8)> {
self.items.next_item(buf)
}
fn attributes(&self) -> Self::Iterator {
PolicyAttributesIter {
cursor: AttributesCursor {
current_position: 0,
remaining_items: self.items.total_items,
total_items: self.items.total_items,
},
}
}
}
impl Iterator for PolicyAttributesIter {
type Item = (AttributeTag, AttributeValue);
fn next(&mut self) -> Option<Self::Item> {
let mut inner = [0u8; 32];
let (tag, len) = self.cursor.next_item(&mut inner[1..])?;
inner[0] = len;
Some((tag, unsafe { AttributeValue::new_unchecked(inner) }))
}
}
impl ExactSizeIterator for PolicyAttributesIter {
fn len(&self) -> usize { self.cursor.remaining_items as usize }
}
pub struct PoliciesIterator {
pos: u32,
remaining_items: u16,
}
impl Iterator for PoliciesIterator {
type Item = Policy<AttributesCursor>;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining_items == 0 {
return None;
}
let mut buf: MaybeUninit<[u8; 2 + 4 + 8 + 8 + 2]> = MaybeUninit::uninit();
let buf = unsafe {
prims::get_policy_section(buf.as_mut_ptr() as *mut u8, 2 + 4 + 8 + 8 + 2, self.pos);
buf.assume_init()
};
let skip_part: [u8; 2] = buf[0..2].try_into().unwrap_abort();
let ip_part: [u8; 4] = buf[2..2 + 4].try_into().unwrap_abort();
let created_at_part: [u8; 8] = buf[2 + 4..2 + 4 + 8].try_into().unwrap_abort();
let valid_to_part: [u8; 8] = buf[2 + 4 + 8..2 + 4 + 8 + 8].try_into().unwrap_abort();
let len_part: [u8; 2] = buf[2 + 4 + 8 + 8..2 + 4 + 8 + 8 + 2].try_into().unwrap_abort();
let identity_provider = IdentityProvider::from_le_bytes(ip_part);
let created_at = Timestamp::from_timestamp_millis(u64::from_le_bytes(created_at_part));
let valid_to = Timestamp::from_timestamp_millis(u64::from_le_bytes(valid_to_part));
let remaining_items = u16::from_le_bytes(len_part);
let attributes_start = self.pos + 2 + 4 + 8 + 8 + 2;
self.pos += u32::from(u16::from_le_bytes(skip_part)) + 2;
self.remaining_items -= 1;
Some(Policy {
identity_provider,
created_at,
valid_to,
items: AttributesCursor {
current_position: attributes_start,
remaining_items,
total_items: remaining_items,
},
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
let rem = self.remaining_items as usize;
(rem, Some(rem))
}
}
impl ExactSizeIterator for PoliciesIterator {
#[inline(always)]
fn len(&self) -> usize { self.remaining_items as usize }
}
impl<T: sealed::ContextType> HasCommonData for ExternContext<T> {
type MetadataType = ExternChainMeta;
type ParamType = ExternParameter;
type PolicyIteratorType = PoliciesIterator;
type PolicyType = Policy<AttributesCursor>;
#[inline(always)]
fn metadata(&self) -> &Self::MetadataType { &ExternChainMeta {} }
fn policies(&self) -> PoliciesIterator {
let mut buf: MaybeUninit<[u8; 2]> = MaybeUninit::uninit();
let buf = unsafe {
prims::get_policy_section(buf.as_mut_ptr() as *mut u8, 2, 0);
buf.assume_init()
};
PoliciesIterator {
pos: 2, remaining_items: u16::from_le_bytes(buf),
}
}
#[inline(always)]
fn parameter_cursor(&self) -> Self::ParamType { ExternParameter::default() }
}
const INVOKE_TRANSFER_TAG: u32 = 0;
const INVOKE_CALL_TAG: u32 = 1;
const INVOKE_QUERY_ACCOUNT_BALANCE_TAG: u32 = 2;
const INVOKE_QUERY_CONTRACT_BALANCE_TAG: u32 = 3;
const INVOKE_QUERY_EXCHANGE_RATES_TAG: u32 = 4;
const INVOKE_CHECK_ACCOUNT_SIGNATURE_TAG: u32 = 5;
const INVOKE_QUERY_ACCOUNT_PUBLIC_KEYS_TAG: u32 = 6;
#[cfg(feature = "p7")]
const INVOKE_QUERY_CONTRACT_MODULE_REFERENCE_TAG: u32 = 7;
#[cfg(feature = "p7")]
const INVOKE_QUERY_CONTRACT_NAME_TAG: u32 = 8;
#[inline(always)]
fn get_invoke_failure_code(code: u64) -> Option<u8> {
if code & 0xff_ffff_ffff == 0 {
None
} else {
let error_code = (code & 0xff_0000_0000) >> 32;
Some(error_code as u8)
}
}
fn parse_transfer_response_code(code: u64) -> TransferResult {
if let Some(error_code) = get_invoke_failure_code(code) {
match error_code {
0x01 => Err(TransferError::AmountTooLarge),
0x02 => Err(TransferError::MissingAccount),
_ => crate::trap(), }
} else {
Ok(())
}
}
#[inline(always)]
fn parse_upgrade_response_code(code: u64) -> UpgradeResult {
if let Some(error_code) = get_invoke_failure_code(code) {
match error_code {
0x07 => Err(UpgradeError::MissingModule),
0x08 => Err(UpgradeError::MissingContract),
0x09 => Err(UpgradeError::UnsupportedModuleVersion),
_ => crate::trap(), }
} else {
Ok(())
}
}
fn parse_call_response_code(code: u64) -> CallContractResult<ExternCallResponse> {
if let Some(error_code) = get_invoke_failure_code(code) {
match error_code {
0x00 =>
{
let reason = (0x0000_0000_ffff_ffff & code) as u32 as i32;
if reason == 0 {
crate::trap()
} else {
let rv = (code >> 40) as u32;
if rv > 0 {
Err(CallContractError::LogicReject {
reason,
return_value: ExternCallResponse::new(unsafe {
NonZeroU32::new_unchecked(rv)
}),
})
} else {
unsafe { crate::hint::unreachable_unchecked() } }
}
}
0x01 => Err(CallContractError::AmountTooLarge),
0x02 => Err(CallContractError::MissingAccount),
0x03 => Err(CallContractError::MissingContract),
0x04 => Err(CallContractError::MissingEntrypoint),
0x05 => Err(CallContractError::MessageFailed),
0x06 => Err(CallContractError::Trap),
_ => unsafe { crate::hint::unreachable_unchecked() }, }
} else {
let rv = (code >> 40) as u32;
let tag = 0x80_0000u32; if tag & rv != 0 {
Ok((true, NonZeroU32::new(rv & !tag).map(ExternCallResponse::new)))
} else {
Ok((false, NonZeroU32::new(rv).map(ExternCallResponse::new)))
}
}
}
fn parse_query_account_balance_response_code(
code: u64,
) -> Result<ExternCallResponse, QueryAccountBalanceError> {
if let Some(error_code) = get_invoke_failure_code(code) {
if error_code == 0x02 {
Err(QueryAccountBalanceError)
} else {
unsafe { crate::hint::unreachable_unchecked() }
}
} else {
let return_value_index = NonZeroU32::new((code >> 40) as u32).unwrap_abort();
Ok(ExternCallResponse::new(return_value_index))
}
}
fn parse_query_contract_balance_response_code(
code: u64,
) -> Result<ExternCallResponse, QueryContractBalanceError> {
if let Some(error_code) = get_invoke_failure_code(code) {
if error_code == 0x03 {
Err(QueryContractBalanceError)
} else {
unsafe { crate::hint::unreachable_unchecked() }
}
} else {
let return_value_index = NonZeroU32::new((code >> 40) as u32).unwrap_abort();
Ok(ExternCallResponse::new(return_value_index))
}
}
fn parse_query_account_public_keys_response_code(
code: u64,
) -> Result<ExternCallResponse, QueryAccountPublicKeysError> {
if let Some(error_code) = get_invoke_failure_code(code) {
if error_code == 0x02 {
Err(QueryAccountPublicKeysError)
} else {
unsafe { crate::hint::unreachable_unchecked() }
}
} else {
let return_value_index = NonZeroU32::new((code >> 40) as u32).unwrap_abort();
Ok(ExternCallResponse::new(return_value_index))
}
}
fn parse_check_account_signature_response_code(
code: u64,
) -> Result<bool, CheckAccountSignatureError> {
if let Some(error_code) = get_invoke_failure_code(code) {
if error_code == 0x02 {
Err(CheckAccountSignatureError::MissingAccount)
} else if error_code == 0x0a {
Err(CheckAccountSignatureError::MalformedData)
} else if error_code == 0x0b {
Ok(false)
} else {
unsafe { crate::hint::unreachable_unchecked() }
}
} else {
Ok(true)
}
}
fn parse_query_exchange_rates_response_code(code: u64) -> ExternCallResponse {
if get_invoke_failure_code(code).is_some() {
unsafe { crate::hint::unreachable_unchecked() }
} else {
let return_value_index = NonZeroU32::new((code >> 40) as u32).unwrap_abort();
ExternCallResponse::new(return_value_index)
}
}
#[cfg(feature = "p7")]
fn parse_query_contract_module_reference_response_code(
code: u64,
) -> Result<ExternCallResponse, QueryContractModuleReferenceError> {
if let Some(error_code) = get_invoke_failure_code(code) {
if error_code == 0x03 {
Err(QueryContractModuleReferenceError)
} else {
unsafe { crate::hint::unreachable_unchecked() }
}
} else {
let return_value_index = NonZeroU32::new((code >> 40) as u32).unwrap_abort();
Ok(ExternCallResponse::new(return_value_index))
}
}
#[cfg(feature = "p7")]
fn parse_query_contract_name_response_code(
code: u64,
) -> Result<OwnedContractName, QueryContractNameError> {
if let Some(error_code) = get_invoke_failure_code(code) {
if error_code == 0x03 {
Err(QueryContractNameError)
} else {
unsafe { crate::hint::unreachable_unchecked() }
}
} else {
let return_value_index = (code >> 40) as u32;
let name = unsafe {
let name_size = prims::get_parameter_size(return_value_index);
if name_size < 0 {
crate::hint::unreachable_unchecked()
}
let mut buf = vec![0; name_size as usize];
prims::get_parameter_section(return_value_index, buf.as_mut_ptr(), name_size as u32, 0);
String::from_utf8_unchecked(buf)
};
Ok(OwnedContractName::new_unchecked(name))
}
}
fn invoke_transfer_worker(receiver: &AccountAddress, amount: Amount) -> TransferResult {
let mut bytes: MaybeUninit<[u8; ACCOUNT_ADDRESS_SIZE + 8]> = MaybeUninit::uninit();
let data = unsafe {
(bytes.as_mut_ptr() as *mut u8).copy_from_nonoverlapping(
receiver.as_ref() as *const [u8; ACCOUNT_ADDRESS_SIZE] as *const u8,
ACCOUNT_ADDRESS_SIZE,
);
(bytes.as_mut_ptr() as *mut u8).add(ACCOUNT_ADDRESS_SIZE).copy_from_nonoverlapping(
&amount.micro_ccd.to_le_bytes() as *const [u8; 8] as *const u8,
8,
);
bytes.assume_init()
};
let response = unsafe {
prims::invoke(INVOKE_TRANSFER_TAG, data.as_ptr(), (ACCOUNT_ADDRESS_SIZE + 8) as u32)
};
parse_transfer_response_code(response)
}
fn invoke_contract_construct_parameter(
to: &ContractAddress,
parameter: Parameter,
method: EntrypointName,
amount: Amount,
) -> Vec<u8> {
let mut data =
Vec::with_capacity(16 + parameter.as_ref().len() + 2 + method.size() as usize + 2 + 8);
let mut cursor = Cursor::new(&mut data);
to.serial(&mut cursor).unwrap_abort();
parameter.serial(&mut cursor).unwrap_abort();
method.serial(&mut cursor).unwrap_abort();
amount.serial(&mut cursor).unwrap_abort();
data
}
fn query_account_balance_worker(address: &AccountAddress) -> QueryAccountBalanceResult {
let response = unsafe {
prims::invoke(
INVOKE_QUERY_ACCOUNT_BALANCE_TAG,
AsRef::<[u8]>::as_ref(&address).as_ptr(),
32,
)
};
let mut return_value = parse_query_account_balance_response_code(response)?;
Ok(AccountBalance::deserial(&mut return_value).unwrap_abort())
}
fn query_contract_balance_worker(address: &ContractAddress) -> QueryContractBalanceResult {
let data = [address.index.to_le_bytes(), address.subindex.to_le_bytes()];
let response =
unsafe { prims::invoke(INVOKE_QUERY_CONTRACT_BALANCE_TAG, data.as_ptr() as *const u8, 16) };
let mut return_value = parse_query_contract_balance_response_code(response)?;
Ok(Amount::deserial(&mut return_value).unwrap_abort())
}
fn query_exchange_rates_worker() -> ExchangeRates {
let response_code = unsafe { prims::invoke(INVOKE_QUERY_EXCHANGE_RATES_TAG, [].as_ptr(), 0) };
let mut response = parse_query_exchange_rates_response_code(response_code);
ExchangeRates::deserial(&mut response).unwrap_abort()
}
fn query_account_public_keys_worker(address: AccountAddress) -> QueryAccountPublicKeysResult {
let data: &[u8] = address.as_ref();
let response =
unsafe { prims::invoke(INVOKE_QUERY_ACCOUNT_PUBLIC_KEYS_TAG, data.as_ptr(), 32) };
let mut return_value = parse_query_account_public_keys_response_code(response)?;
Ok(crate::AccountPublicKeys::deserial(&mut return_value).unwrap_abort())
}
fn check_account_signature_worker(
address: AccountAddress,
signatures: &AccountSignatures,
data: &[u8],
) -> CheckAccountSignatureResult {
let mut buffer = address.0.to_vec();
(data.len() as u32).serial(&mut buffer).unwrap_abort();
buffer.extend_from_slice(data);
signatures.serial(&mut buffer).unwrap_abort();
let response = unsafe {
prims::invoke(INVOKE_CHECK_ACCOUNT_SIGNATURE_TAG, buffer.as_ptr(), buffer.len() as u32)
};
drop(buffer);
parse_check_account_signature_response_code(response)
}
#[cfg(feature = "p7")]
fn query_contract_module_reference_worker(
address: &ContractAddress,
) -> QueryContractModuleReferenceResult {
let data = [address.index.to_le_bytes(), address.subindex.to_le_bytes()];
let response = unsafe {
prims::invoke(INVOKE_QUERY_CONTRACT_MODULE_REFERENCE_TAG, data.as_ptr() as *const u8, 16)
};
let mut return_value = parse_query_contract_module_reference_response_code(response)?;
Ok(ModuleReference::deserial(&mut return_value).unwrap_abort())
}
#[cfg(feature = "p7")]
fn query_contract_name_worker(address: &ContractAddress) -> QueryContractNameResult {
let data = [address.index.to_le_bytes(), address.subindex.to_le_bytes()];
let response =
unsafe { prims::invoke(INVOKE_QUERY_CONTRACT_NAME_TAG, data.as_ptr() as *const u8, 16) };
parse_query_contract_name_response_code(response)
}
impl<S> StateBuilder<S>
where
S: HasStateApi,
{
pub fn open(state: S) -> Self {
Self {
state_api: state,
}
}
#[must_use]
pub fn new_state_container(&mut self) -> (S, [u8; 8]) {
(self.state_api.clone(), self.get_and_update_item_prefix())
}
pub fn new_map<K, V>(&mut self) -> StateMap<K, V, S> {
let (state_api, prefix) = self.new_state_container();
StateMap::open(state_api, prefix)
}
pub fn new_set<T>(&mut self) -> StateSet<T, S> {
let (state_api, prefix) = self.new_state_container();
StateSet::open(state_api, prefix)
}
#[must_use]
pub fn new_box<T: Serial>(&mut self, value: T) -> StateBox<T, S> {
let (state_api, prefix) = self.new_state_container();
let mut state_entry = self.state_api.create_entry(&prefix).unwrap_abort();
value.serial(&mut state_entry).unwrap_abort();
StateBox::new(value, state_api, state_entry)
}
fn get_and_update_item_prefix(&mut self) -> [u8; 8] {
let mut next_collection_prefix_entry = self
.state_api
.entry(NEXT_ITEM_PREFIX_KEY)
.or_insert_raw(&INITIAL_NEXT_ITEM_PREFIX)
.unwrap_abort();
let collection_prefix = next_collection_prefix_entry.read_u64().unwrap_abort(); next_collection_prefix_entry.move_to_start();
next_collection_prefix_entry.write_u64(collection_prefix + 1).unwrap_abort(); collection_prefix.to_le_bytes()
}
}
impl StateBuilder<StateApi> {
pub fn new_btree_set<K>(&mut self) -> state_btree::StateBTreeSet<K> {
let (state_api, prefix) = self.new_state_container();
state_btree::StateBTreeSet::new(state_api, prefix)
}
pub fn new_btree_map<K, V>(&mut self) -> state_btree::StateBTreeMap<K, V> {
state_btree::StateBTreeMap {
key_value: self.new_map(),
key_order: self.new_btree_set(),
}
}
pub fn new_btree_set_degree<const M: usize, K>(&mut self) -> state_btree::StateBTreeSet<K, M> {
if M >= 2 {
let (state_api, prefix) = self.new_state_container();
state_btree::StateBTreeSet::new(state_api, prefix)
} else {
crate::fail!(
"Invalid minimum degree used for StateBTreeSet, must be >=2 instead got {}",
M
)
}
}
pub fn new_btree_map_degree<const M: usize, K, V>(
&mut self,
) -> state_btree::StateBTreeMap<K, V, M> {
if M >= 2 {
state_btree::StateBTreeMap {
key_value: self.new_map(),
key_order: self.new_btree_set_degree(),
}
} else {
crate::fail!(
"Invalid minimum degree used for StateBTreeMap, must be >=2 instead got {}",
M
)
}
}
}
impl<S> HasHost<S> for ExternHost<S>
where
S: Serial + DeserialWithState<ExternStateApi>,
{
type ReturnValueType = ExternCallResponse;
type StateApiType = ExternStateApi;
fn invoke_transfer(&self, receiver: &AccountAddress, amount: Amount) -> TransferResult {
invoke_transfer_worker(receiver, amount)
}
fn invoke_contract_raw(
&mut self,
to: &ContractAddress,
parameter: Parameter,
method: EntrypointName,
amount: Amount,
) -> CallContractResult<Self::ReturnValueType> {
let data = invoke_contract_construct_parameter(to, parameter, method, amount);
let len = data.len();
self.commit_state();
let response = unsafe { prims::invoke(INVOKE_CALL_TAG, data.as_ptr(), len as u32) };
let (state_modified, res) = parse_call_response_code(response)?;
if state_modified {
if let Ok(new_state) = S::deserial_with_state(
&self.state_builder.state_api,
&mut self.state_builder.state_api.lookup_entry(&[]).unwrap_abort(),
) {
self.state = new_state;
} else {
crate::trap()
}
}
Ok((state_modified, res))
}
fn invoke_contract_raw_read_only(
&self,
to: &ContractAddress,
parameter: Parameter,
method: EntrypointName,
amount: Amount,
) -> ReadOnlyCallContractResult<Self::ReturnValueType> {
let data = invoke_contract_construct_parameter(to, parameter, method, amount);
let len = data.len();
let response = unsafe { prims::invoke(INVOKE_CALL_TAG, data.as_ptr(), len as u32) };
let (state_modified, res) = parse_call_response_code(response)?;
if state_modified {
crate::trap()
} else {
Ok(res)
}
}
#[inline(always)]
fn account_balance(&self, address: AccountAddress) -> QueryAccountBalanceResult {
query_account_balance_worker(&address)
}
#[inline(always)]
fn contract_balance(&self, address: ContractAddress) -> QueryContractBalanceResult {
query_contract_balance_worker(&address)
}
#[inline(always)]
fn exchange_rates(&self) -> ExchangeRates { query_exchange_rates_worker() }
fn upgrade(&mut self, module: ModuleReference) -> UpgradeResult {
let response = unsafe { prims::upgrade(module.as_ref().as_ptr()) };
parse_upgrade_response_code(response)
}
fn account_public_keys(&self, address: AccountAddress) -> QueryAccountPublicKeysResult {
query_account_public_keys_worker(address)
}
fn check_account_signature(
&self,
address: AccountAddress,
signatures: &AccountSignatures,
data: &[u8],
) -> CheckAccountSignatureResult {
check_account_signature_worker(address, signatures, data)
}
#[cfg(feature = "p7")]
#[inline(always)]
fn contract_module_reference(
&self,
address: ContractAddress,
) -> QueryContractModuleReferenceResult {
query_contract_module_reference_worker(&address)
}
#[cfg(feature = "p7")]
#[inline(always)]
fn contract_name(&self, address: ContractAddress) -> QueryContractNameResult {
query_contract_name_worker(&address)
}
fn state(&self) -> &S { &self.state }
fn state_mut(&mut self) -> &mut S { &mut self.state }
fn commit_state(&mut self) {
let mut root_entry = self.state_builder.state_api.lookup_entry(&[]).unwrap_abort();
self.state.serial(&mut root_entry).unwrap_abort();
let new_state_size = root_entry.size().unwrap_abort();
root_entry.truncate(new_state_size).unwrap_abort();
}
#[inline(always)]
fn self_balance(&self) -> Amount {
Amount::from_micro_ccd(unsafe { prims::get_receive_self_balance() })
}
#[inline(always)]
fn state_builder(&mut self) -> &mut StateBuilder<Self::StateApiType> { &mut self.state_builder }
#[inline(always)]
fn state_and_builder(&mut self) -> (&mut S, &mut StateBuilder<Self::StateApiType>) {
(&mut self.state, &mut self.state_builder)
}
}
impl HasHost<ExternStateApi> for ExternLowLevelHost {
type ReturnValueType = ExternCallResponse;
type StateApiType = ExternStateApi;
#[inline(always)]
fn invoke_transfer(&self, receiver: &AccountAddress, amount: Amount) -> TransferResult {
invoke_transfer_worker(receiver, amount)
}
fn invoke_contract_raw(
&mut self,
to: &ContractAddress,
parameter: Parameter,
method: EntrypointName,
amount: Amount,
) -> CallContractResult<Self::ReturnValueType> {
let data = invoke_contract_construct_parameter(to, parameter, method, amount);
let len = data.len();
let response = unsafe { prims::invoke(INVOKE_CALL_TAG, data.as_ptr(), len as u32) };
parse_call_response_code(response)
}
#[inline(always)]
fn account_balance(&self, address: AccountAddress) -> QueryAccountBalanceResult {
query_account_balance_worker(&address)
}
#[inline(always)]
fn contract_balance(&self, address: ContractAddress) -> QueryContractBalanceResult {
query_contract_balance_worker(&address)
}
#[inline(always)]
fn exchange_rates(&self) -> ExchangeRates { query_exchange_rates_worker() }
fn upgrade(&mut self, module: ModuleReference) -> UpgradeResult {
let response = unsafe { prims::upgrade(module.as_ref().as_ptr()) };
parse_upgrade_response_code(response)
}
fn account_public_keys(&self, address: AccountAddress) -> QueryAccountPublicKeysResult {
query_account_public_keys_worker(address)
}
fn check_account_signature(
&self,
address: AccountAddress,
signatures: &AccountSignatures,
data: &[u8],
) -> CheckAccountSignatureResult {
check_account_signature_worker(address, signatures, data)
}
#[cfg(feature = "p7")]
#[inline(always)]
fn contract_module_reference(
&self,
address: ContractAddress,
) -> QueryContractModuleReferenceResult {
query_contract_module_reference_worker(&address)
}
#[cfg(feature = "p7")]
#[inline(always)]
fn contract_name(&self, address: ContractAddress) -> QueryContractNameResult {
query_contract_name_worker(&address)
}
#[inline(always)]
fn state(&self) -> &ExternStateApi { &self.state_api }
#[inline(always)]
fn state_mut(&mut self) -> &mut ExternStateApi { &mut self.state_api }
#[inline(always)]
fn commit_state(&mut self) {
}
#[inline(always)]
fn self_balance(&self) -> Amount {
Amount::from_micro_ccd(unsafe { prims::get_receive_self_balance() })
}
#[inline(always)]
fn state_builder(&mut self) -> &mut StateBuilder<Self::StateApiType> { &mut self.state_builder }
#[inline(always)]
fn state_and_builder(
&mut self,
) -> (&mut ExternStateApi, &mut StateBuilder<Self::StateApiType>) {
(&mut self.state_api, &mut self.state_builder)
}
fn invoke_contract_raw_read_only(
&self,
to: &ContractAddress,
parameter: Parameter<'_>,
method: EntrypointName<'_>,
amount: Amount,
) -> ReadOnlyCallContractResult<Self::ReturnValueType> {
let data = invoke_contract_construct_parameter(to, parameter, method, amount);
let len = data.len();
let response = unsafe { prims::invoke(INVOKE_CALL_TAG, data.as_ptr(), len as u32) };
let (state_modified, res) = parse_call_response_code(response)?;
if state_modified {
crate::trap()
} else {
Ok(res)
}
}
}
impl HasCryptoPrimitives for ExternCryptoPrimitives {
fn verify_ed25519_signature(
&self,
public_key: PublicKeyEd25519,
signature: SignatureEd25519,
message: &[u8],
) -> bool {
let res = unsafe {
prims::verify_ed25519_signature(
public_key.0.as_ptr(),
signature.0.as_ptr(),
message.as_ptr(),
message.len() as u32,
)
};
res == 1
}
fn verify_ecdsa_secp256k1_signature(
&self,
public_key: PublicKeyEcdsaSecp256k1,
signature: SignatureEcdsaSecp256k1,
message_hash: [u8; 32],
) -> bool {
let res = unsafe {
prims::verify_ecdsa_secp256k1_signature(
public_key.0.as_ptr(),
signature.0.as_ptr(),
message_hash.as_ptr(),
)
};
res == 1
}
fn hash_sha2_256(&self, data: &[u8]) -> HashSha2256 {
let mut output: MaybeUninit<[u8; 32]> = MaybeUninit::uninit();
unsafe {
prims::hash_sha2_256(data.as_ptr(), data.len() as u32, output.as_mut_ptr() as *mut u8);
HashSha2256(output.assume_init())
}
}
fn hash_sha3_256(&self, data: &[u8]) -> HashSha3256 {
let mut output: MaybeUninit<[u8; 32]> = MaybeUninit::uninit();
unsafe {
prims::hash_sha3_256(data.as_ptr(), data.len() as u32, output.as_mut_ptr() as *mut u8);
HashSha3256(output.assume_init())
}
}
fn hash_keccak_256(&self, data: &[u8]) -> HashKeccak256 {
let mut output: MaybeUninit<[u8; 32]> = MaybeUninit::uninit();
unsafe {
prims::hash_keccak_256(
data.as_ptr(),
data.len() as u32,
output.as_mut_ptr() as *mut u8,
);
HashKeccak256(output.assume_init())
}
}
}
impl HasInitContext for ExternContext<crate::types::ExternInitContext> {
type InitData = ();
fn open(_: Self::InitData) -> Self { ExternContext::default() }
#[inline(always)]
fn init_origin(&self) -> AccountAddress {
let mut bytes: MaybeUninit<[u8; ACCOUNT_ADDRESS_SIZE]> = MaybeUninit::uninit();
let ptr = bytes.as_mut_ptr();
let address = unsafe {
prims::get_init_origin(ptr as *mut u8);
bytes.assume_init()
};
AccountAddress(address)
}
}
impl HasReceiveContext for ExternContext<crate::types::ExternReceiveContext> {
type ReceiveData = ();
fn open(_: Self::ReceiveData) -> Self { ExternContext::default() }
#[inline(always)]
fn invoker(&self) -> AccountAddress {
let mut bytes: MaybeUninit<[u8; ACCOUNT_ADDRESS_SIZE]> = MaybeUninit::uninit();
let ptr = bytes.as_mut_ptr();
let address = unsafe {
prims::get_receive_invoker(ptr as *mut u8);
bytes.assume_init()
};
AccountAddress(address)
}
#[inline(always)]
fn self_address(&self) -> ContractAddress {
let mut bytes: MaybeUninit<[u8; 16]> = MaybeUninit::uninit();
let ptr = bytes.as_mut_ptr();
let address = unsafe {
prims::get_receive_self_address(ptr as *mut u8);
bytes.assume_init()
};
match from_bytes(&address) {
Ok(v) => v,
Err(_) => crate::trap(),
}
}
#[inline(always)]
fn sender(&self) -> Address {
let mut bytes: MaybeUninit<[u8; 33]> = MaybeUninit::uninit();
let ptr = bytes.as_mut_ptr() as *mut u8;
unsafe {
prims::get_receive_sender(ptr);
let tag = *ptr;
match tag {
0u8 => {
match from_bytes(core::slice::from_raw_parts(ptr.add(1), ACCOUNT_ADDRESS_SIZE))
{
Ok(v) => Address::Account(v),
Err(_) => crate::trap(),
}
}
1u8 => match from_bytes(core::slice::from_raw_parts(ptr.add(1), 16)) {
Ok(v) => Address::Contract(v),
Err(_) => crate::trap(),
},
_ => crate::trap(), }
}
}
#[inline(always)]
fn owner(&self) -> AccountAddress {
let mut bytes: MaybeUninit<[u8; ACCOUNT_ADDRESS_SIZE]> = MaybeUninit::uninit();
let ptr = bytes.as_mut_ptr();
let address = unsafe {
prims::get_receive_owner(ptr as *mut u8);
bytes.assume_init()
};
AccountAddress(address)
}
fn named_entrypoint(&self) -> OwnedEntrypointName {
let mut data = crate::vec![0u8; unsafe { prims::get_receive_entrypoint_size() as usize }];
unsafe { prims::get_receive_entrypoint(data.as_mut_ptr()) };
OwnedEntrypointName::new_unchecked(unsafe { String::from_utf8_unchecked(data) })
}
}
impl HasLogger for Logger {
#[inline(always)]
fn init() -> Self {
Self {
_private: (),
}
}
fn log_raw(&mut self, event: &[u8]) -> Result<(), LogError> {
let res = unsafe { prims::log_event(event.as_ptr(), event.len() as u32) };
match res {
1 => Ok(()),
0 => Err(LogError::Full),
_ => Err(LogError::Malformed),
}
}
}
#[doc(hidden)]
pub fn put_in_memory(input: &[u8]) -> *mut u8 {
let bytes_length = input.len() as u32;
let mut bytes = to_bytes(&bytes_length);
bytes.extend_from_slice(input);
let ptr = bytes.as_mut_ptr();
#[cfg(feature = "std")]
::std::mem::forget(bytes);
#[cfg(not(feature = "std"))]
core::mem::forget(bytes);
ptr
}
impl<A, E> UnwrapAbort for Result<A, E> {
type Unwrap = A;
#[inline]
fn unwrap_abort(self) -> Self::Unwrap {
match self {
Ok(x) => x,
Err(_) => crate::trap(),
}
}
}
impl<A, E: fmt::Debug> ExpectReport for Result<A, E> {
type Unwrap = A;
fn expect_report(self, msg: &str) -> Self::Unwrap {
match self {
Ok(x) => x,
Err(e) => crate::fail!("{}: {:?}", msg, e),
}
}
}
impl<A: fmt::Debug, E> ExpectErrReport for Result<A, E> {
type Unwrap = E;
fn expect_err_report(self, msg: &str) -> Self::Unwrap {
match self {
Ok(a) => crate::fail!("{}: {:?}", msg, a),
Err(e) => e,
}
}
}
impl<A> UnwrapAbort for Option<A> {
type Unwrap = A;
#[inline(always)]
#[allow(clippy::redundant_closure)]
fn unwrap_abort(self) -> Self::Unwrap { self.unwrap_or_else(|| crate::trap()) }
}
impl<A> ExpectReport for Option<A> {
type Unwrap = A;
fn expect_report(self, msg: &str) -> Self::Unwrap {
match self {
Some(v) => v,
None => crate::fail!("{}", msg),
}
}
}
impl<A: fmt::Debug> ExpectNoneReport for Option<A> {
fn expect_none_report(self, msg: &str) {
if let Some(x) = self {
crate::fail!("{}: {:?}", msg, x)
}
}
}
impl<D: Deserial, S: HasStateApi> DeserialWithState<S> for D {
#[inline(always)]
fn deserial_with_state<R: Read>(_state: &S, source: &mut R) -> ParseResult<Self> {
Self::deserial(source)
}
}
impl<D: DeserialCtx, S: HasStateApi> DeserialCtxWithState<S> for D {
#[inline(always)]
fn deserial_ctx_with_state<R: Read>(
size_length: schema::SizeLength,
ensure_ordered: bool,
_state: &S,
source: &mut R,
) -> ParseResult<Self> {
Self::deserial_ctx(size_length, ensure_ordered, source)
}
}
impl<K, V, S> DeserialWithState<S> for StateMap<K, V, S>
where
S: HasStateApi,
{
fn deserial_with_state<R: Read>(state: &S, source: &mut R) -> ParseResult<Self> {
source.read_array().map(|map_prefix| StateMap::open(state.clone(), map_prefix))
}
}
impl<T, S> DeserialWithState<S> for StateSet<T, S>
where
S: HasStateApi,
T: Serial + DeserialWithState<S>,
{
fn deserial_with_state<R: Read>(state: &S, source: &mut R) -> ParseResult<Self> {
source.read_array().map(|set_prefix| StateSet::open(state.clone(), set_prefix))
}
}
impl<T, S> DeserialWithState<S> for StateBox<T, S>
where
S: HasStateApi,
T: Serial + DeserialWithState<S>,
{
fn deserial_with_state<R: Read>(state: &S, source: &mut R) -> ParseResult<Self> {
let prefix = source.read_array()?;
Ok(StateBox {
state_api: state.clone(),
inner: UnsafeCell::new(StateBoxInner::Reference {
prefix,
}),
})
}
}
impl<T: Serialize> Deletable for T {
#[inline(always)]
fn delete(self) {} }
impl<T, S> Deletable for StateBox<T, S>
where
T: Serial + DeserialWithState<S> + Deletable,
S: HasStateApi,
{
fn delete(mut self) {
let inner = mem::replace(
&mut self.inner,
UnsafeCell::new(StateBoxInner::Reference {
prefix: [0u8; 8],
}),
);
let (entry, value) = match inner.into_inner() {
StateBoxInner::Loaded {
entry,
value,
..
} => (entry, value),
StateBoxInner::Reference {
prefix,
} => {
let mut entry = self.state_api.lookup_entry(&prefix).unwrap_abort();
let value = T::deserial_with_state(&self.state_api, &mut entry).unwrap_abort();
(entry, value)
}
};
self.state_api.delete_entry(entry).unwrap_abort();
value.delete()
}
}
impl<T, S> Deletable for StateSet<T, S>
where
S: HasStateApi,
{
fn delete(mut self) {
self.state_api.delete_prefix(&self.prefix).unwrap_abort();
}
}
impl<K, V, S> Deletable for StateMap<K, V, S>
where
S: HasStateApi,
K: Serialize,
V: Serial + DeserialWithState<S> + Deletable,
{
fn delete(mut self) { self.clear(); }
}
impl Serial for HashSha2256 {
fn serial<W: Write>(&self, out: &mut W) -> Result<(), W::Err> { self.0.serial(out) }
}
impl Deserial for HashSha2256 {
fn deserial<R: Read>(source: &mut R) -> ParseResult<Self> {
Ok(HashSha2256(Deserial::deserial(source)?))
}
}
impl schema::SchemaType for HashSha2256 {
fn get_type() -> concordium_contracts_common::schema::Type { schema::Type::ByteArray(32) }
}
impl Serial for HashSha3256 {
fn serial<W: Write>(&self, out: &mut W) -> Result<(), W::Err> { self.0.serial(out) }
}
impl Deserial for HashSha3256 {
fn deserial<R: Read>(source: &mut R) -> ParseResult<Self> {
Ok(HashSha3256(Deserial::deserial(source)?))
}
}
impl schema::SchemaType for HashSha3256 {
fn get_type() -> concordium_contracts_common::schema::Type { schema::Type::ByteArray(32) }
}
impl Serial for HashKeccak256 {
fn serial<W: Write>(&self, out: &mut W) -> Result<(), W::Err> { self.0.serial(out) }
}
impl Deserial for HashKeccak256 {
fn deserial<R: Read>(source: &mut R) -> ParseResult<Self> {
Ok(HashKeccak256(Deserial::deserial(source)?))
}
}
impl schema::SchemaType for HashKeccak256 {
fn get_type() -> concordium_contracts_common::schema::Type { schema::Type::ByteArray(32) }
}
impl schema::SchemaType for MetadataUrl {
fn get_type() -> schema::Type {
schema::Type::Struct(schema::Fields::Named(crate::vec![
(String::from("url"), schema::Type::String(schema::SizeLength::U16)),
(String::from("hash"), Option::<HashSha2256>::get_type()),
]))
}
}
impl Serial for MetadataUrl {
fn serial<W: Write>(&self, out: &mut W) -> Result<(), W::Err> {
let bytes = self.url.as_bytes();
let len = bytes.len() as u16;
len.serial(out)?;
serial_vector_no_length(bytes, out)?;
self.hash.serial(out)
}
}
impl Deserial for MetadataUrl {
fn deserial<R: Read>(source: &mut R) -> ParseResult<Self> {
let len: u16 = source.get()?;
let bytes = deserial_vector_no_length(source, len as usize)?;
Ok(MetadataUrl {
url: String::from_utf8(bytes).map_err(|_| ParseError::default())?,
hash: Deserial::deserial(source)?,
})
}
}
#[cfg(test)]
mod tests {
#[test]
fn statemap_multiple_entries_not_allowed() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/state/map-multiple-entries.rs");
}
#[test]
fn statemap_multiple_state_ref_mut_not_allowed() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/state/map-multiple-state-ref-mut.rs");
}
}
#[cfg(feature = "internal-wasm-test")]
mod wasm_test {
use crate::{
claim, claim_eq, concordium_test, to_bytes, Deletable, Deserial, DeserialWithState,
EntryRaw, HasStateApi, HasStateEntry, ParseResult, Serial, StateApi, StateBuilder,
StateError, StateMap, StateSet, INITIAL_NEXT_ITEM_PREFIX,
};
const GENERIC_MAP_PREFIX: u64 = 1;
impl<S> StateBuilder<S>
where
S: HasStateApi,
{
pub(crate) fn get<K: Serial, V: DeserialWithState<S>>(
&self,
key: K,
) -> Option<ParseResult<V>> {
let key_with_map_prefix = Self::prepend_generic_map_key(key);
self.state_api
.lookup_entry(&key_with_map_prefix)
.map(|mut entry| V::deserial_with_state(&self.state_api, &mut entry))
}
pub(crate) fn insert<K: Serial, V: Serial>(
&mut self,
key: K,
value: V,
) -> Result<(), StateError> {
let key_with_map_prefix = Self::prepend_generic_map_key(key);
match self.state_api.entry(key_with_map_prefix) {
EntryRaw::Vacant(vac) => {
let _ = vac.insert(&value);
}
EntryRaw::Occupied(mut occ) => occ.insert(&value),
}
Ok(())
}
fn prepend_generic_map_key<K: Serial>(key: K) -> Vec<u8> {
let mut key_with_map_prefix = to_bytes(&GENERIC_MAP_PREFIX);
key_with_map_prefix.append(&mut to_bytes(&key));
key_with_map_prefix
}
}
#[concordium_test]
fn high_level_insert_get() {
let expected_value: u64 = 123123123;
let mut state_builder = StateBuilder::open(StateApi::open());
state_builder.insert(0, expected_value).expect("Insert failed");
let actual_value: u64 = state_builder.get(0).expect("Not found").expect("Not a valid u64");
claim_eq!(expected_value, actual_value);
}
#[concordium_test]
fn low_level_entry() {
let expected_value: u64 = 123123123;
let key = to_bytes(&42u64);
let mut state = StateApi::open();
state
.entry(&key[..])
.or_insert_raw(&to_bytes(&expected_value))
.expect("No iterators, so insertion should work.");
match state.entry(key) {
EntryRaw::Vacant(_) => panic!("Unexpected vacant entry."),
EntryRaw::Occupied(occ) => {
claim_eq!(u64::deserial(&mut occ.get()), Ok(expected_value))
}
}
}
#[concordium_test]
fn high_level_statemap() {
let my_map_key = "my_map";
let mut state_builder = StateBuilder::open(StateApi::open());
let map_to_insert = state_builder.new_map::<String, String>();
state_builder.insert(my_map_key, map_to_insert).expect("Insert failed");
let mut my_map: StateMap<String, String, _> = state_builder
.get(my_map_key)
.expect("Could not get statemap")
.expect("Deserializing statemap failed");
let _ = my_map.insert("abc".to_string(), "hello, world".to_string());
let _ = my_map.insert("def".to_string(), "hallo, Weld".to_string());
let _ = my_map.insert("ghi".to_string(), "hej, verden".to_string());
claim_eq!(*my_map.get(&"abc".to_string()).unwrap(), "hello, world".to_string());
let mut iter = my_map.iter();
let (k1, v1) = iter.next().unwrap();
claim_eq!(*k1, "abc".to_string());
claim_eq!(*v1, "hello, world".to_string());
let (k2, v2) = iter.next().unwrap();
claim_eq!(*k2, "def".to_string());
claim_eq!(*v2, "hallo, Weld".to_string());
let (k3, v3) = iter.next().unwrap();
claim_eq!(*k3, "ghi".to_string());
claim_eq!(*v3, "hej, verden".to_string());
claim!(iter.next().is_none());
}
#[concordium_test]
fn statemap_insert_remove() {
let mut state_builder = StateBuilder::open(StateApi::open());
let mut map = state_builder.new_map();
let value = String::from("hello");
let _ = map.insert(42, value.clone());
claim_eq!(*map.get(&42).unwrap(), value);
map.remove(&42);
claim!(map.get(&42).is_none());
}
#[concordium_test]
fn statemap_clear() {
let mut state_builder = StateBuilder::open(StateApi::open());
let mut map = state_builder.new_map();
let _ = map.insert(1, 2);
let _ = map.insert(2, 3);
let _ = map.insert(3, 4);
map.clear();
claim!(map.is_empty());
}
#[concordium_test]
fn high_level_nested_statemaps() {
let inner_map_key = 0u8;
let key_to_value = 77u8;
let value = 255u8;
let mut state_builder = StateBuilder::open(StateApi::open());
let mut outer_map = state_builder.new_map::<u8, StateMap<u8, u8, _>>();
let mut inner_map = state_builder.new_map::<u8, u8>();
let _ = inner_map.insert(key_to_value, value);
let _ = outer_map.insert(inner_map_key, inner_map);
claim_eq!(*outer_map.get(&inner_map_key).unwrap().get(&key_to_value).unwrap(), value);
}
#[concordium_test]
fn statemap_iter_mut_works() {
let mut state_builder = StateBuilder::open(StateApi::open());
let mut map = state_builder.new_map();
let _ = map.insert(0u8, 1u8);
let _ = map.insert(1u8, 2u8);
let _ = map.insert(2u8, 3u8);
for (_, mut v) in map.iter_mut() {
v.update(|old_value| *old_value += 10);
}
let mut iter = map.iter();
let (k1, v1) = iter.next().unwrap();
claim_eq!(*k1, 0);
claim_eq!(*v1, 11);
let (k2, v2) = iter.next().unwrap();
claim_eq!(*k2, 1);
claim_eq!(*v2, 12);
let (k3, v3) = iter.next().unwrap();
claim_eq!(*k3, 2);
claim_eq!(*v3, 13);
claim!(iter.next().is_none());
}
#[concordium_test]
fn iter_mut_works_on_nested_statemaps() {
let mut state_builder = StateBuilder::open(StateApi::open());
let mut outer_map = state_builder.new_map();
let mut inner_map = state_builder.new_map();
let _ = inner_map.insert(0u8, 1u8);
let _ = inner_map.insert(1u8, 2u8);
let _ = outer_map.insert(99u8, inner_map);
for (_, mut v_map) in outer_map.iter_mut() {
v_map.update(|v_map| {
for (_, mut inner_v) in v_map.iter_mut() {
inner_v.update(|inner_v| *inner_v += 10);
}
});
}
let mut outer_iter = outer_map.iter();
let (inner_map_key, inner_map) = outer_iter.next().unwrap();
claim_eq!(*inner_map_key, 99);
claim!(outer_iter.next().is_none());
let mut inner_iter = inner_map.iter();
let (k1, v1) = inner_iter.next().unwrap();
claim_eq!(*k1, 0);
claim_eq!(*v1, 11);
let (k2, v2) = inner_iter.next().unwrap();
claim_eq!(*k2, 1);
claim_eq!(*v2, 12);
claim!(inner_iter.next().is_none());
}
#[concordium_test]
fn statemap_iterator_unlocks_tree_once_dropped() {
let mut state_builder = StateBuilder::open(StateApi::open());
let mut map = state_builder.new_map();
let _ = map.insert(0u8, 1u8);
let _ = map.insert(1u8, 2u8);
{
let _iter = map.iter();
} let _ = map.insert(2u8, 3u8);
}
#[concordium_test]
fn high_level_stateset() {
let my_set_key = "my_set";
let mut state_builder = StateBuilder::open(StateApi::open());
let mut set = state_builder.new_set::<u8>();
claim!(set.insert(0));
claim!(set.insert(1));
claim!(!set.insert(1));
claim!(set.insert(2));
claim!(set.remove(&2));
state_builder.insert(my_set_key, set).expect("Insert failed");
claim!(state_builder.get::<_, StateSet<u8, _>>(my_set_key).unwrap().unwrap().contains(&0),);
claim!(!state_builder.get::<_, StateSet<u8, _>>(my_set_key).unwrap().unwrap().contains(&2),);
let set = state_builder.get::<_, StateSet<u8, _>>(my_set_key).unwrap().unwrap();
let mut iter = set.iter();
claim_eq!(*iter.next().unwrap(), 0);
claim_eq!(*iter.next().unwrap(), 1);
claim!(iter.next().is_none());
}
#[concordium_test]
fn high_level_nested_stateset() {
let inner_set_key = 0u8;
let value = 255u8;
let mut state_builder = StateBuilder::open(StateApi::open());
let mut outer_map = state_builder.new_map::<u8, StateSet<u8, _>>();
let mut inner_set = state_builder.new_set::<u8>();
inner_set.insert(value);
let _ = outer_map.insert(inner_set_key, inner_set);
claim!(outer_map.get(&inner_set_key).unwrap().contains(&value));
}
#[concordium_test]
fn stateset_insert_remove() {
let mut state_builder = StateBuilder::open(StateApi::open());
let mut set = state_builder.new_set();
let _ = set.insert(42);
claim!(set.contains(&42));
set.remove(&42);
claim!(!set.contains(&42));
}
#[concordium_test]
fn stateset_clear() {
let mut state_builder = StateBuilder::open(StateApi::open());
let mut set = state_builder.new_set();
let _ = set.insert(1);
let _ = set.insert(2);
let _ = set.insert(3);
set.clear();
claim!(set.is_empty());
}
#[concordium_test]
fn stateset_iterator_unlocks_tree_once_dropped() {
let mut state_builder = StateBuilder::open(StateApi::open());
let mut set = state_builder.new_set();
set.insert(0u8);
set.insert(1);
{
let _iter = set.iter();
} set.insert(2);
}
#[concordium_test]
fn allocate_and_get_statebox() {
let mut state_builder = StateBuilder::open(StateApi::open());
let boxed_value = String::from("I'm boxed");
let statebox = state_builder.new_box(boxed_value.clone());
claim_eq!(*statebox.get(), boxed_value);
}
#[concordium_test]
fn a_new_entry_can_not_be_created_under_a_locked_subtree() {
let expected_value: u64 = 123123123;
let key = to_bytes(b"ab");
let sub_key = to_bytes(b"abc");
let mut state = StateApi::open();
state
.entry(&key[..])
.or_insert_raw(&to_bytes(&expected_value))
.expect("No iterators, so insertion should work.");
claim!(state.iterator(&key).is_ok(), "Iterator should be present");
let entry = state.create_entry(&sub_key);
claim!(entry.is_err(), "Should not be able to create an entry under a locked subtree");
}
#[concordium_test]
fn a_new_entry_can_be_created_under_a_different_subtree_in_same_super_tree() {
let expected_value: u64 = 123123123;
let key = to_bytes(b"abcd");
let key2 = to_bytes(b"abe");
let mut state = StateApi::open();
state
.entry(&key[..])
.or_insert_raw(&to_bytes(&expected_value))
.expect("No iterators, so insertion should work.");
claim!(state.iterator(&key).is_ok(), "Iterator should be present");
let entry = state.create_entry(&key2);
claim!(entry.is_ok(), "Failed to create a new entry under a different subtree");
}
#[concordium_test]
fn an_existing_entry_can_not_be_deleted_under_a_locked_subtree() {
let expected_value: u64 = 123123123;
let key = to_bytes(b"ab");
let sub_key = to_bytes(b"abc");
let mut state = StateApi::open();
state
.entry(&key[..])
.or_insert_raw(&to_bytes(&expected_value))
.expect("no iterators, so insertion should work.");
let sub_entry = state
.entry(sub_key)
.or_insert_raw(&to_bytes(&expected_value))
.expect("Should be possible to create the entry.");
claim!(state.iterator(&key).is_ok(), "Iterator should be present");
claim!(
state.delete_entry(sub_entry).is_err(),
"Should not be able to delete entry under a locked subtree"
);
}
#[concordium_test]
fn an_existing_entry_can_be_deleted_from_a_different_subtree_in_same_super_tree() {
let expected_value: u64 = 123123123;
let key = to_bytes(b"abcd");
let key2 = to_bytes(b"abe");
let mut state = StateApi::open();
state
.entry(&key[..])
.or_insert_raw(&to_bytes(&expected_value))
.expect("No iterators, so insertion should work.");
let entry2 = state
.entry(key2)
.or_insert_raw(&to_bytes(&expected_value))
.expect("Should be possible to create the entry.");
claim!(state.iterator(&key).is_ok(), "Iterator should be present");
claim!(
state.delete_entry(entry2).is_ok(),
"Should be able to delete entry under a different subtree"
);
}
#[concordium_test]
fn deleting_nested_stateboxes_works() {
let mut state_builder = StateBuilder::open(StateApi::open());
let inner_box = state_builder.new_box(99u8);
let middle_box = state_builder.new_box(inner_box);
let outer_box = state_builder.new_box(middle_box);
outer_box.delete();
let mut iter = state_builder.state_api.iterator(&[]).expect("Could not get iterator");
claim!(iter.nth(1).is_none());
}
#[concordium_test]
fn clearing_statemap_with_stateboxes_works() {
let mut state_builder = StateBuilder::open(StateApi::open());
let box1 = state_builder.new_box(1u8);
let box2 = state_builder.new_box(2u8);
let box3 = state_builder.new_box(3u8);
let mut map = state_builder.new_map();
let _ = map.insert(1u8, box1);
let _ = map.insert(2u8, box2);
let _ = map.insert(3u8, box3);
map.clear();
let mut iter = state_builder.state_api.iterator(&[]).expect("Could not get iterator");
claim!(iter.nth(1).is_none());
}
#[concordium_test]
fn clearing_nested_statemaps_works() {
let mut state_builder = StateBuilder::open(StateApi::open());
let mut inner_map_1 = state_builder.new_map();
let _ = inner_map_1.insert(1u8, 2u8);
let _ = inner_map_1.insert(2u8, 3u8);
let _ = inner_map_1.insert(3u8, 4u8);
let mut inner_map_2 = state_builder.new_map();
let _ = inner_map_2.insert(11u8, 12u8);
let _ = inner_map_2.insert(12u8, 13u8);
let _ = inner_map_2.insert(13u8, 14u8);
let mut outer_map = state_builder.new_map();
let _ = outer_map.insert(0u8, inner_map_1);
let _ = outer_map.insert(1u8, inner_map_2);
outer_map.clear();
let mut iter = state_builder.state_api.iterator(&[]).expect("Could not get iterator");
claim!(iter.nth(1).is_none());
}
#[concordium_test]
fn occupied_entry_truncates_leftover_data() {
let mut state_builder = StateBuilder::open(StateApi::open());
let mut map = state_builder.new_map();
let _ = map.insert(99u8, "A longer string that should be truncated".into());
let a_short_string = "A short string".to_string();
let expected_size = a_short_string.len() + 4; map.entry(99u8).and_modify(|v| *v = a_short_string);
let actual_size = state_builder
.state_api
.lookup_entry(&[INITIAL_NEXT_ITEM_PREFIX[0], 0, 0, 0, 0, 0, 0, 0, 99])
.expect("Lookup failed")
.size()
.expect("Getting size failed");
claim_eq!(expected_size as u32, actual_size);
}
#[concordium_test]
fn occupied_entry_raw_truncates_leftover_data() {
let mut state = StateApi::open();
state
.entry([])
.or_insert_raw(&to_bytes(&"A longer string that should be truncated"))
.expect("No iterators, so insertion should work.");
let a_short_string = "A short string";
let expected_size = a_short_string.len() + 4; match state.entry([]) {
EntryRaw::Vacant(_) => panic!("Entry is vacant"),
EntryRaw::Occupied(mut occ) => occ.insert_raw(&to_bytes(&a_short_string)),
}
let actual_size =
state.lookup_entry(&[]).expect("Lookup failed").size().expect("Getting size failed");
claim_eq!(expected_size as u32, actual_size);
}
}