Skip to content

Commit

Permalink
WIP on state objects & bank
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronc committed Oct 4, 2024
1 parent 3e44e3a commit c08b8ce
Show file tree
Hide file tree
Showing 11 changed files with 101 additions and 32 deletions.
36 changes: 25 additions & 11 deletions rust/examples/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,53 @@
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,
}

#[handler_api]
pub trait BankAPI {
fn get_balance(&self, ctx: &Context, account: AccountID, denom: &str) -> Result<u128>;
fn send(&self, ctx: &mut Context, to: AccountID, amount: &[Coin], evt: &mut EventBus<EventSend>) -> 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<u128> {
self.balances.get(ctx, (account, denom))
}

fn send(&self, ctx: &mut Context, to: AccountID, amount: &[Coin], evt: &mut EventBus<EventSend>) -> 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(())
}
Expand Down
3 changes: 2 additions & 1 deletion rust/schema/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ mod bump;
pub mod schema;
mod message;

pub use value::SchemaValue;
pub use value::SchemaValue;
pub use state_object::Str;
3 changes: 2 additions & 1 deletion rust/schema/src/state_object/key_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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() }
}
Expand Down
2 changes: 1 addition & 1 deletion rust/schema/src/state_object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
4 changes: 3 additions & 1 deletion rust/schema/src/state_object/value_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion rust/schema_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
54 changes: 52 additions & 2 deletions rust/state_objects/src/accumulator.rs
Original file line number Diff line number Diff line change
@@ -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<K> {
_phantom: std::marker::PhantomData<K>,
map: Map<K, u128>,
}

impl<K: ObjectKey> AccumulatorMap<K> {
/// Gets the current value for the given key, defaulting always to 0.
pub fn get<'a, L>(&self, ctx: &Context, key: L) -> Result<u128>
where
L: Borrow<K::In<'a>>,
{
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<u128>
where
L: Borrow<K::In<'a>>,
{
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<u128>
where
L: Borrow<K::In<'a>>,
{
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 <K> StateObjectResource for AccumulatorMap<K> {
unsafe fn new(scope: &[u8], prefix: u8) -> std::result::Result<Self, InitializationError> {
Ok(AccumulatorMap {
map: Map::new(scope, prefix)?,
})
}
}
6 changes: 3 additions & 3 deletions rust/state_objects/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
6 changes: 4 additions & 2 deletions rust/state_objects/src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ use ixc_schema::state_object::{decode_object_value, encode_object_key, encode_ob

/// A key-value map.
pub struct Map<K, V> {
_phantom: std::marker::PhantomData<(K, V)>,
_k: std::marker::PhantomData<K>,
_v: std::marker::PhantomData<V>,
#[cfg(feature = "std")]
prefix: Vec<u8>,
// TODO no_std prefix
Expand Down Expand Up @@ -133,7 +134,8 @@ unsafe impl<K, V> StateObjectResource for Map<K, V> {
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,
})
Expand Down
15 changes: 7 additions & 8 deletions rust/state_objects/src/uint_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ pub struct UIntMap<K, V: UInt> {
map: Map<K, V>,
}

pub trait UInt: ObjectFieldValue<In<'static>=Self, Out<'static>=Self> + Sized + Clone + 'static
pub trait UInt: ObjectFieldValue
where
for<'a> <Self as ObjectFieldValue>::In: Into<u128>,
<Self as ObjectFieldValue>::Out: From<u128>,
{
fn add(self, other: Self) -> Option<Self>;
fn sub(self, other: Self) -> Option<Self>;
const ZERO: Self;
}

impl UInt for u128 {
Expand All @@ -23,19 +25,16 @@ impl UInt for u128 {
fn sub(self, other: Self) -> Option<Self> {
self.checked_sub(other)
}

const ZERO: Self = 0;
}

impl<K: ObjectKey, V: UInt> UIntMap<K, V> {
/// Gets the current value for the given key, defaulting always to 0.
pub fn get<'a, L>(&self, ctx: &Context, key: L) -> Result<V>
pub fn get<'a, L>(&self, ctx: &'a Context, key: L) -> Result<V>
where
L: Borrow<K::In<'a>>,
{
// 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.
Expand Down

0 comments on commit c08b8ce

Please sign in to comment.