#![allow(
clippy::string_lit_as_bytes,
clippy::manual_string_new,
clippy::needless_pass_by_value,
clippy::uninlined_format_args,
clippy::needless_collect,
clippy::cast_sign_loss
)]
use std::{collections::BTreeMap, fs::read, path::PathBuf, sync::Arc};
use wacc::types::{CheckCount, ContextPath};
use wacc::{
storage::{Pairs, Stack},
vm::{Builder, Context, Instance, Value},
};
use wasmtime::{AsContextMut, StoreLimitsBuilder};
const MEMORY_LIMIT: usize = 1 << 22;
fn load_wasm(file_name: &str) -> Option<Vec<u8>> {
let mut pb = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
pb.push("target");
pb.push(file_name);
println!("trying to load: {}", pb.as_os_str().display());
read(&pb).ok()
}
fn load_wast(file_name: &str) -> Vec<u8> {
let mut pb = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
pb.push("examples/wacc/wast");
pb.push(file_name);
println!("trying to load: {}", pb.as_os_str().display());
read(&pb).unwrap_or_else(|_| panic!("WAST file {file_name} must exist"))
}
fn test_example(
script: Vec<u8>,
func: &str,
expected: bool,
current: Kvp,
proposed: Kvp,
pstack: Stk,
rstack: Stk,
) -> Instance {
let context = Context {
current: Box::new(current),
proposed: Box::new(proposed),
pstack: Box::new(pstack),
rstack: Box::new(rstack),
check_count: CheckCount::zero(),
write_idx: 0,
context: ContextPath::new("/forks/child/"),
log: Vec::default(),
limiter: StoreLimitsBuilder::new()
.memory_size(MEMORY_LIMIT)
.instances(2)
.memories(1)
.build(),
};
let mut instance = Builder::new()
.with_context(context)
.with_bytes(&script)
.try_build()
.unwrap();
let result = instance.run(func).unwrap();
if result != expected {
let mut ctx = instance.store.as_context_mut();
let context = ctx.data_mut();
println!("stack:");
println!("\t top --");
while let Some(v) = context.rstack.pop() {
println!("\t\t{v:?}\n\t --");
}
panic!();
}
instance
}
#[derive(Default, Clone)]
struct Kvp {
pub pairs: BTreeMap<String, Value>,
}
impl Pairs for Kvp {
fn get(&self, key: &str) -> Option<Value> {
self.pairs.get(key).cloned()
}
fn put(&mut self, key: &str, value: &Value) -> Option<Value> {
self.pairs.insert(key.to_string(), value.clone())
}
}
#[derive(Default, Clone)]
struct Stk {
pub stack: Vec<Value>,
}
impl Stack for Stk {
fn push(&mut self, value: Value) {
self.stack.push(value);
}
fn pop(&mut self) -> Option<Value> {
self.stack.pop()
}
fn top(&self) -> Option<Value> {
self.stack.last().cloned()
}
fn peek(&self, idx: usize) -> Option<Value> {
if idx >= self.stack.len() {
return None;
}
Some(self.stack[self.stack.len() - 1 - idx].clone())
}
fn len(&self) -> usize {
self.stack.len()
}
fn is_empty(&self) -> bool {
self.stack.is_empty()
}
}
#[test]
fn test_preimage_wast() {
let mut kvp_unlock = Kvp::default();
let mut kvp_lock = Kvp::default();
let mut pstack_values: Vec<Value>;
{
let pstack = Stk::default();
let rstack = Stk::default();
let _ = kvp_unlock.put(
"/entry/proof",
&"for great justice, move every zig!".to_string().into(),
);
let script = load_wast("preimage_unlock.wast");
let mut instance = test_example(
script,
"for_great_justice",
true,
kvp_unlock.clone(),
kvp_unlock.clone(),
pstack,
rstack,
);
let mut ctx = instance.store.as_context_mut();
let context = ctx.data_mut();
assert_eq!(1, context.pstack.len());
assert_eq!(
context.pstack.top(),
Some(Value::Str {
hint: String::new(),
data: Arc::from("for great justice, move every zig!")
})
);
pstack_values = vec![];
for i in 0..context.pstack.len() {
if let Some(val) = context.pstack.peek(context.pstack.len() - 1 - i) {
pstack_values.push(val);
}
}
}
{
let pstack = Stk {
stack: pstack_values,
};
let rstack = Stk::default();
let _ = kvp_lock.put(
"/hash",
&hex::decode("16206b761d3b2e7675e088e337a82207b55711d3957efdb877a3d261b0ca2c38e201")
.unwrap()
.into(),
);
let script = load_wast("preimage_lock.wast");
let mut instance = test_example(
script,
"move_every_zig",
true,
kvp_lock,
kvp_unlock,
pstack,
rstack,
);
let mut ctx = instance.store.as_context_mut();
let context = ctx.data_mut();
assert_eq!(1, context.rstack.len());
assert_eq!(context.rstack.top(), Some(Value::Success(0)));
}
}
#[test]
fn test_preimage_wasm() {
let Some(unlock_script) = load_wasm("preimage_unlock.wasm") else {
eprintln!("Skipping test_preimage_wasm: preimage_unlock.wasm not found (run 'make' in examples/wast to build)");
return;
};
let Some(lock_script) = load_wasm("preimage_lock.wasm") else {
eprintln!("Skipping test_preimage_wasm: preimage_lock.wasm not found (run 'make' in examples/wast to build)");
return;
};
let mut kvp_unlock = Kvp::default();
let mut kvp_lock = Kvp::default();
let mut pstack_values: Vec<Value>;
{
let pstack = Stk::default();
let rstack = Stk::default();
let _ = kvp_unlock.put(
"/entry/proof",
&"for great justice, move every zig!".to_string().into(),
);
let mut instance = test_example(
unlock_script,
"for_great_justice",
true,
kvp_unlock.clone(),
kvp_unlock.clone(),
pstack,
rstack,
);
let mut ctx = instance.store.as_context_mut();
let context = ctx.data_mut();
assert_eq!(1, context.pstack.len());
assert_eq!(
context.pstack.top(),
Some(Value::Str {
hint: String::new(),
data: Arc::from("for great justice, move every zig!")
})
);
pstack_values = vec![];
for i in 0..context.pstack.len() {
if let Some(val) = context.pstack.peek(context.pstack.len() - 1 - i) {
pstack_values.push(val);
}
}
}
{
let pstack = Stk {
stack: pstack_values,
};
let rstack = Stk::default();
let _ = kvp_lock.put(
"/hash",
&hex::decode("16206b761d3b2e7675e088e337a82207b55711d3957efdb877a3d261b0ca2c38e201")
.unwrap()
.into(),
);
let mut instance = test_example(
lock_script,
"move_every_zig",
true,
kvp_lock,
kvp_unlock,
pstack,
rstack,
);
let mut ctx = instance.store.as_context_mut();
let context = ctx.data_mut();
assert_eq!(1, context.rstack.len());
assert_eq!(context.rstack.top(), Some(Value::Success(0)));
}
}