Skip to content

Commit

Permalink
feat(dan/engine): hook up template calls to backend state
Browse files Browse the repository at this point in the history
  • Loading branch information
sdbondi committed Aug 5, 2022
1 parent 12355b0 commit f3ad0fb
Show file tree
Hide file tree
Showing 14 changed files with 141 additions and 56 deletions.
2 changes: 1 addition & 1 deletion dan_layer/engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ edition = "2018"
[dependencies]
tari_common = { path = "../../common" }
tari_common_types = { path = "../../base_layer/common_types" }
tari_crypto = { git = "https://github.com/tari-project/tari-crypto.git", tag = "v0.15.3" }
tari_crypto = { git = "https://github.com/tari-project/tari-crypto.git", tag = "v0.15.4" }
tari_dan_common_types = { path = "../common_types" }
tari_mmr = { path = "../../base_layer/mmr" }
tari_template_abi = { path = "../template_abi" }
Expand Down
4 changes: 3 additions & 1 deletion dan_layer/engine/src/instruction/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

use tari_template_types::models::PackageId;

use crate::wasm::WasmExecutionError;
use crate::{runtime::RuntimeError, wasm::WasmExecutionError};

#[derive(Debug, thiserror::Error)]
pub enum InstructionError {
Expand All @@ -32,4 +32,6 @@ pub enum InstructionError {
PackageNotFound { package_id: PackageId },
#[error("Invalid template")]
TemplateNameNotFound { name: String },
#[error(transparent)]
RuntimeError(#[from] RuntimeError),
}
4 changes: 3 additions & 1 deletion dan_layer/engine/src/instruction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ mod signature;
pub use signature::InstructionSignature;
use tari_template_types::models::PackageId;

use crate::models::ComponentId;

#[derive(Debug, Clone)]
pub enum Instruction {
CallFunction {
Expand All @@ -42,7 +44,7 @@ pub enum Instruction {
},
CallMethod {
package_id: PackageId,
component_id: String,
component_id: ComponentId,
method: String,
args: Vec<Vec<u8>>,
},
Expand Down
29 changes: 18 additions & 11 deletions dan_layer/engine/src/instruction/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

use std::{collections::HashMap, sync::Arc};

use tari_template_abi::encode_with_len;
use tari_template_types::models::PackageId;

use crate::{
Expand Down Expand Up @@ -59,7 +60,7 @@ where TRuntimeInterface: RuntimeInterface + Clone + 'static
// TODO: implement engine
let state = Runtime::new(Arc::new(self.runtime_interface.clone()));
for instruction in instruction_set.instructions {
match instruction {
let result = match instruction {
Instruction::CallFunction {
package_id,
template,
Expand All @@ -76,8 +77,7 @@ where TRuntimeInterface: RuntimeInterface + Clone + 'static

// TODO: implement intelligent instance caching
let process = Process::start(module.clone(), state.clone(), package_id)?;
let result = process.invoke_by_name(&function, args)?;
results.push(result);
process.invoke_by_name(&function, args)?
},
Instruction::CallMethod {
package_id,
Expand All @@ -89,18 +89,25 @@ where TRuntimeInterface: RuntimeInterface + Clone + 'static
.packages
.get(&package_id)
.ok_or(InstructionError::PackageNotFound { package_id })?;
// TODO: load component, not module - component_id is currently hard-coded as the template name in
// tests
let module = package
.get_module_by_name(&component_id)
.ok_or(InstructionError::TemplateNameNotFound { name: component_id })?;

let component = self.runtime_interface.get_component(&component_id)?;
let module = package.get_module_by_name(&component.module_name).ok_or_else(|| {
InstructionError::TemplateNameNotFound {
name: component.module_name.clone(),
}
})?;

let mut final_args = Vec::with_capacity(args.len() + 1);
final_args.push(encode_with_len(&component));
final_args.extend(args);

// TODO: implement intelligent instance caching
let process = Process::start(module.clone(), state.clone(), package_id)?;
let result = process.invoke_by_name(&method, args)?;
results.push(result);
process.invoke_by_name(&method, final_args)?
},
}
};

results.push(result);
}

Ok(results)
Expand Down
7 changes: 4 additions & 3 deletions dan_layer/engine/src/models/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,19 @@
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use tari_template_abi::CreateComponentArg;
use tari_template_abi::{CreateComponentArg, Decode, Encode};
use tari_template_types::{
models::{ContractAddress, PackageId},
Hash,
};

pub type ComponentId = Hash;

#[derive(Debug, Clone, Encode, Decode)]
pub struct Component {
pub contract_address: ContractAddress,
pub package_id: PackageId,
pub component_name: String,
pub module_name: String,
pub state: Vec<u8>,
}

Expand All @@ -40,7 +41,7 @@ impl From<CreateComponentArg> for Component {
Self {
contract_address: arg.contract_address,
package_id: arg.package_id,
component_name: arg.component_name,
module_name: arg.component_name,
state: arg.state,
}
}
Expand Down
14 changes: 11 additions & 3 deletions dan_layer/engine/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ use std::{
use tari_common_types::types::FixedHash;
use tari_template_abi::LogLevel;

use crate::models::{Bucket, Component, ComponentId};
use crate::{
models::{Bucket, Component, ComponentId},
state_store::StateStoreError,
};

#[derive(Clone)]
pub struct Runtime {
Expand Down Expand Up @@ -66,11 +69,16 @@ pub struct ChangeTracker {

#[derive(Debug, thiserror::Error)]
pub enum RuntimeError {
#[error("todo")]
Todo,
#[error("State DB error: {0}")]
StateDbError(#[from] anyhow::Error),
#[error("State storage error: {0}")]
StateStoreError(#[from] StateStoreError),
#[error("Component not found with id '{id}'")]
ComponentNotFound { id: ComponentId },
}

pub trait RuntimeInterface: Send + Sync {
fn emit_log(&self, level: LogLevel, message: &str);
fn create_component(&self, component: Component) -> Result<ComponentId, RuntimeError>;
fn get_component(&self, component_id: &ComponentId) -> Result<Component, RuntimeError>;
}
8 changes: 8 additions & 0 deletions dan_layer/engine/src/state_store/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,20 @@ impl<'a> StateReader for MemoryTransaction<RwLockReadGuard<'a, InnerKvMap>> {
fn get_state_raw(&self, key: &[u8]) -> Result<Option<Vec<u8>>, StateStoreError> {
Ok(self.pending.get(key).cloned().or_else(|| self.guard.get(key).cloned()))
}

fn exists(&self, key: &[u8]) -> Result<bool, StateStoreError> {
Ok(self.pending.contains_key(key) || self.guard.contains_key(key))
}
}

impl<'a> StateReader for MemoryTransaction<RwLockWriteGuard<'a, InnerKvMap>> {
fn get_state_raw(&self, key: &[u8]) -> Result<Option<Vec<u8>>, StateStoreError> {
Ok(self.pending.get(key).cloned().or_else(|| self.guard.get(key).cloned()))
}

fn exists(&self, key: &[u8]) -> Result<bool, StateStoreError> {
Ok(self.pending.contains_key(key) || self.guard.contains_key(key))
}
}

impl<'a> StateWriter for MemoryTransaction<RwLockWriteGuard<'a, InnerKvMap>> {
Expand Down
4 changes: 3 additions & 1 deletion dan_layer/engine/src/state_store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

mod memory;
pub mod memory;

use std::{error::Error, io};

Expand Down Expand Up @@ -49,6 +49,8 @@ pub trait StateReader {
let value = value.map(|v| V::deserialize(&mut v.as_slice())).transpose()?;
Ok(value)
}

fn exists(&self, key: &[u8]) -> Result<bool, StateStoreError>;
}

pub trait StateWriter: StateReader {
Expand Down
23 changes: 22 additions & 1 deletion dan_layer/engine/tests/mock_runtime_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,29 @@ use tari_dan_engine::{
crypto,
models::{Component, ComponentId},
runtime::{RuntimeError, RuntimeInterface},
state_store::{memory::MemoryStateStore, AtomicDb, StateReader, StateWriter},
};
use tari_template_abi::LogLevel;
#[derive(Debug, Clone, Default)]
pub struct MockRuntimeInterface {
ids: Arc<AtomicU32>,
state: MemoryStateStore,
calls: Arc<RwLock<Vec<&'static str>>>,
}

impl MockRuntimeInterface {
pub fn new() -> Self {
Self {
ids: Arc::new(AtomicU32::new(0)),
state: MemoryStateStore::default(),
calls: Arc::new(RwLock::new(vec![])),
}
}

pub fn state_store(&self) -> MemoryStateStore {
self.state.clone()
}

pub fn next_id(&self) -> u32 {
self.ids.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
}
Expand All @@ -69,13 +76,27 @@ impl RuntimeInterface for MockRuntimeInterface {
log::log!(target: "tari::dan::engine::runtime", level, "{}", message);
}

fn create_component(&self, _new_component: Component) -> Result<ComponentId, RuntimeError> {
fn create_component(&self, new_component: Component) -> Result<ComponentId, RuntimeError> {
self.calls.write().unwrap().push("create_component");
let component_id: [u8; 32] = crypto::hasher("component")
.chain(self.next_id().to_le_bytes())
.finalize()
.into();

let mut tx = self.state.write_access().map_err(RuntimeError::StateDbError)?;
tx.set_state(&component_id, new_component)?;
self.state.commit(tx).map_err(RuntimeError::StateDbError)?;

Ok(component_id.into())
}

fn get_component(&self, component_id: &ComponentId) -> Result<Component, RuntimeError> {
let component = self
.state
.read_access()
.map_err(RuntimeError::StateDbError)?
.get_state(component_id)?
.ok_or(RuntimeError::ComponentNotFound { id: *component_id })?;
Ok(component)
}
}
35 changes: 28 additions & 7 deletions dan_layer/engine/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ use tari_crypto::ristretto::RistrettoSecretKey;
use tari_dan_engine::{
crypto::create_key_pair,
instruction::{Instruction, InstructionBuilder, InstructionProcessor},
models::ComponentId,
models::{Component, ComponentId},
packager::Package,
state_store::{memory::MemoryStateStore, AtomicDb, StateReader},
wasm::compile::compile_template,
};
use tari_template_abi::encode_with_len;
Expand All @@ -47,6 +48,7 @@ fn test_hello_world() {
fn test_state() {
// TODO: use the Component and ComponentId types in the template
let template_test = TemplateTest::new("State".to_string(), "tests/state".to_string());
let store = template_test.state_store();

// constructor
let component1: ComponentId = template_test.call_function("new".to_string(), vec![]);
Expand All @@ -56,19 +58,34 @@ fn test_state() {
let component2: ComponentId = template_test.call_function("new".to_string(), vec![]);
assert_ne!(component1, component2);

let component: Component = store
.read_access()
.unwrap()
.get_state(&component1)
.unwrap()
.expect("component1 not found");
assert_eq!(component.module_name, "State");
let component: Component = store
.read_access()
.unwrap()
.get_state(&component2)
.unwrap()
.expect("component2 not found");
assert_eq!(component.module_name, "State");

// call the "set" method to update the instance value
let new_value = 20_u32;
template_test.call_method::<()>("State".to_string(), "set".to_string(), vec![
template_test.call_method::<()>(component2, "set".to_string(), vec![
encode_with_len(&component2),
encode_with_len(&new_value),
]);

// call the "get" method to get the current value
let value: u32 = template_test.call_method("State".to_string(), "get".to_string(), vec![encode_with_len(
&component2,
)]);
// TODO: when state storage is implemented in the engine, assert the previous setted value (20_u32)
let value: u32 = template_test.call_method(component2, "get".to_string(), vec![encode_with_len(&component2)]);

// TODO: component needs to be saved in the macro code
assert_eq!(value, 0);
// assert_eq!(value, new_value);
}

struct TemplateTest {
Expand Down Expand Up @@ -99,6 +116,10 @@ impl TemplateTest {
}
}

pub fn state_store(&self) -> MemoryStateStore {
self.runtime_interface.state_store()
}

pub fn assert_calls(&self, expected: &[&'static str]) {
let calls = self.runtime_interface.get_calls();
assert_eq!(calls, expected);
Expand All @@ -124,7 +145,7 @@ impl TemplateTest {
result[0].decode::<T>().unwrap()
}

pub fn call_method<T>(&self, component_id: String, method_name: String, args: Vec<Vec<u8>>) -> T
pub fn call_method<T>(&self, component_id: ComponentId, method_name: String, args: Vec<Vec<u8>>) -> T
where T: BorshDeserialize {
let instruction = InstructionBuilder::new()
.add_instruction(Instruction::CallMethod {
Expand Down
6 changes: 6 additions & 0 deletions dan_layer/storage_lmdb/src/engine_state_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ impl<'a, T: Deref<Target = ConstTransaction<'a>>> StateReader for LmdbTransactio
.to_opt()
.map_err(StateStoreError::custom)
}

fn exists(&self, key: &[u8]) -> Result<bool, StateStoreError> {
Ok(self.get_state_raw(key)?.is_some())
}
}

impl<'a> StateWriter for LmdbTransaction<WriteTransaction<'a>> {
Expand Down Expand Up @@ -128,6 +132,7 @@ mod tests {
{
let mut access = store.write_access().unwrap();
access.set_state(b"abc", user_data.clone()).unwrap();
assert!(access.exists(b"abc").unwrap());
let res = access.get_state(b"abc").unwrap();
assert_eq!(res, Some(user_data.clone()));
let res = access.get_state::<_, UserData>(b"def").unwrap();
Expand All @@ -139,6 +144,7 @@ mod tests {
let access = store.read_access().unwrap();
let res = access.get_state::<_, UserData>(b"abc").unwrap();
assert_eq!(res, None);
assert!(!access.exists(b"abc").unwrap());
}

{
Expand Down
Loading

0 comments on commit f3ad0fb

Please sign in to comment.