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

Support backref for wasm #395

Merged
merged 12 commits into from
Apr 25, 2024
44 changes: 40 additions & 4 deletions src/serde/ser_br.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use super::object_cache::{serialized_length, treehash, ObjectCache};
use super::read_cache_lookup::ReadCacheLookup;
use super::write_atom::write_atom;
use crate::allocator::{Allocator, NodePtr, SExp};
use crate::serde::ser::LimitedWriter;

const BACK_REFERENCE: u8 = 0xfe;
const CONS_BOX_MARKER: u8 = 0xff;
Expand Down Expand Up @@ -77,10 +78,45 @@ pub fn node_to_stream_backrefs<W: io::Write>(
Ok(())
}

pub fn node_to_bytes_backrefs_limit(
arvidn marked this conversation as resolved.
Show resolved Hide resolved
a: &Allocator,
node: NodePtr,
limit: usize,
) -> io::Result<Vec<u8>> {
let buffer = Cursor::new(Vec::new());
let mut writer = LimitedWriter::new(buffer, limit);
node_to_stream_backrefs(a, node, &mut writer)?;
let vec = writer.into_inner().into_inner();
Ok(vec)
}

pub fn node_to_bytes_backrefs(a: &Allocator, node: NodePtr) -> io::Result<Vec<u8>> {
let mut buffer = Cursor::new(Vec::new());
node_to_bytes_backrefs_limit(a, node, 2000000)
}
ChiaMineJP marked this conversation as resolved.
Show resolved Hide resolved

node_to_stream_backrefs(a, node, &mut buffer)?;
let vec = buffer.into_inner();
Ok(vec)
#[test]
fn test_serialize_limit() {
let mut a = Allocator::new();

let leaf = a.new_atom(&[1, 2, 3, 4, 5]).unwrap();
let l1 = a.new_pair(leaf, leaf).unwrap();
let l2 = a.new_pair(l1, l1).unwrap();
let l3 = a.new_pair(l2, l2).unwrap();

let expected = &[255, 255, 255, 133, 1, 2, 3, 4, 5, 254, 2, 254, 2, 254, 2];

{
assert_eq!(node_to_bytes_backrefs(&a, l3).unwrap(), expected);
}

{
assert_eq!(node_to_bytes_backrefs_limit(&a, l3, 15).unwrap(), expected);
}

{
assert_eq!(
node_to_bytes_backrefs_limit(&a, l3, 14).unwrap_err().kind(),
io::ErrorKind::OutOfMemory
);
}
}
3 changes: 3 additions & 0 deletions wasm/src/flags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// when this flag is set, the block generator serialization is allowed to
// contain back-references
pub const ALLOW_BACKREFS: u32 = 0x2000000;
18 changes: 15 additions & 3 deletions wasm/src/lazy_node.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use clvmr::allocator::{Allocator, NodePtr, SExp};
use std::rc::Rc;

use js_sys::Array;
use std::rc::Rc;
use wasm_bindgen::prelude::*;

use crate::flags::ALLOW_BACKREFS;
use clvmr::allocator::{Allocator, NodePtr, SExp};
use clvmr::serde::{node_to_bytes, node_to_bytes_backrefs};

#[wasm_bindgen]
#[derive(Clone)]
pub struct LazyNode {
Expand Down Expand Up @@ -35,6 +37,16 @@ impl LazyNode {
_ => None,
}
}

#[wasm_bindgen]
pub fn to_bytes(&self, flag: u32) -> Option<Vec<u8>> {
let serializer = if (flag & ALLOW_BACKREFS) != 0 {
node_to_bytes_backrefs
} else {
node_to_bytes
};
arvidn marked this conversation as resolved.
Show resolved Hide resolved
serializer(&self.allocator, self.node).ok()
}
}

