Skip to content

Commit

Permalink
Add the ability to override operators in a clvm runner.
Browse files Browse the repository at this point in the history
  • Loading branch information
prozacchiwawa committed Oct 12, 2023
1 parent faeea67 commit 114b905
Show file tree
Hide file tree
Showing 10 changed files with 269 additions and 53 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ yaml-rust = "0.4"
linked-hash-map = "0.5.6"
serde = { version = "1.0", features = ["derive", "rc"] }
regex = "1.8.4"
pprof = { version = "0.13", features = ["flamegraph"] }

[dependencies.pyo3]
version = "0.14.2"
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "maturin"

[tool.maturin]
bindings = "pyo3"
features = ["extension-module"]
features = ["extension-module","debug-print"]
python-source = "python"

[project]
Expand Down
27 changes: 14 additions & 13 deletions src/classic/clvm_tools/cmds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ impl ArgumentValueConv for OperatorsVersion {
pub fn run(args: &[String]) {
env_logger::init();

let guard = pprof::ProfilerGuardBuilder::default().frequency(100).blocklist(&["libc", "libgcc", "pthread", "vdso"]).build().unwrap();
// let guard = pprof::ProfilerGuardBuilder::default().frequency(100).blocklist(&["libc", "libgcc", "pthread", "vdso"]).build().unwrap();

let mut s = Stream::new(None);
launch_tool(&mut s, args, "run", 2);
Expand All @@ -330,10 +330,10 @@ pub fn run(args: &[String]) {
.expect("stdout");
io::stdout().flush().expect("stdout");

if let Ok(report) = guard.report().build() {
let file = fs::File::create("flamegraph-compile.svg").unwrap();
report.flamegraph(file).unwrap();
};
// if let Ok(report) = guard.report().build() {
// let file = fs::File::create("flamegraph-compile.svg").unwrap();
// report.flamegraph(file).unwrap();
// };
}

pub fn brun(args: &[String]) {
Expand Down Expand Up @@ -512,6 +512,8 @@ pub fn cldb_hierarchy(
pub fn cldb(args: &[String]) {
env_logger::init();

// let guard = pprof::ProfilerGuardBuilder::default().frequency(100).blocklist(&["libc", "libgcc", "pthread", "vdso"]).build().unwrap();

let tool_name = "cldb".to_string();
let props = TArgumentParserProps {
description: "Execute a clvm script.".to_string(),
Expand Down Expand Up @@ -621,7 +623,7 @@ pub fn cldb(args: &[String]) {
_ => None,
});

let only_print = parsed_args.get("only_print").map(|_| true).unwrap_or(false);
let only_print = parsed_args.get("debug_print").map(|_| true).unwrap_or(false);

let do_optimize = parsed_args
.get("optimize")
Expand All @@ -647,8 +649,6 @@ pub fn cldb(args: &[String]) {
)
.map_err(|_| CompileErr(prog_srcloc, "Failed to parse hex".to_string())),
_ => {
// let guard = pprof::ProfilerGuardBuilder::default().frequency(1000).blocklist(&["libc", "libgcc", "pthread", "vdso"]).build().unwrap();

// don't clobber a symbol table brought in via -y unless we're
// compiling here.
let unopt_res = compile_file(
Expand All @@ -664,11 +664,6 @@ pub fn cldb(args: &[String]) {
unopt_res.map(Rc::new)
};

// if let Ok(report) = guard.report().build() {
// let file = fs::File::create("flamegraph-compile.svg").unwrap();
// report.flamegraph(file).unwrap();
// };

res
}
};
Expand Down Expand Up @@ -770,6 +765,12 @@ pub fn cldb(args: &[String]) {
loop {
if cldbrun.is_ended() {
println!("{}", yamlette_string(&output));

// if let Ok(report) = guard.report().build() {
// let file = fs::File::create("flamegraph-compile.svg").unwrap();
// report.flamegraph(file).unwrap();
// };

return;
}

Expand Down
29 changes: 9 additions & 20 deletions src/compiler/evaluate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -937,26 +937,13 @@ impl<'info> Evaluator {
)
}

fn defmac_ordering(&self) -> bool {
let dialect = self.opts.dialect();
dialect.strict || dialect.stepping.unwrap_or(21) > 22
}

fn make_com_module(&self, l: &Srcloc, prog_args: Rc<SExp>, body: Rc<SExp>) -> Rc<SExp> {
let end_of_list = if self.defmac_ordering() {
let mut mod_list: Vec<Rc<SExp>> = self.helpers.iter().map(|h| h.to_sexp()).collect();
mod_list.push(body);
Rc::new(enlist(l.clone(), &mod_list))
} else {
let mut end_of_list =
Rc::new(SExp::Cons(l.clone(), body, Rc::new(SExp::Nil(l.clone()))));

for h in self.helpers.iter() {
end_of_list = Rc::new(SExp::Cons(l.clone(), h.to_sexp(), end_of_list));
}
let mut end_of_list =
Rc::new(SExp::Cons(l.clone(), body, Rc::new(SExp::Nil(l.clone()))));

end_of_list
};
for h in self.helpers.iter() {
end_of_list = Rc::new(SExp::Cons(l.clone(), h.to_sexp(), end_of_list));
}

Rc::new(SExp::Cons(
l.clone(),
Expand Down Expand Up @@ -1719,14 +1706,14 @@ impl<'info> Evaluator {
in_defun: bool,
use_body: Rc<SExp>,
) -> Result<Rc<SExp>, CompileErr> {
assert!(self.opts.dialect().strict);
// Com takes place in the current environment.
// We can only reduce com if all bindings are
// primitive.
let updated_opts = self
.opts
.set_stdenv(!in_defun && !self.opts.dialect().strict)
.set_in_defun(in_defun)
.set_frontend_opt(false);
.set_in_defun(in_defun);

let com_result = updated_opts.compile_program(
allocator,
Expand All @@ -1735,6 +1722,8 @@ impl<'info> Evaluator {
&mut HashMap::new(),
)?;

eprintln!("com_result {:?} {com_result}", self.opts.dialect());

Ok(Rc::new(com_result))
}

Expand Down
1 change: 1 addition & 0 deletions src/py/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,5 +405,6 @@ fn clvm_tools_rs(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(check_dependencies, m)?)?;
m.add_function(wrap_pyfunction!(compose_run_function, m)?)?;
m.add_class::<PythonRunStep>()?;
m.add_function(wrap_pyfunction!(brun_with_operators, m)?)?;
Ok(())
}
176 changes: 176 additions & 0 deletions wasm/src/dialect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
use std::collections::HashMap;
use std::rc::Rc;

use js_sys::{Array, Function, Reflect};
use clvmr::{Allocator, ChiaDialect, NO_UNKNOWN_OPS, ENABLE_BLS_OPS, ENABLE_SECP_OPS, run_program_with_pre_eval};
use clvmr::allocator::{NodePtr, SExp};
use clvmr::cost::Cost;
use clvmr::dialect::{Dialect, OperatorSet};
use clvmr::reduction::{EvalErr, Reduction, Response};

use wasm_bindgen::{JsCast, JsValue};

use clvm_tools_rs::classic::clvm::OPERATORS_LATEST_VERSION;
use clvm_tools_rs::classic::clvm_tools::stages::stage_0::{RunProgramOption, TRunProgram};
use clvm_tools_rs::compiler::clvm::{convert_from_clvm_rs, convert_to_clvm_rs};
use clvm_tools_rs::compiler::srcloc::Srcloc;

use crate::jsval::{js_object_from_sexp, sexp_from_js_object};
use crate::objects::convert_to_program;

/// A dialect that allows javascript code to add operators to a clvm runner.
/// This can be used for a variety of purposes, but allows the calpoker driver
/// to run code to an exception by starting an isolated VM.
pub struct JsEnrichedCLVM {
loc: Srcloc,
base_dialect: Rc<dyn Dialect>,
extra_ops: HashMap<Vec<u8>, Function>,
}

impl JsEnrichedCLVM {
pub fn new(loc: Srcloc, extra_ops: HashMap<Vec<u8>, Function>) -> Self {
let base_dialect = Rc::new(ChiaDialect::new(
NO_UNKNOWN_OPS | ENABLE_BLS_OPS | ENABLE_SECP_OPS,
));
JsEnrichedCLVM {
loc,
base_dialect,
extra_ops,
}
}

// Return the extension operator system to use while compiling based on user
// preference.
fn get_operators_extension(&self) -> OperatorSet {
OperatorSet::BLS
}
}

impl Dialect for JsEnrichedCLVM {
fn quote_kw(&self) -> &[u8] {
&[1]
}

fn apply_kw(&self) -> &[u8] {
&[2]
}

fn softfork_kw(&self) -> &[u8] {
&[36]
}

// The softfork operator comes with an extension argument.
fn softfork_extension(&self, ext: u32) -> OperatorSet { OperatorSet::BLS }

fn op(
&self,
allocator: &mut Allocator,
op: NodePtr,
sexp: NodePtr,
max_cost: Cost,
_extension: OperatorSet,
) -> Response {
let make_eval_obj = |allocator: &mut Allocator| -> Result<NodePtr, EvalErr> {
allocator.new_pair(op, sexp)
};
let run_failure_to_response_err = |allocator: &mut Allocator, e| -> EvalErr {
let eval_obj =
match make_eval_obj(allocator) {
Ok(eval_obj) => eval_obj,
Err(e) => { return e; }
};
EvalErr(eval_obj, format!("Failure in data conversion"))
};

let js_err_to_response_err = |allocator: &mut Allocator, e: JsValue| {
let eval_obj =
match make_eval_obj(allocator) {
Ok(eval_obj) => eval_obj,
Err(e) => { return e; }
};

if let Some(string_res) = e.as_string() {
EvalErr(eval_obj, format!("Failure returned from injected operator: {string_res}"));
}

EvalErr(eval_obj, format!("Failure returned from injected operator"))
};

// Check for an operator in the operators given to us that matches the
// hex representation of the operator name.
let extensions_to_clvmr_during_compile = self.get_operators_extension();

match allocator.sexp(op) {
SExp::Atom => {
// use of op obvious.
let opbuf = allocator.atom(op);
if let Some(extra_op) = self.extra_ops.get(opbuf) {
// Setup arguments.
let converted_sexp = convert_from_clvm_rs(allocator, self.loc.clone(), sexp).map_err(|e| run_failure_to_response_err(allocator, e))?;
let call_args = js_sys::Array::new();
let arg = convert_to_program(converted_sexp).map_err(|e| js_err_to_response_err(allocator, e))?;
call_args.set(0, arg);

let operator_result = Reflect::apply(
extra_op,
&JsValue::null(),
&call_args,
).map_err(|e| js_err_to_response_err(allocator, e))?;

let from_javascript_program = sexp_from_js_object(self.loc.clone(), &operator_result);
let null = allocator.null();
if let Some(sexp) = from_javascript_program {
// Success path.
let as_clvm_rs = convert_to_clvm_rs(allocator, sexp).map_err(|e| run_failure_to_response_err(allocator, e))?;
return Ok(Reduction(1,as_clvm_rs));
}

// Error path: no possible conversion.
let eval_obj = make_eval_obj(allocator)?;
return Err(EvalErr(eval_obj, "Injected operator returned unconvertible value".to_string()));
}

let extensions_to_clvmr_during_compile = self.get_operators_extension();

self.base_dialect.op(
allocator,
op,
sexp,
max_cost,
extensions_to_clvmr_during_compile,
)
}
_ => self.base_dialect.op(
allocator,
op,
sexp,
max_cost,
extensions_to_clvmr_during_compile,
),
}
}

fn allow_unknown_ops(&self) -> bool {
false
}
}

impl TRunProgram for JsEnrichedCLVM {
fn run_program(
&self,
allocator: &mut Allocator,
program: NodePtr,
args: NodePtr,
option: Option<RunProgramOption>,
) -> Response {
let max_cost = option.as_ref().and_then(|o| o.max_cost).unwrap_or(0);
run_program_with_pre_eval(
allocator,
self,
program,
args,
max_cost,
option.and_then(|o| o.pre_eval_f),
)
}
}
22 changes: 11 additions & 11 deletions wasm/src/jsval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,19 @@ pub fn get_property(o: &Object, name: &str) -> Option<JsValue> {
o,
&JsValue::from_str(name),
))
.and_then(|desc_obj| match Object::try_from(desc_obj) {
Some(o) => {
for pj in Object::entries(o).values() {
let pair = Array::from(&pj.unwrap());
let propname = pair.get(0).as_string();
if propname == Some("value".to_string()) {
return Some(pair.get(1));
.and_then(|desc_obj| match Object::try_from(desc_obj) {
Some(o) => {
for pj in Object::entries(o).values() {
let pair = Array::from(&pj.unwrap());
let propname = pair.get(0).as_string();
if propname == Some("value".to_string()) {
return Some(pair.get(1));
}
}
None
}
None
}
_ => None,
})
_ => None,
})
}

fn location_lc_pair(o: &Object) -> Option<(usize, usize)> {
Expand Down
1 change: 1 addition & 0 deletions wasm/src/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod api;
mod dialect;
mod jsval;
pub mod objects;
Loading

0 comments on commit 114b905

Please sign in to comment.