Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion benches/run-program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ type EnvFn = fn(&mut Allocator) -> NodePtr;

fn run_program_benchmark(c: &mut Criterion) {
let mut a = Allocator::new();
let dialect = ChiaDialect::new(ClvmFlags::empty());
let dialect = ChiaDialect::new(ClvmFlags::ENABLE_GC);

let test_case_checkpoint = a.checkpoint();

Expand Down
29 changes: 29 additions & 0 deletions clvm-fuzzing/src/node_eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,32 @@ pub fn node_eq(allocator: &Allocator, lhs: NodePtr, rhs: NodePtr) -> bool {
}
true
}

/// Compare two CLVM trees that may belong to different allocators.
/// Returns true if they are structurally identical, false otherwise.
pub fn node_eq_two(
lhs_allocator: &Allocator,
lhs: NodePtr,
rhs_allocator: &Allocator,
rhs: NodePtr,
) -> bool {
let mut stack = vec![(lhs, rhs)];

while let Some((l, r)) = stack.pop() {
match (lhs_allocator.sexp(l), rhs_allocator.sexp(r)) {
(SExp::Pair(ll, lr), SExp::Pair(rl, rr)) => {
stack.push((lr, rr));
stack.push((ll, rl));
}
(SExp::Atom, SExp::Atom) => {
if lhs_allocator.atom(l).as_ref() != rhs_allocator.atom(r).as_ref() {
return false;
}
}
_ => {
return false;
}
}
}
true
}
6 changes: 6 additions & 0 deletions docs/new-operator-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ Follow this checklist when adding operators:
- Extend the benchmark-clvm-cost.rs to include benchmarks for the new operator,
to establish its cost.
- The opcode decoding and dispatching happens in `src/chia_dialect.rs`
- The ChiaDialect trait also has a function called gc_candidate(). If the new
operator is likely to return a small atom (say 48 bytes or less), this
function should return `true` for the new opcode. This allows the interpreter to
free all memory allocated by the opcode and any arguments computed for its
invocation. As long as the return value is a small atom and can easily be put
back in the allocator.
- Add support for the new operators in `src/test_ops.rs` `parse_atom()`, to
compile the name of the operator to its corresponding opcode.
- If the operator(s) are part of an extension to `softfork`, add another value
Expand Down
6 changes: 6 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ path = "fuzz_targets/run_program.rs"
test = false
doc = false

[[bin]]
name = "garbage-collection"
path = "fuzz_targets/garbage_collection.rs"
test = false
doc = false

[[bin]]
name = "serialized-length"
path = "fuzz_targets/serialized_length.rs"
Expand Down
68 changes: 68 additions & 0 deletions fuzz/fuzz_targets/garbage_collection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#![no_main]

use clvm_fuzzing::{make_clvm_program, make_tree_limits, node_eq_two};
use libfuzzer_sys::{Corpus, fuzz_target};

use clvmr::allocator::Allocator;
use clvmr::chia_dialect::{ChiaDialect, ClvmFlags};
use clvmr::cost::Cost;
use clvmr::reduction::{Reduction, Response};
use clvmr::run_program::run_program;

const MAX_COST: Cost = 11_000_000_000;

fuzz_target!(|data: &[u8]| -> Corpus {
let mut results: Vec<(Response, Allocator)> = Vec::new();

for flags in [ClvmFlags::empty(), ClvmFlags::ENABLE_GC] {
let mut unstructured = arbitrary::Unstructured::new(data);
let mut a = Allocator::new();
let (args, _) =
make_tree_limits(&mut a, &mut unstructured, 100, true).expect("out of memory");
let Ok(program) = make_clvm_program(&mut a, &mut unstructured, args, 100_000) else {
return Corpus::Reject;
};
let dialect = ChiaDialect::new(flags);
let result = run_program(&mut a, &dialect, program, args, MAX_COST);
results.push((result, a));
}

assert_eq!(
results[0].1.atom_count(),
results[1].1.atom_count(),
"atom count differs empty vs ENABLE_GC"
);
assert_eq!(
results[0].1.pair_count(),
results[1].1.pair_count(),
"pair count differs empty vs ENABLE_GC"
);
assert_eq!(
results[0].1.heap_size(),
results[1].1.heap_size(),
"heap size differs empty vs ENABLE_GC"
);

match (&results[0].0, &results[1].0) {
(Ok(Reduction(cost_empty, node_empty)), Ok(Reduction(cost_gc, node_gc))) => {
assert_eq!(cost_empty, cost_gc, "cost differs empty vs ENABLE_GC");
assert!(
node_eq_two(&results[0].1, *node_empty, &results[1].1, *node_gc),
"result value differs empty vs ENABLE_GC"
);
}
(Err(e_empty), Err(e_gc)) => {
assert_eq!(
e_empty.to_string(),
e_gc.to_string(),
"error differs empty vs ENABLE_GC"
);
}
_ => panic!(
"outcome mismatch: empty={} ENABLE_GC={}",
results[0].0.is_ok(),
results[1].0.is_ok()
),
}
Corpus::Keep
});
7 changes: 6 additions & 1 deletion fuzz/fuzz_targets/run_program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ fuzz_target!(|data: &[u8]| -> Corpus {

let allocator_checkpoint = allocator.checkpoint();

for flags in [ClvmFlags::empty(), ClvmFlags::NO_UNKNOWN_OPS, MEMPOOL_MODE] {
for flags in [
ClvmFlags::ENABLE_GC,
ClvmFlags::empty(),
ClvmFlags::NO_UNKNOWN_OPS,
MEMPOOL_MODE,
] {
let dialect = ChiaDialect::new(flags.union(ClvmFlags::DISABLE_OP));
allocator.restore_checkpoint(&allocator_checkpoint);

Expand Down
Loading
Loading