impl LazyNode {
Expand Down
4 changes: 3 additions & 1 deletion wasm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pub mod api;
pub mod flags;
pub mod lazy_node;
pub mod run_program;
pub mod serialize;

#[cfg(test)]
pub mod tests;
41 changes: 22 additions & 19 deletions wasm/src/api.rs → wasm/src/run_program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ use js_sys::Array;
use std::rc::Rc;
use wasm_bindgen::prelude::*;

use crate::flags::ALLOW_BACKREFS;
use crate::lazy_node::LazyNode;
use clvmr::allocator::Allocator;
use clvmr::chia_dialect::ChiaDialect;
use clvmr::chia_dialect::NO_UNKNOWN_OPS as _no_unknown_ops;
use clvmr::cost::Cost;
use clvmr::run_program::run_program;
use clvmr::serde::{node_from_bytes, node_to_bytes, serialized_length_from_bytes};
use clvmr::serde::{node_from_bytes, node_from_bytes_backrefs, node_to_bytes};

// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
Expand All @@ -25,31 +26,28 @@ impl Flag {
pub fn no_unknown_ops() -> u32 {
_no_unknown_ops
}
}

#[wasm_bindgen]
pub fn serialized_length(program: &[u8]) -> Result<u64, String> {
match serialized_length_from_bytes(program) {
Ok(length) => Ok(length),
Err(err) => Err(err.to_string()),
#[wasm_bindgen]
pub fn allow_backrefs() -> u32 {
ALLOW_BACKREFS
}
}

#[wasm_bindgen]
pub fn run_clvm(program: &[u8], args: &[u8]) -> Vec<u8> {
pub fn run_clvm(program: &[u8], args: &[u8], flag: u32) -> Vec<u8> {
let max_cost: Cost = 1_000_000_000_000_000;

let mut allocator = Allocator::new();
let program = node_from_bytes(&mut allocator, program).unwrap();
let args = node_from_bytes(&mut allocator, args).unwrap();
let deserializer = if (flag & ALLOW_BACKREFS) != 0 {
node_from_bytes_backrefs
} else {
node_from_bytes
};
let program = deserializer(&mut allocator, program).unwrap();
let args = deserializer(&mut allocator, args).unwrap();
let dialect = ChiaDialect::new(flag);

let r = run_program(
&mut allocator,
&ChiaDialect::new(0),
program,
args,
max_cost,
);
let r = run_program(&mut allocator, &dialect, program, args, max_cost);
match r {
Ok(reduction) => node_to_bytes(&allocator, reduction.1).unwrap(),
Err(_eval_err) => format!("{:?}", _eval_err).into(),
Expand All @@ -64,8 +62,13 @@ pub fn run_chia_program(
flag: u32,
) -> Result<Array, String> {
let mut allocator = Allocator::new();
let program = node_from_bytes(&mut allocator, program).unwrap();
let args = node_from_bytes(&mut allocator, args).unwrap();
let deserializer = if (flag & ALLOW_BACKREFS) != 0 {
node_from_bytes_backrefs
} else {
node_from_bytes
};
let program = deserializer(&mut allocator, program).unwrap();
let args = deserializer(&mut allocator, args).unwrap();
let dialect = ChiaDialect::new(flag);

let r = run_program(&mut allocator, &dialect, program, args, max_cost);
Expand Down
24 changes: 24 additions & 0 deletions wasm/src/serialize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use std::rc::Rc;
use wasm_bindgen::prelude::wasm_bindgen;

use crate::flags::ALLOW_BACKREFS;
use crate::lazy_node::LazyNode;
use clvmr::serde::{node_from_bytes, node_from_bytes_backrefs, serialized_length_from_bytes};
use clvmr::Allocator;

#[wasm_bindgen]
pub fn serialized_length(program: &[u8]) -> Result<u64, String> {
serialized_length_from_bytes(program).map_err(|x| x.to_string())
}

#[wasm_bindgen]
pub fn sexp_from_bytes(b: &[u8], flag: u32) -> Result<LazyNode, String> {
arvidn marked this conversation as resolved.
Show resolved Hide resolved
let mut allocator = Allocator::new();
let deserializer = if (flag & ALLOW_BACKREFS) != 0 {
node_from_bytes_backrefs
} else {
node_from_bytes
};
let node = deserializer(&mut allocator, b).map_err(|e| e.to_string())?;
Ok(LazyNode::new(Rc::new(allocator), node))
}
Loading