Skip to content

Commit

Permalink
Use the view system APIs to read and write simple storage states (#1453)
Browse files Browse the repository at this point in the history
* Use `view_system_api` for `SimpleStateStorage`

Prepare to remove the APIs specific to the `SimpleStateStorage`.

* Remove `load` and `store` contract system APIs

They are no longer needed.

* Use view system API to load state for service

Prepare to remove system APIs specific to the simple state storage.

* Remove test for mock blob state API

The APIs are being removed.

* Remove `load` service system API

No longer needed.

* Remove `load` and `store` from mock system API

The APIs for the blob storage are being removed.
  • Loading branch information
jvff authored Jan 9, 2024
1 parent 0f501d6 commit b4ec74f
Show file tree
Hide file tree
Showing 11 changed files with 42 additions and 155 deletions.
3 changes: 0 additions & 3 deletions linera-sdk/mock_system_api.wit
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ enum log-level {
error,
}

mocked-load: func() -> list<u8>
mocked-store: func(value: list<u8>) -> bool

mocked-read-multi-values-bytes: func(keys: list<list<u8>>) -> list<option<list<u8>>>
mocked-read-value-bytes: func(key: list<u8>) -> option<list<u8>>
mocked-find-keys: func(prefix: list<u8>) -> list<list<u8>>
Expand Down
55 changes: 0 additions & 55 deletions linera-sdk/src/bin/linera-wasm-test-runner/mock_system_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,30 +369,6 @@ pub fn add_to_linker(linker: &mut Linker<Resources>) -> Result<()> {
})
},
)?;
linker.func_wrap1_async(
"contract_system_api",
"load: func() -> list<u8>",
move |mut caller: Caller<'_, Resources>, return_offset: i32| {
Box::new(async move {
let function = get_function(&mut caller, "mocked-load: func() -> list<u8>").expect(
"Missing `mocked-load` function in the module. \
Please ensure `linera_sdk::test::mock_application_state` was called",
);

let (result_offset,) = function
.typed::<(), (i32,), _>(&mut caller)
.expect("Incorrect `mocked-load` function signature")
.call_async(&mut caller, ())
.await
.expect(
"Failed to call `mocked-load` function. \
Please ensure `linera_sdk::test::mock_application_state` was called",
);

copy_memory_slices(&mut caller, result_offset, return_offset, 8);
})
},
)?;

linker.func_wrap1_async(
"service_system_api",
Expand Down Expand Up @@ -604,36 +580,6 @@ pub fn add_to_linker(linker: &mut Linker<Resources>) -> Result<()> {
})
},
)?;
linker.func_wrap0_async(
"service_system_api",
"load::new: func() -> handle<load>",
move |_: Caller<'_, Resources>| Box::new(async move { 0 }),
)?;
linker.func_wrap2_async(
"service_system_api",
"load::wait: func(self: handle<load>) -> result<list<u8>, string>",
move |mut caller: Caller<'_, Resources>, _handle: i32, return_offset: i32| {
Box::new(async move {
let function = get_function(&mut caller, "mocked-load: func() -> list<u8>").expect(
"Missing `mocked-load` function in the module. \
Please ensure `linera_sdk::test::mock_application_state` was called",
);

let (result_offset,) = function
.typed::<(), (i32,), _>(&mut caller)
.expect("Incorrect `mocked-load` function signature")
.call_async(&mut caller, ())
.await
.expect(
"Failed to call `mocked-load` function. \
Please ensure `linera_sdk::test::mock_application_state` was called",
);

store_in_memory(&mut caller, return_offset, 0_i32);
copy_memory_slices(&mut caller, result_offset, return_offset + 4, 8);
})
},
)?;
linker.func_wrap15_async(
"service_system_api",
"try-query-application: func(\
Expand Down Expand Up @@ -1123,7 +1069,6 @@ pub fn add_to_linker(linker: &mut Linker<Resources>) -> Result<()> {
)?;

