Skip to content

Commit

Permalink
feat(dan/engine): add more engine primitives and add template context (
Browse files Browse the repository at this point in the history
…#4388)

Description
---
- adds `Component` primitive and adds fields to `Vault` and `Bucket`
- adds `engine()` helper for internal use in macro code
- adds `context()` helper that provides some basic execution context 
- adds `template_types` crate for common types for `tari_engine` and templates
- supports adding a component to the state store via `OP_CREATE_COMPONENT`, with updated test
- adds `exists` method to state store
- loads the component for the `CallMethod` instruction and passes it as an argument to WASM code
- implements component state updates

Motivation and Context
---
Incremental progress on state loading and persisting.

`template_lib` - library for wasm contracts. Needs to support wasm and non-wasm targets as well as no_std

How Has This Been Tested?
---
Rust unit / integration tests updated as needed.
  • Loading branch information
sdbondi committed Aug 10, 2022
1 parent 16ddc4e commit a481f89
Show file tree
Hide file tree
Showing 55 changed files with 1,350 additions and 487 deletions.
13 changes: 7 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions dan_layer/common_types/src/lib.rs
Expand Up @@ -4,8 +4,6 @@
pub mod proto;
pub mod storage;

mod hash;
mod template_id;

pub use hash::Hash;
pub use template_id::TemplateId;
7 changes: 4 additions & 3 deletions dan_layer/engine/Cargo.toml
Expand Up @@ -6,13 +6,14 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
tari_common_types = { path = "../../base_layer/common_types" }
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.4" }
tari_dan_common_types = { path = "../common_types" }
tari_mmr = { path = "../../base_layer/mmr" }
tari_utilities = { git = "https://github.com/tari-project/tari_utilities.git", tag = "v0.4.5" }
tari_crypto = { git = "https://github.com/tari-project/tari-crypto.git", tag = "v0.15.4" }
tari_template_abi = { path = "../template_abi" }
tari_template_lib = { path = "../template_lib", features = ["serde"] }
tari_utilities = { git = "https://github.com/tari-project/tari_utilities.git", tag = "v0.4.5" }

anyhow = "1.0.53"
borsh = "0.9.3"
Expand Down
11 changes: 7 additions & 4 deletions dan_layer/engine/src/flow/workers/create_bucket_worker.rs
Expand Up @@ -10,7 +10,10 @@ use d3ne::{InputData, Node, OutputData, OutputDataBuilder, Worker};
use tari_common_types::types::PublicKey;
use tari_utilities::{hex::Hex, ByteArray};

use crate::{models::Bucket, state::StateDbUnitOfWork};
use crate::{
models::{Bucket, ResourceAddress},
state::StateDbUnitOfWork,
};

pub struct CreateBucketWorker<TUnitOfWork: StateDbUnitOfWork> {
pub state_db: Arc<RwLock<TUnitOfWork>>,
Expand All @@ -24,19 +27,19 @@ impl<TUnitOfWork: StateDbUnitOfWork> Worker for CreateBucketWorker<TUnitOfWork>
fn work(&self, node: &Node, input_data: InputData) -> anyhow::Result<OutputData> {
// TODO: return proper errors....
let amount = u64::try_from(node.get_number_field("amount", &input_data)?)?;
let vault_id = ResourceAddress::from_hex(&node.get_string_field("vault_id", &input_data)?)?;
let token_id = u64::try_from(node.get_number_field("token_id", &input_data)?)?;
let asset_id = u64::try_from(node.get_number_field("asset_id", &input_data)?)?;
let from = PublicKey::from_hex(&node.get_string_field("from", &input_data)?).expect("Not a valid pub key");
let mut state = self.state_db.write().unwrap();
let balance_key = format!("token_id-{}-{}", asset_id, token_id);
let balance_key = format!("token_id-{}-{}", vault_id, token_id);
let balance = state.get_u64(&balance_key, from.as_bytes())?.unwrap_or(0);
let new_balance = balance.checked_sub(amount).expect("Not enough funds to create bucket");
state
.set_u64(&balance_key, from.as_bytes(), new_balance)
.expect("Could not save state");
let output = OutputDataBuilder::new()
.data("default", Box::new(()))
.data("bucket", Box::new(Bucket::new(amount, token_id, asset_id)))
.data("bucket", Box::new(Bucket::for_token(vault_id, vec![token_id])))
.build();
Ok(output)
}
Expand Down
8 changes: 4 additions & 4 deletions dan_layer/engine/src/flow/workers/mint_bucket_worker.rs
Expand Up @@ -5,7 +5,7 @@ use std::convert::TryFrom;

use d3ne::{InputData, Node, OutputData, OutputDataBuilder, Worker};

use crate::models::Bucket;
use crate::models::{Bucket, ResourceAddress};

pub struct MintBucketWorker {}

Expand Down Expand Up @@ -48,10 +48,10 @@ impl Worker for MintBucketWorker {
}

fn work(&self, node: &Node, inputs: InputData) -> anyhow::Result<OutputData> {
let amount = u64::try_from(node.get_number_field("amount", &inputs)?)?;
let _amount = u64::try_from(node.get_number_field("amount", &inputs)?)?;
let token_id = u64::try_from(node.get_number_field("token_id", &inputs)?)?;
let asset_id = u64::try_from(node.get_number_field("asset_id", &inputs)?)?;
let bucket = Bucket::new(amount, token_id, asset_id);
let vault_id = ResourceAddress::from_hex(&node.get_string_field("vault_id", &inputs)?)?;
let bucket = Bucket::for_token(vault_id, vec![token_id]);
let output = OutputDataBuilder::new()
.data("default", Box::new(()))
.data("bucket", Box::new(bucket))
Expand Down
7 changes: 6 additions & 1 deletion dan_layer/engine/src/flow/workers/store_bucket_worker.rs
Expand Up @@ -70,7 +70,12 @@ impl<TUnitOfWork: StateDbUnitOfWork> Worker for StoreBucketWorker<TUnitOfWork> {
let bucket: Bucket = serde_json::from_str(&node.get_string_field("bucket", &inputs)?)?;
let to = PublicKey::from_hex(&node.get_string_field("to", &inputs)?)?;
let mut state = self.state_db.write().unwrap();
let balance_key = format!("token_id-{}-{}", bucket.asset_id(), bucket.token_id());
// TODO: handle panics
let balance_key = format!(
"token_id-{}-{}",
bucket.resource_address(),
bucket.token_ids().unwrap()[0]
);
let balance = state.get_u64(&balance_key, to.as_bytes())?.unwrap_or(0);
state.set_u64(
&balance_key,
Expand Down
6 changes: 5 additions & 1 deletion dan_layer/engine/src/instruction/error.rs
Expand Up @@ -20,7 +20,9 @@
// 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 crate::{packager::PackageId, wasm::WasmExecutionError};
use tari_template_lib::models::PackageId;

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

#[derive(Debug, thiserror::Error)]
pub enum InstructionError {
Expand All @@ -30,4 +32,6 @@ pub enum InstructionError {
PackageNotFound { package_id: PackageId },
#[error("Invalid template")]
TemplateNameNotFound { name: String },
#[error(transparent)]
RuntimeError(#[from] RuntimeError),
}
6 changes: 3 additions & 3 deletions dan_layer/engine/src/instruction/mod.rs
Expand Up @@ -29,8 +29,8 @@ mod processor;
pub use processor::InstructionProcessor;

mod signature;

use crate::{instruction::signature::InstructionSignature, packager::PackageId};
pub use signature::InstructionSignature;
use tari_template_lib::models::{ComponentId, PackageId};

#[derive(Debug, Clone)]
pub enum Instruction {
Expand All @@ -42,7 +42,7 @@ pub enum Instruction {
},
CallMethod {
package_id: PackageId,
component_id: String,
component_id: ComponentId,
method: String,
args: Vec<Vec<u8>>,
},
Expand Down
68 changes: 35 additions & 33 deletions dan_layer/engine/src/instruction/processor.rs
Expand Up @@ -20,85 +20,87 @@
// 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 std::{collections::HashMap, sync::Arc};
use std::sync::Arc;

use tari_template_abi::encode;

use crate::{
instruction::{error::InstructionError, Instruction, InstructionSet},
packager::{Package, PackageId},
packager::Package,
runtime::{Runtime, RuntimeInterface},
traits::Invokable,
wasm::{ExecutionResult, Process},
};

#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone)]
pub struct InstructionProcessor<TRuntimeInterface> {
packages: HashMap<PackageId, Package>,
package: Package,
runtime_interface: TRuntimeInterface,
}

impl<TRuntimeInterface> InstructionProcessor<TRuntimeInterface>
where TRuntimeInterface: RuntimeInterface + Clone + 'static
{
pub fn new(runtime_interface: TRuntimeInterface) -> Self {
pub fn new(runtime_interface: TRuntimeInterface, package: Package) -> Self {
Self {
packages: HashMap::new(),
package,
runtime_interface,
}
}

pub fn load(&mut self, package: Package) -> &mut Self {
self.packages.insert(package.id(), package);
self
}

pub fn execute(&self, instruction_set: InstructionSet) -> Result<Vec<ExecutionResult>, InstructionError> {
let mut results = Vec::with_capacity(instruction_set.instructions.len());

// 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,
function,
args,
} => {
let package = self
.packages
.get(&package_id)
.ok_or(InstructionError::PackageNotFound { package_id })?;
let module = package
if package_id != self.package.id() {
return Err(InstructionError::PackageNotFound { package_id });
}

let module = self
.package
.get_module_by_name(&template)
.ok_or(InstructionError::TemplateNameNotFound { name: template })?;

// TODO: implement intelligent instance caching
let process = Process::start(module.clone(), state.clone())?;
let result = process.invoke_by_name(&function, args)?;
results.push(result);
let process = Process::start(module.clone(), state.clone(), package_id)?;
process.invoke_by_name(&function, args)?
},
Instruction::CallMethod {
package_id,
component_id,
method,
args,
} => {
let package = self
.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 })?;
if package_id != self.package.id() {
return Err(InstructionError::PackageNotFound { package_id });
}
let component = self.runtime_interface.get_component(&component_id)?;
let module = self.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(&component).unwrap());
final_args.extend(args);

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

results.push(result);
}

Ok(results)
Expand Down
40 changes: 27 additions & 13 deletions dan_layer/engine/src/models/bucket.rs
Expand Up @@ -2,32 +2,46 @@
// SPDX-License-Identifier: BSD-3-Clause

use serde::Deserialize;
use tari_template_abi::{Decode, Encode};

#[derive(Debug, Default, Clone, Deserialize)]
use crate::models::resource::{Resource, ResourceAddress};

#[derive(Debug, Clone, Encode, Decode, Deserialize)]
pub struct Bucket {
amount: u64,
token_id: u64,
asset_id: u64,
resource: Resource,
}

impl Bucket {
pub fn new(amount: u64, token_id: u64, asset_id: u64) -> Self {
pub fn for_coin(address: ResourceAddress, amount: u64) -> Self {
Self {
resource: Resource::Coin { address, amount },
}
}

pub fn for_token(address: ResourceAddress, token_ids: Vec<u64>) -> Self {
Self {
amount,
token_id,
asset_id,
resource: Resource::Token { address, token_ids },
}
}

pub fn amount(&self) -> u64 {
self.amount
match self.resource {
Resource::Coin { ref amount, .. } => *amount,
Resource::Token { ref token_ids, .. } => token_ids.len() as u64,
}
}

pub fn token_id(&self) -> u64 {
self.token_id
pub fn resource_address(&self) -> ResourceAddress {
match self.resource {
Resource::Coin { address, .. } => address,
Resource::Token { address, .. } => address,
}
}

pub fn asset_id(&self) -> u64 {
self.asset_id
pub fn token_ids(&self) -> Option<&[u64]> {
match self.resource {
Resource::Coin { .. } => None,
Resource::Token { ref token_ids, .. } => Some(token_ids),
}
}
}

0 comments on commit a481f89

Please sign in to comment.