Skip to content

Commit

Permalink
chore: factor out StoreCore (#1240)
Browse files Browse the repository at this point in the history
`StoreCore` is a data structure that can encode data as DAGs of
tagged pointers, which can be content-addressed with a custom
hasher. The tag type as well as the digest type are generic.

To accomplish this, all pointer types are now unified in a single
type hierarchy.
  • Loading branch information
arthurpaulino authored Apr 17, 2024
1 parent bab22ba commit bb04f94
Show file tree
Hide file tree
Showing 35 changed files with 1,314 additions and 1,280 deletions.
10 changes: 5 additions & 5 deletions benches/common/fib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub(crate) fn fib_limit(n: usize, rc: usize) -> usize {
rc * (frame / rc + usize::from(frame % rc != 0))
}

fn lurk_fib<F: LurkField>(store: &Store<F>, n: usize) -> Ptr {
fn lurk_fib<F: LurkField>(store: &Store<F>, n: usize) -> &Ptr {
let frame_idx = fib_frame(n);
let limit = frame_idx;
let fib_expr = fib_expr(store);
Expand All @@ -59,7 +59,7 @@ fn lurk_fib<F: LurkField>(store: &Store<F>, n: usize) -> Ptr {
// body: (.lurk.user.fib), continuation: Outermost }

let [_, _, rest_bindings] = store.pop_binding(target_env).unwrap();
let [_, val, _] = store.pop_binding(&rest_bindings).unwrap();
let [_, val, _] = store.pop_binding(rest_bindings).unwrap();
val
}

Expand Down Expand Up @@ -109,7 +109,7 @@ pub(crate) fn test_fib_io_matches() {
let fib_9 = store.num_u64(34);
let fib_10 = store.num_u64(55);
let fib_11 = store.num_u64(89);
assert_eq!(fib_9, lurk_fib(&store, 9));
assert_eq!(fib_10, lurk_fib(&store, 10));
assert_eq!(fib_11, lurk_fib(&store, 11));
assert_eq!(&fib_9, lurk_fib(&store, 9));
assert_eq!(&fib_10, lurk_fib(&store, 10));
assert_eq!(&fib_11, lurk_fib(&store, 11));
}
22 changes: 11 additions & 11 deletions chain-server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,22 +173,22 @@ where

// produce the data for the response
let chain_response_data = ser(ChainResponseData::new(
&result,
&next_callable,
result,
next_callable,
&self.store,
compressed_proof,
))
.map_err(|e| Status::internal(e.to_string()))?;

// save the session
if let Some(session) = &self.session {
let session_data = SessionData::pack_standalone(self, &next_callable);
let session_data = SessionData::pack_standalone(self, next_callable);
dump(session_data, session).map_err(|e| Status::internal(e.to_string()))?;
}

// now it's safe to set the new callable state since no error
// has occurred so far
*callable_state = next_callable;
*callable_state = *next_callable;

Ok(Response::new(ChainResponse {
chain_response_data,
Expand Down Expand Up @@ -371,8 +371,8 @@ where

// produce the data for the response
let chain_response_data = ser(ChainResponseData::new(
&result,
&next_callable,
result,
next_callable,
&self.store,
compressed_proof,
))
Expand All @@ -382,16 +382,16 @@ where
if let Some(session) = &self.session {
let session_data = SessionData::pack_stream(
self,
&next_callable,
Some((&result, recursive_proof.clone())),
next_callable,
Some((result, recursive_proof.clone())),
);
dump(session_data, session).map_err(|e| Status::internal(e.to_string()))?;
}

// now it's safe to set the new state since no error has occurred so far
*state = StreamState {
callable: next_callable,
result_and_proof: Some((result, recursive_proof)),
callable: *next_callable,
result_and_proof: Some((*result, recursive_proof)),
};

Ok(Response::new(ChainResponse {
Expand Down Expand Up @@ -643,7 +643,7 @@ fn get_service_and_address<
let callable = if init_args.comm {
let hash_ptr = store.read_with_default_state(&init_args.callable)?;
let hash = store
.fetch_f(&hash_ptr)
.fetch_f_by_val(hash_ptr.val())
.ok_or("Failed to parse callable hash")?;
fetch_comm(hash, &store)?;
hash_ptr.cast(Tag::Expr(lurk::tag::ExprTag::Comm))
Expand Down
4 changes: 2 additions & 2 deletions examples/keccak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ impl<F: LurkField> CircomGadget<F> for CircomKeccak<F> {
let bytes_to_hash = bits_to_bytes(
&z_bits
.iter()
.map(|ptr| ptr.value() != &F::ZERO)
.map(|ptr| ptr.hash() != &F::ZERO)
.collect::<Vec<_>>(),
);

Expand Down Expand Up @@ -371,7 +371,7 @@ fn main() {
.unwrap()
.0
.iter()
.map(|ptr| ptr.raw().get_atom().unwrap() == 1)
.map(|ptr| ptr.val().get_atom_idx().unwrap() == 1)
.collect::<Vec<_>>(),
);

Expand Down
10 changes: 5 additions & 5 deletions foil/src/coil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ fn lookup_vertex_id<F: LurkField>(store: &Store<F>, env: &Ptr, var: &Ptr) -> Res
return Ok(None);
};

if *var == bound_var {
match store.fetch_num(&id) {
if var == bound_var {
match store.fetch_num(id) {
Some(f) => Ok(f.to_u64().map(|u| {
let n = u as Id;
info!("found {n}");
Expand All @@ -97,7 +97,7 @@ fn lookup_vertex_id<F: LurkField>(store: &Store<F>, env: &Ptr, var: &Ptr) -> Res
}
}
} else {
lookup_vertex_id(store, &rest_env, var)
lookup_vertex_id(store, rest_env, var)
}
}

Expand Down Expand Up @@ -127,9 +127,9 @@ impl Context {
let [_var, id, rest_env] = store
.pop_binding(&self.env)
.ok_or(anyhow!("failed to pop binding"))?;
self.env = rest_env;
self.env = *rest_env;

Ok(match store.fetch_num(&id) {
Ok(match store.fetch_num(id) {
Some(f) => f
.to_u64()
.map(|u| u as Id)
Expand Down
23 changes: 12 additions & 11 deletions src/circuit/gadgets/pointer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use ff::PrimeField;

use crate::{
field::LurkField,
lem::pointers::ZPtr,
tag::{ExprTag, Tag},
z_ptr::{ZContPtr, ZExprPtr, ZPtr},
z_ptr::{ZContPtr, ZExprPtr},
};

use super::{
Expand Down Expand Up @@ -50,17 +51,17 @@ impl<F: LurkField> Debug for AllocatedPtr<F> {
}

impl<F: LurkField> AllocatedPtr<F> {
pub fn alloc<Fo, CS: ConstraintSystem<F>, T: Tag>(
pub fn alloc<Fo, CS: ConstraintSystem<F>>(
cs: &mut CS,
value: Fo,
) -> Result<Self, SynthesisError>
where
Fo: FnOnce() -> Result<ZPtr<T, F>, SynthesisError>,
Fo: FnOnce() -> Result<ZPtr<F>, SynthesisError>,
{
let mut hash = None;
let alloc_tag = AllocatedNum::alloc(ns!(cs, "tag"), || {
let ptr = value()?;
hash = Some(*ptr.value());
hash = Some(*ptr.hash());
Ok(ptr.tag_field())
})?;

Expand All @@ -74,14 +75,14 @@ impl<F: LurkField> AllocatedPtr<F> {
})
}

pub fn alloc_infallible<Fo, CS: ConstraintSystem<F>, T: Tag>(cs: &mut CS, value: Fo) -> Self
pub fn alloc_infallible<Fo, CS: ConstraintSystem<F>>(cs: &mut CS, value: Fo) -> Self
where
Fo: FnOnce() -> ZPtr<T, F>,
Fo: FnOnce() -> ZPtr<F>,
{
let mut hash = None;
let alloc_tag = AllocatedNum::alloc_infallible(ns!(cs, "tag"), || {
let ptr = value();
hash = Some(*ptr.value());
hash = Some(*ptr.hash());
ptr.tag_field()
});

Expand All @@ -106,12 +107,12 @@ impl<F: LurkField> AllocatedPtr<F> {
})
}

pub fn alloc_constant<CS: ConstraintSystem<F>, T: Tag>(
pub fn alloc_constant<CS: ConstraintSystem<F>>(
cs: &mut CS,
value: ZPtr<T, F>,
value: ZPtr<F>,
) -> Result<Self, SynthesisError> {
let alloc_tag = allocate_constant(ns!(cs, "tag"), value.tag_field());
let alloc_hash = allocate_constant(ns!(cs, "hash"), *value.value());
let alloc_hash = allocate_constant(ns!(cs, "hash"), *value.hash());

Ok(AllocatedPtr {
tag: alloc_tag,
Expand All @@ -131,7 +132,7 @@ impl<F: LurkField> AllocatedPtr<F> {
&self.hash
}

pub fn get_value<T: Tag>(&self) -> Option<ZPtr<T, F>> {
pub fn get_value(&self) -> Option<ZPtr<F>> {
self.tag.get_value().and_then(|tag| {
self.hash
.get_value()
Expand Down
15 changes: 7 additions & 8 deletions src/cli/repl/meta_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ use crate::{
field::LurkField,
lem::{
eval::evaluate_with_env_and_cont,
pointers::{Ptr, RawPtr},
store::expect_ptrs,
pointers::{IVal, Ptr},
tag::Tag,
},
package::{Package, SymbolRef},
Expand Down Expand Up @@ -272,7 +271,7 @@ where
let second_io = repl
.eval_expr(second)
.with_context(|| "evaluating second arg")?;
let (Tag::Expr(ExprTag::Num), RawPtr::Atom(secret)) = first_io[0].parts() else {
let (Tag::Expr(ExprTag::Num), IVal::Atom(secret)) = first_io[0].parts() else {
bail!(
"Secret must be a number. Got {}",
first_io[0].fmt_to_string(&repl.store, &repl.state.borrow())
Expand Down Expand Up @@ -589,7 +588,7 @@ where
.store
.fetch_cons(result)
.ok_or_else(|| anyhow!("Chained function must return a cons expression"))?;
let (Tag::Expr(ExprTag::Comm), RawPtr::Atom(hash)) = comm.parts() else {
let (Tag::Expr(ExprTag::Comm), IVal::Atom(hash)) = comm.parts() else {
bail!("Second component of a chain must be a commitment")
};
let hash = *repl.store.expect_f(*hash);
Expand All @@ -598,7 +597,7 @@ where
.store
.open(hash)
.expect("data must have been committed");
repl.hide(*secret, *fun)
repl.hide(secret.0, *fun)
},
};

Expand Down Expand Up @@ -822,7 +821,7 @@ where
};

let (car, _) = repl.store.car_cdr_simple(&rest)?;
let (Tag::Expr(ExprTag::Num), RawPtr::Atom(rc_idx)) = car.parts() else {
let (Tag::Expr(ExprTag::Num), IVal::Atom(rc_idx)) = car.parts() else {
bail!("Reduction count must be a Num")
};
let Some(rc) = repl.store.expect_f(*rc_idx).to_u64().map(|u| u as usize) else {
Expand Down Expand Up @@ -868,13 +867,13 @@ where
.eval_expr_with_env(apply_call, repl.store.intern_empty_env())
.with_context(|| "evaluating protocol function call")?;

let (Tag::Expr(ExprTag::Cons), RawPtr::Hash4(idx)) = &io[0].parts() else {
let (Tag::Expr(ExprTag::Cons), IVal::Tuple2(idx)) = &io[0].parts() else {
bail!(
"Protocol function must return a pair. Got {}",
io[0].fmt_to_string(&repl.store, &repl.state.borrow())
)
};
let [pre_verify, post_verify] = &expect_ptrs!(repl.store, 2, *idx);
let [pre_verify, post_verify] = repl.store.expect_tuple2(*idx);

if pre_verify.is_nil() {
bail!("Pre-verification predicate rejected the input")
Expand Down
10 changes: 6 additions & 4 deletions src/cli/repl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::{
make_eval_step_from_config, EvalConfig,
},
interpreter::Frame,
pointers::{Ptr, RawPtr},
pointers::{IVal, Ptr},
store::Store,
tag::Tag,
Func,
Expand Down Expand Up @@ -322,7 +322,10 @@ where
&self.store,
(input[0], output[0]),
(input[1], output[1]),
(cont.parts(), cont_out.parts()),
(
(cont.tag_field(), *cont.hash()),
(cont_out.tag_field(), *cont_out.hash()),
),
);

let claim_comm = Commitment::new(None, claim, &self.store);
Expand Down Expand Up @@ -555,8 +558,7 @@ where
/// Errors when `hash_expr` doesn't reduce to a Num or Comm pointer
fn get_comm_hash(&mut self, hash_expr: Ptr) -> Result<&F> {
let io = self.eval_expr(hash_expr)?;
let (Tag::Expr(ExprTag::Num | ExprTag::Comm), RawPtr::Atom(hash_idx)) = io[0].parts()
else {
let (Tag::Expr(ExprTag::Num | ExprTag::Comm), IVal::Atom(hash_idx)) = io[0].parts() else {
bail!("Commitment hash expression must reduce to a Num or Comm pointer")
};
Ok(self.store.expect_f(*hash_idx))
Expand Down
Loading

1 comment on commit bb04f94

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmarks

Table of Contents

Overview

This benchmark report shows the Fibonacci GPU benchmark.
NVIDIA L4
Intel(R) Xeon(R) CPU @ 2.20GHz
32 vCPUs
125 GB RAM
Workflow run: https://github.com/lurk-lab/lurk-rs/actions/runs/8729661747

Benchmark Results

LEM Fibonacci Prove - rc = 100

ref=bab22ba2eb600c8f83c9d30b439084537a0f3b53 ref=bb04f94ab5510d5e1c4ac94315c5abcb27edbb11
num-100 1.48 s (✅ 1.00x) 1.50 s (✅ 1.01x slower)
num-200 2.81 s (✅ 1.00x) 2.87 s (✅ 1.02x slower)

LEM Fibonacci Prove - rc = 600

ref=bab22ba2eb600c8f83c9d30b439084537a0f3b53 ref=bb04f94ab5510d5e1c4ac94315c5abcb27edbb11
num-100 1.86 s (✅ 1.00x) 1.88 s (✅ 1.01x slower)
num-200 3.05 s (✅ 1.00x) 3.11 s (✅ 1.02x slower)

Made with criterion-table

Please sign in to comment.