Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cached state values in the program state #840

Merged
merged 39 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
105b87a
add cache
iFrostizz Apr 17, 2024
9babe31
implement cache reads
iFrostizz Apr 18, 2024
7c33c70
actually cache reads
iFrostizz Apr 18, 2024
d29598e
implement cache delete and handle borsch errors
iFrostizz Apr 18, 2024
78068ba
use only one cache map
iFrostizz Apr 18, 2024
92e7310
remove flushed bool
iFrostizz Apr 18, 2024
d1d4ff9
Merge branch 'main' into cached_rw
iFrostizz Apr 18, 2024
1635bd5
root of transactions by stateless merkledb
bianyuanop Jan 19, 2024
aa44f97
make root generation a function
iFrostizz Apr 26, 2024
68e49b6
preallocate memory for merkle array and consumebytes flag
bianyuanop Jan 19, 2024
ce00289
add <*.code-workspace> to .gitignore and remove it from git commit
bianyuanop Jan 20, 2024
e551960
move root generation func to merkle package, tx root by items of [txI…
bianyuanop Jan 20, 2024
7442836
rebase & blk marshal/unmarshal & merkleroot to ids.ID
bianyuanop Jan 28, 2024
07993bf
write benches for the merkle package
iFrostizz Mar 18, 2024
214005b
use crypto/rand, fix var name, report allocs
iFrostizz Mar 22, 2024
058d7e7
put the 10k bench back
iFrostizz Mar 22, 2024
4aec589
pass config by parameter
iFrostizz Mar 31, 2024
d6c4913
Merge branch 'main' into cached_rw
iFrostizz Apr 26, 2024
04ec581
Merge branch 'main' into HEAD
iFrostizz Apr 26, 2024
2ff02e0
happy clippy
iFrostizz Apr 26, 2024
8d1fca0
borrow V
iFrostizz Apr 26, 2024
aa1ac4c
add TODO
iFrostizz Apr 26, 2024
fb2df78
Merge branch 'cached_rw' of github.com:iFrostizz/hypersdk into cached_rw
iFrostizz Apr 26, 2024
1cd0f97
Revert "pass config by parameter"
iFrostizz Apr 26, 2024
674d2bf
Revert "put the 10k bench back"
iFrostizz Apr 26, 2024
57e3ad4
Revert "use crypto/rand, fix var name, report allocs"
iFrostizz Apr 26, 2024
7e5b9db
Revert "write benches for the merkle package"
iFrostizz Apr 26, 2024
c6c2f6c
Revert "rebase & blk marshal/unmarshal & merkleroot to ids.ID"
iFrostizz Apr 26, 2024
0989981
Revert "move root generation func to merkle package, tx root by items…
iFrostizz Apr 26, 2024
ea05d30
Revert "add <*.code-workspace> to .gitignore and remove it from git c…
iFrostizz Apr 26, 2024
34937c0
Revert "preallocate memory for merkle array and consumebytes flag"
iFrostizz Apr 26, 2024
a72ca67
Revert "make root generation a function"
iFrostizz Apr 26, 2024
dcabc00
Revert "pass config by parameter"
iFrostizz Apr 26, 2024
6a620ca
Revert "move root generation func to merkle package, tx root by items…
iFrostizz Apr 26, 2024
255510b
Revert "preallocate memory for merkle array and consumebytes flag"
iFrostizz Apr 26, 2024
2a08609
Revert "make root generation a function"
iFrostizz Apr 26, 2024
81aef46
merge main!
iFrostizz Apr 26, 2024
5687417
merge imports
iFrostizz Apr 26, 2024
378ae12
Merge branch 'main' into cached_rw
richardpringle Apr 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion x/programs/rust/examples/counter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub fn initialize_address(context: Context, address: Address) -> bool {

if program
.state()
.get::<i64, _>(StateKeys::Counter(address))
.get::<i64>(StateKeys::Counter(address))
.is_ok()
{
panic!("counter already initialized for address")
Expand Down
6 changes: 3 additions & 3 deletions x/programs/rust/examples/token/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub fn mint_to(context: Context, recipient: Address, amount: i64) -> bool {
let Context { program } = context;
let balance = program
.state()
.get::<i64, _>(StateKey::Balance(recipient))
.get::<i64>(StateKey::Balance(recipient))
.unwrap_or_default();

program
Expand Down Expand Up @@ -90,14 +90,14 @@ pub fn transfer(context: Context, sender: Address, recipient: Address, amount: i
// ensure the sender has adequate balance
let sender_balance = program
.state()
.get::<i64, _>(StateKey::Balance(sender))
.get::<i64>(StateKey::Balance(sender))
.expect("failed to update balance");

assert!(amount >= 0 && sender_balance >= amount, "invalid input");

let recipient_balance = program
.state()
.get::<i64, _>(StateKey::Balance(recipient))
.get::<i64>(StateKey::Balance(recipient))
.unwrap_or_default();

// update balances
Expand Down
2 changes: 1 addition & 1 deletion x/programs/rust/sdk_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ pub fn state_keys(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut item_enum = parse_macro_input!(item as ItemEnum);
// add default attributes
item_enum.attrs.push(syn::parse_quote! {
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

});
item_enum.attrs.push(syn::parse_quote! {
#[repr(u8)]
Expand Down
8 changes: 7 additions & 1 deletion x/programs/rust/wasmlanche-sdk/src/program.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use std::hash::Hash;

use borsh::{BorshDeserialize, BorshSerialize};

use crate::state::Key;
iFrostizz marked this conversation as resolved.
Show resolved Hide resolved
use crate::{memory::to_host_ptr, state::Error as StateError, state::State, Params};

/// Represents the current Program in the context of the caller. Or an external
Expand All @@ -25,7 +28,10 @@ impl Program {
/// Returns a State object that can be used to interact with persistent
/// storage exposed by the host.
#[must_use]
pub fn state(&self) -> State {
pub fn state<K>(&self) -> State<K>
where
K: Into<Key> + Hash + PartialEq + Eq + Clone,
{
State::new(Program::new(*self.id()))
}

Expand Down
86 changes: 63 additions & 23 deletions x/programs/rust/wasmlanche-sdk/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{memory::from_host_ptr, program::Program};
use borsh::{BorshDeserialize, BorshSerialize};
use std::ops::Deref;
use crate::{from_host_ptr, program::Program, state::Error as StateError};
use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize};
use std::{collections::HashMap, hash::Hash, ops::Deref};

#[derive(Clone, thiserror::Error, Debug)]
pub enum Error {
Expand Down Expand Up @@ -38,27 +38,51 @@ pub enum Error {
Delete,
}

pub struct State {
pub struct State<K>
where
K: Into<Key> + Hash + PartialEq + Eq + Clone,
{
program: Program,
cache: HashMap<K, Vec<u8>>,
}

impl State {
impl<K> Drop for State<K>
where
K: Into<Key> + Hash + PartialEq + Eq + Clone,
{
fn drop(&mut self) {
if !self.cache.is_empty() {
// force flush
richardpringle marked this conversation as resolved.
Show resolved Hide resolved
self.flush().unwrap();
}
}
}

impl<K> State<K>
where
K: Into<Key> + Hash + PartialEq + Eq + Clone,
{
#[must_use]
pub fn new(program: Program) -> Self {
Self { program }
Self {
program,
cache: HashMap::new(),
}
}

/// Store a key and value to the host storage. If the key already exists,
/// the value will be overwritten.
/// # Errors
/// Returns an [Error] if the key or value cannot be
/// serialized or if the host fails to handle the operation.
pub fn store<K, V>(&self, key: K, value: &V) -> Result<(), Error>
pub fn store<V>(&mut self, key: K, value: &V) -> Result<(), Error>
where
V: BorshSerialize,
K: Into<Key>,
{
unsafe { host::put_bytes(&self.program, &key.into(), value) }
let serialized = to_vec(&value).map_err(|_| StateError::Deserialization)?;
self.cache.insert(key, serialized);

Ok(())
}

/// Get a value from the host's storage.
Expand All @@ -71,30 +95,46 @@ impl State {
/// the host fails to read the key and value.
/// # Panics
/// Panics if the value cannot be converted from i32 to usize.
pub fn get<T, K>(&self, key: K) -> Result<T, Error>
pub fn get<V>(&mut self, key: K) -> Result<V, Error>
richardpringle marked this conversation as resolved.
Show resolved Hide resolved
where
K: Into<Key>,
T: BorshDeserialize,
V: BorshDeserialize,
{
let val_ptr = unsafe { host::get_bytes(&self.program, &key.into())? };
if val_ptr < 0 {
return Err(Error::Read);
}

// Wrap in OK for now, change from_raw_ptr to return Result
from_host_ptr(val_ptr)
let val_bytes = if let Some(val) = self.cache.get(&key) {
val
} else {
let val_ptr = unsafe { host::get_bytes(&self.program, &key.clone().into())? };
if val_ptr < 0 {
return Err(Error::Read);
}

richardpringle marked this conversation as resolved.
Show resolved Hide resolved
// TODO Wrap in OK for now, change from_raw_ptr to return Result
let bytes = from_host_ptr(val_ptr)?;
self.cache.entry(key).or_insert(bytes)
};

from_slice::<V>(val_bytes).map_err(|_| StateError::Deserialization)
}

/// Delete a value from the hosts's storage.
/// # Errors
/// Returns an [Error] if the key cannot be serialized
/// or if the host fails to delete the key and the associated value
pub fn delete<K>(&self, key: K) -> Result<(), Error>
where
K: Into<Key>,
{
pub fn delete(&mut self, key: K) -> Result<(), Error> {
self.cache.remove(&key);

richardpringle marked this conversation as resolved.
Show resolved Hide resolved
unsafe { host::delete_bytes(&self.program, &key.into()) }
}

/// Apply all pending operations to storage and mark the cache as flushed
fn flush(&mut self) -> Result<(), Error> {
for (key, value) in self.cache.drain() {
unsafe {
iFrostizz marked this conversation as resolved.
Show resolved Hide resolved
host::put_bytes(&self.program, &key.into(), &value)?;
}
}

Ok(())
}
}

/// Key is a wrapper around a `Vec<u8>` that represents a key in the host storage.
Expand Down
2 changes: 1 addition & 1 deletion x/programs/rust/wasmlanche-sdk/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use borsh::{BorshDeserialize, BorshSerialize};

/// A struct that enforces a fixed length of 32 bytes which represents an address.

#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize, Hash)]
pub struct Address([u8; Self::LEN]);

impl Address {
Expand Down
Loading