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

feat(dan): template macro handles component state #4380

Merged
merged 12 commits into from
Aug 3, 2022
1 change: 1 addition & 0 deletions dan_layer/engine/tests/hello_world/Cargo.lock

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

16 changes: 14 additions & 2 deletions dan_layer/engine/tests/state/Cargo.lock

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

1 change: 1 addition & 0 deletions dan_layer/engine/tests/state/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ edition = "2021"
[dependencies]
tari_template_abi = { path = "../../../template_abi" }
tari_template_lib = { path = "../../../template_lib" }
tari_template_macros = { path = "../../../template_macros" }

[profile.release]
opt-level = 's' # Optimize for size.
Expand Down
109 changes: 4 additions & 105 deletions dan_layer/engine/tests/state/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,15 @@
// 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::{decode, encode_with_len, FunctionDef, Type};
use tari_template_lib::{call_engine, generate_abi, generate_main, TemplateImpl};
use tari_template_macros::template;

// that's what the example should look like from the user's perspective
#[allow(dead_code)]
#[template]
mod state_template {
use tari_template_abi::{borsh, Decode, Encode};

// #[tari::template]
#[derive(Encode, Decode)]
pub struct State {
value: u32,
pub value: u32,
}

// #[tari::impl]
impl State {
// #[tari::constructor]
pub fn new() -> Self {
Self { value: 0 }
}
Expand All @@ -49,98 +41,5 @@ mod state_template {
self.value
}
}
}

// TODO: Macro generated code
#[no_mangle]
extern "C" fn State_abi() -> *mut u8 {
let template_name = "State".to_string();

let functions = vec![
FunctionDef {
name: "new".to_string(),
arguments: vec![],
output: Type::U32, // the component_id
},
FunctionDef {
name: "set".to_string(),
arguments: vec![Type::U32, Type::U32], // the component_id and the new value
output: Type::Unit, // does not return anything
},
FunctionDef {
name: "get".to_string(),
arguments: vec![Type::U32], // the component_id
output: Type::U32, // the stored value
},
];

generate_abi(template_name, functions)
}

#[no_mangle]
extern "C" fn State_main(call_info: *mut u8, call_info_len: usize) -> *mut u8 {
let mut template_impl = TemplateImpl::new();
use tari_template_abi::{ops::*, CreateComponentArg, EmitLogArg, LogLevel};
use tari_template_lib::models::ComponentId;

tari_template_lib::call_engine::<_, ()>(OP_EMIT_LOG, &EmitLogArg {
message: "This is a log message from State_main!".to_string(),
level: LogLevel::Info,
});

// constructor
template_impl.add_function(
"new".to_string(),
Box::new(|_| {
let ret = state_template::State::new();
let encoded = encode_with_len(&ret);
// Call the engine to create a new component
// TODO: proper component id
// The macro will know to generate this call because of the #[tari(constructor)] attribute
// TODO: what happens if the user wants to return multiple components/types?
let component_id = call_engine::<_, ComponentId>(OP_CREATE_COMPONENT, &CreateComponentArg {
name: "State".to_string(),
quantity: 1,
metadata: Default::default(),
state: encoded,
});
let component_id = component_id.expect("no asset id returned");
encode_with_len(&component_id)
}),
);

template_impl.add_function(
"set".to_string(),
Box::new(|args| {
// read the function paramenters
let _component_id: u32 = decode(&args[0]).unwrap();
let _new_value: u32 = decode(&args[1]).unwrap();

// update the component value
// TODO: use a real op code (not "123") when they are implemented
call_engine::<_, ()>(123, &());

// the function does not return any value
// TODO: implement "Unit" type empty responses. Right now this fails: wrap_ptr(vec![])
encode_with_len(&0)
}),
);

template_impl.add_function(
"get".to_string(),
Box::new(|args| {
// read the function paramenters
let _component_id: u32 = decode(&args[0]).unwrap();

// get the component state
// TODO: use a real op code (not "123") when they are implemented
let _state = call_engine::<_, ()>(123, &());

// return the value
let value = 1_u32; // TODO: read from the component state
encode_with_len(&value)
}),
);

generate_main(call_info, call_info_len, template_impl)
}
}
8 changes: 4 additions & 4 deletions dan_layer/engine/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,25 +46,25 @@ fn test_hello_world() {

#[test]
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());

