diff --git a/rust/examples/bank.rs b/rust/examples/bank.rs index 395e596d3345..9132cd3a820b 100644 --- a/rust/examples/bank.rs +++ b/rust/examples/bank.rs @@ -3,12 +3,14 @@ pub mod bank { use ixc::*; + #[derive(Resources)] pub struct Bank { - #[schema(name(address, denom), value(amount))] - balances: Map<(AccountID, String), u128>, + #[state(key(address, denom), value(amount))] + balances: AccumulatorMap<(AccountID, Str)>, } #[derive(SchemaValue)] + #[sealed] pub struct Coin<'a> { pub denom: &'a str, pub amount: u128, @@ -16,26 +18,38 @@ pub mod bank { #[handler_api] pub trait BankAPI { + fn get_balance(&self, ctx: &Context, account: AccountID, denom: &str) -> Result; fn send(&self, ctx: &mut Context, to: AccountID, amount: &[Coin], evt: &mut EventBus) -> Result<()>; } #[derive(SchemaValue)] + #[non_exhaustive] pub struct EventSend<'a> { - pub from: Address, - pub to: Address, + pub from: AccountID, + pub to: AccountID, pub coin: Coin<'a>, } - impl BankAPI for Bank { + impl Bank { + #[on_create] + fn create(&self, ctx: &mut Context) -> Result<()> { + Ok(()) + } + } + + impl BankAPI for Bank {fn get_balance(&self, ctx: &Context, account: AccountID, denom: &str) -> Result { + self.balances.get(ctx, (account, denom)) + } + fn send(&self, ctx: &mut Context, to: AccountID, amount: &[Coin], evt: &mut EventBus) -> Result<()> { for coin in amount { - self.balances.safe_sub(ctx, (ctx.sender(), coin.denom), coin.amount)?; + self.balances.safe_sub(ctx, (ctx.caller(), coin.denom), coin.amount)?; self.balances.add(ctx, (to, coin.denom), coin.amount)?; - evt.emit(EventSend { - from: ctx.sender(), - to, - coin: coin.clone(), - })?; + // evt.emit(EventSend { + // from: ctx.sender(), + // to, + // coin: coin.clone(), + // })?; } Ok(()) } diff --git a/rust/schema/src/lib.rs b/rust/schema/src/lib.rs index 1d22e90e8fa8..954fd4cf6be9 100644 --- a/rust/schema/src/lib.rs +++ b/rust/schema/src/lib.rs @@ -27,4 +27,5 @@ mod bump; pub mod schema; mod message; -pub use value::SchemaValue; \ No newline at end of file +pub use value::SchemaValue; +pub use state_object::Str; \ No newline at end of file diff --git a/rust/schema/src/state_object/key_field.rs b/rust/schema/src/state_object/key_field.rs index fb757266eac6..9a7bc1a9bdc0 100644 --- a/rust/schema/src/state_object/key_field.rs +++ b/rust/schema/src/state_object/key_field.rs @@ -3,6 +3,7 @@ use crate::decoder::DecodeError; use crate::encoder::EncodeError; use crate::mem::MemoryManager; use crate::state_object::value_field::ObjectFieldValue; +use crate::Str; /// This trait is implemented for types that can be used as key fields in state objects. pub trait KeyFieldValue: ObjectFieldValue { @@ -106,7 +107,7 @@ impl KeyFieldValue for ixc_message_api::AccountID { } } -impl KeyFieldValue for str { +impl KeyFieldValue for Str { fn out_size<'a>(key: &Self::In<'a>) -> usize { key.len() + 4 } fn out_size_terminal<'a>(key: &Self::In<'a>) -> usize { key.len() } } diff --git a/rust/schema/src/state_object/mod.rs b/rust/schema/src/state_object/mod.rs index 2695fe79de9c..cfce7e1ebd4c 100644 --- a/rust/schema/src/state_object/mod.rs +++ b/rust/schema/src/state_object/mod.rs @@ -8,7 +8,7 @@ mod prefix; mod field_types; pub use value::{ObjectValue, encode_object_value, decode_object_value}; -pub use value_field::ObjectFieldValue; +pub use value_field::{ObjectFieldValue, Str}; pub use key_field::KeyFieldValue; pub use prefix::PrefixKey; pub use key::{ObjectKey, encode_object_key, decode_object_key}; diff --git a/rust/schema/src/state_object/value_field.rs b/rust/schema/src/state_object/value_field.rs index 37e298905efb..393e81c19445 100644 --- a/rust/schema/src/state_object/value_field.rs +++ b/rust/schema/src/state_object/value_field.rs @@ -56,7 +56,9 @@ impl ObjectFieldValue for bool { type In<'a> = bool; type Out<'a> = bool; } -impl ObjectFieldValue for str { +/// This type is used to represent a borrowed &str in a state object. +pub struct Str; +impl ObjectFieldValue for Str { type In<'a> = &'a str; type Out<'a> = &'a str; } diff --git a/rust/schema_macros/src/lib.rs b/rust/schema_macros/src/lib.rs index 3360e7f70dac..2d217d3ee6e0 100644 --- a/rust/schema_macros/src/lib.rs +++ b/rust/schema_macros/src/lib.rs @@ -45,7 +45,7 @@ fn derive_struct_schema(input: &syn::DeriveInput, str: &DataStruct) -> manyhow:: let sealed = has_attribute(&input.attrs, "sealed"); let non_exhaustive = has_attribute(&input.attrs, "non_exhaustive"); if !sealed && !non_exhaustive { - bail!("struct must have either a #[sealed] or #[non_exhaustive] attribute to indicate whether adding new fields is or is not a breaking change") + bail!("struct must have either a #[sealed] or #[non_exhaustive] attribute to indicate whether adding new fields is or is not a breaking change. Only sealed structs can be used as input types and cannot have new fields added.") } if sealed && non_exhaustive { bail!("struct cannot be both sealed and non_exhaustive") diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 607db421d8cb..6fe780a269cf 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -8,7 +8,7 @@ pub use ixc_core::resource::Resources; #[doc(inline)] pub use ixc_message_api::AccountID; #[doc(inline)] -pub use ixc_schema::{SchemaValue}; +pub use ixc_schema::{SchemaValue, Str}; #[doc(inline)] pub use state_objects::*; #[doc(inline)] diff --git a/rust/state_objects/src/accumulator.rs b/rust/state_objects/src/accumulator.rs index 770658e5c415..439275088cda 100644 --- a/rust/state_objects/src/accumulator.rs +++ b/rust/state_objects/src/accumulator.rs @@ -1,5 +1,55 @@ -pub struct Accumulator {} +//! A u128 accumulator map. +use std::borrow::Borrow; +use ixc_core::{Context, Result}; +use ixc_core::resource::{InitializationError, StateObjectResource}; +use ixc_schema::state_object::ObjectKey; +use crate::Map; +// pub struct Accumulator {} + +/// A map from keys to 128-bit unsigned integers that act as accumulators. pub struct AccumulatorMap { - _phantom: std::marker::PhantomData, + map: Map, +} + +impl AccumulatorMap { + /// Gets the current value for the given key, defaulting always to 0. + pub fn get<'a, L>(&self, ctx: &Context, key: L) -> Result + where + L: Borrow>, + { + let value = self.map.get(ctx, key)?; + Ok(value.unwrap_or_default()) + } + + /// Adds the given value to the current value for the given key. + pub fn add<'a, L>(&self, ctx: &mut Context, key: L, value: u128) -> Result + where + L: Borrow>, + { + let current = self.get(ctx, key.borrow())?; + let new_value = current.checked_add(value).ok_or_else(|| ())?; + self.map.set(ctx, key.borrow(), &new_value)?; + Ok(new_value) + } + + /// Subtracts the given value from the current value for the given key, + /// returning an error if the subtraction would result in a negative value. + pub fn safe_sub<'a, L>(&self, ctx: &mut Context, key: L, value: u128) -> Result + where + L: Borrow>, + { + let current = self.get(ctx, key.borrow())?; + let new_value = current.checked_sub(value).ok_or_else(|| ())?; + self.map.set(ctx, key.borrow(), &new_value)?; + Ok(new_value) + } +} + +unsafe impl StateObjectResource for AccumulatorMap { + unsafe fn new(scope: &[u8], prefix: u8) -> std::result::Result { + Ok(AccumulatorMap { + map: Map::new(scope, prefix)?, + }) + } } \ No newline at end of file diff --git a/rust/state_objects/src/lib.rs b/rust/state_objects/src/lib.rs index 6e9601c41114..165f072c5295 100644 --- a/rust/state_objects/src/lib.rs +++ b/rust/state_objects/src/lib.rs @@ -7,17 +7,17 @@ mod item; mod errors; mod index; mod unique; -mod uint_map; +// mod uint_map; mod ordered_map; mod ordered_set; mod table; -mod accumulator; +pub mod accumulator; pub use map::{Map}; pub use set::{Set}; pub use item::{Item}; pub use index::{Index}; pub use unique::{UniqueIndex}; -// pub use uint_map::{UIntMap}; +pub use accumulator::{AccumulatorMap}; pub use ordered_map::{OrderedMap}; pub use ordered_set::{OrderedSet}; \ No newline at end of file diff --git a/rust/state_objects/src/map.rs b/rust/state_objects/src/map.rs index 1cf58a6bf2fb..7345b4c01b88 100644 --- a/rust/state_objects/src/map.rs +++ b/rust/state_objects/src/map.rs @@ -16,7 +16,8 @@ use ixc_schema::state_object::{decode_object_value, encode_object_key, encode_ob /// A key-value map. pub struct Map { - _phantom: std::marker::PhantomData<(K, V)>, + _k: std::marker::PhantomData, + _v: std::marker::PhantomData, #[cfg(feature = "std")] prefix: Vec, // TODO no_std prefix @@ -133,7 +134,8 @@ unsafe impl StateObjectResource for Map { let mut prefix = Vec::from(scope); prefix.push(p); Ok(Self { - _phantom: std::marker::PhantomData, + _k: std::marker::PhantomData, + _v: std::marker::PhantomData, #[cfg(feature = "std")] prefix, }) diff --git a/rust/state_objects/src/uint_map.rs b/rust/state_objects/src/uint_map.rs index 36ce3ae962c7..f5fe29337d62 100644 --- a/rust/state_objects/src/uint_map.rs +++ b/rust/state_objects/src/uint_map.rs @@ -8,11 +8,13 @@ pub struct UIntMap { map: Map, } -pub trait UInt: ObjectFieldValue=Self, Out<'static>=Self> + Sized + Clone + 'static +pub trait UInt: ObjectFieldValue +where + for<'a> ::In: Into, + ::Out: From, { fn add(self, other: Self) -> Option; fn sub(self, other: Self) -> Option; - const ZERO: Self; } impl UInt for u128 { @@ -23,19 +25,16 @@ impl UInt for u128 { fn sub(self, other: Self) -> Option { self.checked_sub(other) } - - const ZERO: Self = 0; } impl UIntMap { /// Gets the current value for the given key, defaulting always to 0. - pub fn get<'a, L>(&self, ctx: &Context, key: L) -> Result + pub fn get<'a, L>(&self, ctx: &'a Context, key: L) -> Result where L: Borrow>, { - // let value = self.map.get(ctx, key)?.clone(); - // Ok(value.unwrap_or(V::ZERO)) - todo!() + let value = self.map.get(ctx, key)?; + Ok(value.unwrap_or_default()) } /// Adds the given value to the current value for the given key.