let resource_names = [
"load",
"read-multi-values-bytes",
"read-value-bytes",
"find-keys",
Expand Down
28 changes: 24 additions & 4 deletions linera-sdk/src/contract/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
//! contract type that implements [`Contract`].
use crate::{
contract::system_api, views::ViewStorageContext, Contract, SimpleStateStorage, ViewStateStorage,
contract::system_api,
views::{AppStateStore, ViewStorageContext},
Contract, SimpleStateStorage, ViewStateStorage,
};
use async_trait::async_trait;
use futures::TryFutureExt;
use linera_views::views::RootView;
use linera_views::{batch::Batch, common::KeyValueStore, views::RootView};
use serde::{de::DeserializeOwned, Serialize};
use std::future::Future;

Expand Down Expand Up @@ -58,11 +60,29 @@ where
Application: Contract + Default + DeserializeOwned + Serialize + Send + 'static,
{
async fn load() -> Application {
system_api::load().expect("Failed to lock contract state")
let maybe_bytes = AppStateStore
.read_value_bytes(&[])
.await
.expect("Failed to read application state bytes");

if let Some(bytes) = maybe_bytes {
bcs::from_bytes(&bytes).expect("Failed to deserialize application state")
} else {
Application::default()
}
}

async fn store(state: Application) {
system_api::store(state).await;
let mut batch = Batch::new();

batch
.put_key_value(vec![], &state)
.expect("Failed to serialize application state");

AppStateStore
.write_batch(batch, &[])
.await
.expect("Failed to store application state bytes");
}
}

Expand Down
3 changes: 1 addition & 2 deletions linera-sdk/src/contract/system_api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ mod private;
pub mod private;

pub(crate) use self::private::{
call_application, call_session, current_application_parameters, load, load_view, store,
store_view,
call_application, call_session, current_application_parameters, load_view, store_view,
};
use super::contract_system_api as wit;
use linera_base::{
Expand Down
30 changes: 0 additions & 30 deletions linera-sdk/src/contract/system_api/private.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,12 @@ use super::super::contract_system_api as wit;
use crate::views::ViewStorageContext;
use linera_base::identifiers::{ApplicationId, SessionId};
use linera_views::views::{RootView, View};
use serde::{de::DeserializeOwned, Serialize};

/// Retrieves the current application parameters.
pub fn current_application_parameters() -> Vec<u8> {
wit::application_parameters()
}

/// Deserializes the application state or creates a new one if the `bytes` vector is empty.
fn deserialize_state<State>(bytes: Vec<u8>) -> State
where
State: Default + DeserializeOwned,
{
if bytes.is_empty() {
State::default()
} else {
bcs::from_bytes(&bytes).expect("Invalid application state")
}
}

/// Loads the application state.
pub fn load<State>() -> Option<State>
where
State: Default + DeserializeOwned,
{
let state_bytes = wit::load();
Some(deserialize_state(state_bytes))
}

/// Saves the application state.
pub async fn store<State>(state: State)
where
State: Serialize,
{
wit::store(&bcs::to_bytes(&state).expect("State serialization failed"));
}

/// Loads the application state or create a new one if it doesn't exist.
pub async fn load_view<State: View<ViewStorageContext>>() -> State {
let context = ViewStorageContext::default();
Expand Down
17 changes: 14 additions & 3 deletions linera-sdk/src/service/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
use crate::{
service::{system_api, wit_types},
views::ViewStorageContext,
views::{AppStateStore, ViewStorageContext},
Service, SimpleStateStorage, ViewStateStorage,
};
use async_trait::async_trait;
use linera_views::views::RootView;
use linera_views::{common::KeyValueStore, views::RootView};
use serde::{de::DeserializeOwned, Serialize};
use std::sync::Arc;

Expand All @@ -36,7 +36,18 @@ where
context: wit_types::QueryContext,
argument: Vec<u8>,
) -> Result<Vec<u8>, String> {
let application: Arc<Application> = Arc::new(system_api::load().await);
let maybe_bytes = AppStateStore
.read_value_bytes(&[])
.await
.expect("Failed to read application state bytes");

let state = if let Some(bytes) = maybe_bytes {
bcs::from_bytes(&bytes).expect("Failed to deserialize application state")
} else {
Application::default()
};

let application: Arc<Application> = Arc::new(state);
let argument: Application::Query =
serde_json::from_slice(&argument).map_err(|e| e.to_string())?;
let query_response = application
Expand Down
4 changes: 1 addition & 3 deletions linera-sdk/src/service/system_api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ mod private;
#[cfg(any(test, feature = "test"))]
pub mod private;

pub(crate) use self::private::{
current_application_parameters, load, load_view, query_application,
};
pub(crate) use self::private::{current_application_parameters, load_view, query_application};
use super::service_system_api as wit;
use linera_base::{
data_types::{Amount, Timestamp},
Expand Down
18 changes: 1 addition & 17 deletions linera-sdk/src/service/system_api/private.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,9 @@
//! that shouldn't be used by applications directly.
use super::super::service_system_api as wit;
use crate::{util::yield_once, views::ViewStorageContext};
use crate::views::ViewStorageContext;
use linera_base::identifiers::ApplicationId;
use linera_views::views::View;
use serde::de::DeserializeOwned;

/// Loads the application state, without locking it for writes.
pub async fn load<State>() -> State
where
State: Default + DeserializeOwned,
{
let promise = wit::Load::new();
yield_once().await;
let bytes = promise.wait().expect("Failed to load application state");
if bytes.is_empty() {
State::default()
} else {
bcs::from_bytes(&bytes).expect("Invalid application state")
}
}

/// Helper function to load the service state or create a new one if it doesn't exist.
pub async fn load_view<State: View<ViewStorageContext>>() -> State {
Expand Down
23 changes: 0 additions & 23 deletions linera-sdk/src/test/unit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ static mut MOCK_APPLICATION_PARAMETERS: Option<Vec<u8>> = None;
static mut MOCK_SYSTEM_BALANCE: Option<Amount> = None;
static mut MOCK_SYSTEM_TIMESTAMP: Option<Timestamp> = None;
static mut MOCK_LOG_COLLECTOR: Vec<(log::Level, String)> = Vec::new();
static mut MOCK_APPLICATION_STATE: Option<Vec<u8>> = None;
static mut MOCK_KEY_VALUE_STORE: Option<MemoryContext<()>> = None;
static mut MOCK_TRY_QUERY_APPLICATION: Option<
Box<dyn FnMut(ApplicationId, Vec<u8>) -> Result<Vec<u8>, String>>,
Expand Down Expand Up @@ -74,11 +73,6 @@ pub fn log_messages() -> Vec<(log::Level, String)> {
unsafe { MOCK_LOG_COLLECTOR.clone() }
}

/// Sets the mocked application state.
pub fn mock_application_state(state: impl Into<Option<Vec<u8>>>) {
unsafe { MOCK_APPLICATION_STATE = state.into() };
}

/// Initializes and returns a view context for using as the mocked key-value store.
pub fn mock_key_value_store() -> MemoryContext<()> {
let store = linera_views::memory::create_memory_context();
Expand Down Expand Up @@ -151,23 +145,6 @@ impl wit::MockSystemApi for MockSystemApi {
unsafe { MOCK_LOG_COLLECTOR.push((level.into(), message)) }
}

fn mocked_load() -> Vec<u8> {
unsafe { MOCK_APPLICATION_STATE.clone() }.expect(
"Unexpected call to the `load` system API. \
Please call `mock_application_state` first",
)
}

fn mocked_store(state: Vec<u8>) -> bool {
assert!(
unsafe { MOCK_APPLICATION_STATE.is_some() },
"Unexpected call to `store_and_unlock` system API. \
Please call `mock_application_state` first."
);
unsafe { MOCK_APPLICATION_STATE = Some(state) };
true
}

fn mocked_read_multi_values_bytes(keys: Vec<Vec<u8>>) -> Vec<Option<Vec<u8>>> {
unsafe { MOCK_KEY_VALUE_STORE.as_mut() }
.expect(
Expand Down
1 change: 1 addition & 0 deletions linera-sdk/src/views/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
mod system_api;

pub(crate) use self::system_api::AppStateStore;
pub use self::system_api::ViewStorageContext;
pub use linera_views::{
self,
Expand Down
15 changes: 0 additions & 15 deletions linera-sdk/wasm-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,21 +146,6 @@ fn mock_service_log() {
assert_eq!(test::log_messages(), expected);
}

/// Test loading a mocked application state without locking it.
#[webassembly_test]
fn mock_load_blob_state() {
let state = vec![0, 1, 2, 3, 4, 5, 6];

test::mock_application_state(
bcs::to_bytes(&state).expect("Failed to serialize vector using BCS"),
);

assert_eq!(
service::system_api::private::load::<Vec<u8>>().blocking_wait(),
state
);
}

/// A dummy view to test the key value store.
#[derive(RootView)]
struct DummyView<C> {
Expand Down

0 comments on commit b4ec74f

Please sign in to comment.