// constructor
let component: ComponentId = template_test.call_function("new".to_string(), vec![]);
assert_eq!(component.1, 0);
let component: ComponentId = template_test.call_function("new".to_string(), vec![]);
assert_eq!(component.1, 1);

// 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![
encode_with_len(&component),
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(
&component,
)]);
assert_eq!(value, 1);
// TODO: when state storage is implemented in the engine, assert the previous setted value (20_u32)
assert_eq!(value, 0);
}

struct TemplateTest {
Expand Down
20 changes: 1 addition & 19 deletions dan_layer/template_lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub mod models;

// TODO: we should only use stdlib if the template dev needs to include it e.g. use core::mem when stdlib is not
// available
use std::{collections::HashMap, mem, ptr::copy, slice};
use std::{collections::HashMap, mem, slice};

use tari_template_abi::{encode_with_len, Decode, Encode, FunctionDef, TemplateDef};

Expand Down Expand Up @@ -119,21 +119,3 @@ pub fn call_debug<T: AsRef<[u8]>>(data: T) {
unsafe { debug(ptr, len) }
}

#[no_mangle]
pub unsafe extern "C" fn tari_alloc(len: u32) -> *mut u8 {
let cap = (len + 4) as usize;
let mut buf = Vec::<u8>::with_capacity(cap);
let ptr = buf.as_mut_ptr();
mem::forget(buf);
copy(len.to_le_bytes().as_ptr(), ptr, 4);
ptr
}

#[no_mangle]
pub unsafe extern "C" fn tari_free(ptr: *mut u8) {
let mut len = [0u8; 4];
copy(ptr, len.as_mut_ptr(), 4);

let cap = (u32::from_le_bytes(len) + 4) as usize;
let _ = Vec::<u8>::from_raw_parts(ptr, cap, cap);
}
38 changes: 38 additions & 0 deletions dan_layer/template_lib/src/models/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,42 @@
// 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.

// TODO: use the actual component id type
pub type ComponentId = ([u8; 32], u32);

use tari_template_abi::{Decode, Encode, encode_with_len, ops::OP_CREATE_COMPONENT, CreateComponentArg};

use crate::call_engine;

pub fn initialise<T: Encode + Decode>(template_name: String, initial_state: T) -> ComponentId {
let encoded_state = encode_with_len(&initial_state);

// Call the engine to create a new component
// TODO: proper component id
// TODO: what happens if the user wants to return multiple components/types?
let component_id = call_engine::<_, ComponentId>(OP_CREATE_COMPONENT, &CreateComponentArg {
name: template_name,
quantity: 1,
metadata: Default::default(),
state: encoded_state,
});
component_id.expect("no asset id returned")
}

pub fn get_state<T: Encode + Decode>(_id: u32) -> T {
// get the component state
// TODO: use a real op code (not "123") when they are implemented
let _state = call_engine::<_, ()>(123, &());

// create and return a mock state because state is not implemented yet in the engine
let len = std::mem::size_of::<T>();
let byte_vec = vec![0_u8; len];
let mut mock_value = byte_vec.as_slice();
T::deserialize(&mut mock_value).unwrap()
}

pub fn set_state<T: Encode + Decode>(_id: u32, _state: T) {
// update the component value
// TODO: use a real op code (not "123") when they are implemented
call_engine::<_, ()>(123, &());
}
2 changes: 1 addition & 1 deletion dan_layer/template_lib/src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

mod component;
pub use component::ComponentId;
pub use component::*;
8 changes: 8 additions & 0 deletions dan_layer/template_macros/Cargo.lock

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

1 change: 1 addition & 0 deletions dan_layer/template_macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ proc-macro = true

[dependencies]
tari_template_abi = { path = "../template_abi" }
tari_template_lib = { path = "../template_lib" }
syn = { version = "1.0.98", features = ["full"] }
proc-macro2 = "1.0.42"
quote = "1.0.20"
Expand Down
Loading