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

Standard Circom Gadget with Keccak example #997

Merged
merged 24 commits into from
Mar 13, 2024

Conversation

tchataigner
Copy link
Member

@tchataigner tchataigner commented Dec 21, 2023

Goal of this PR

The goal of this PR is to properly implement some standard Circom Gadget in our Lurk library, along with a Keccak example.

Current progress

  • Implemented two ways for the CircomCoprocessor to look for gadget, either via CLI or directly on a remote Github repository
  • Create a standard template for Circom Gadget on Github
    • Help required to pass it in public instead of private
  • Create keccak-circom-gadget repository based on the template.

Left TODO

  • Create pull request over circom-scotia to ensure that we can have slice returned as an output. Branch here.
  • Once merged, update the example for Keccak in lurk-rs to make it work
  • Finally, derive the example to implement the CircomKeccak implementing CircomGadget directly in our repository.

Current caveats

The goal of the following bullet points is to point out diverse part of the flow where I think I need external opinion before setting them in stone :)

  1. Right now, the CircomCoprocessor looks for the CircomGadget files locally before fetching them from the remote rpeository. This is a great simple caching solution BUT it might be too naïve because it does not emcompasss the version of the gadget. Thus if I update my CircomGadget version it will still use the old one. Should I fix that?
  2. I'd like to know if what I've done for the foundation of remote Circom Gadget seems alright.

Related issue

This is part of tackling the issue #962

Copy link
Member

@huitseeker huitseeker left a comment

Choose a reason for hiding this comment

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

I like the approach of grabbing the files from an online location: even if I'm a bit sceptical about the bit about doing, specifically, a Github release, which I feel might add friction.

Have you created a small toy repo where we'd be able to test this import mechanism? Perhaps try to run a GH import on nightly?

src/cli/circom.rs Outdated Show resolved Hide resolved
src/cli/circom.rs Show resolved Hide resolved
pub(crate) fn create_circom_gadget(circom_folder: &Utf8PathBuf, reference: &str) -> Result<()> {
let circom_gadget = circom_dir().join(reference);

// We expect a format <AUTHOR>/<NAME> for the name.
Copy link
Member

Choose a reason for hiding this comment

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

nit: GADGET_NAME may be more useful, otherwise the reader might just wonder who this is supposed to be the name of. (nit -> you can safely ignore this)

src/cli/mod.rs Outdated Show resolved Hide resolved
src/coprocessor/circom/README.md Outdated Show resolved Hide resolved
src/coprocessor/circom/mod.rs Outdated Show resolved Hide resolved
src/coprocessor/circom/mod.rs Outdated Show resolved Hide resolved
src/coprocessor/circom/mod.rs Outdated Show resolved Hide resolved
src/coprocessor/circom/mod.rs Outdated Show resolved Hide resolved
Comment on lines 221 to 255
/// TODO: Generalize
fn arity(&self) -> usize {
0
}

fn synthesize_simple<CS: ConstraintSystem<F>>(
&self,
cs: &mut CS,
g: &crate::lem::circuit::GlobalAllocator<F>,
_s: &Store<F>,
_not_dummy: &bellpepper_core::boolean::Boolean,
args: &[AllocatedPtr<F>],
) -> std::result::Result<AllocatedPtr<F>, SynthesisError> {
let input = self.gadget.clone().into_circom_input(args);
let witness =
circom_scotia::calculate_witness(&self.config, input, true).map_err(|e| {
eprintln!("{:?}", e);
SynthesisError::Unsatisfiable
})?;
let output = circom_scotia::synthesize(cs, self.config.r1cs.clone(), Some(witness))?;
let num_tag = g.alloc_tag(cs, &crate::tag::ExprTag::Num);
let res = AllocatedPtr::from_parts(num_tag.clone(), output);

Ok(res)
}
}

impl<F: LurkField, C: CircomGadget<F> + Debug> Coprocessor<F> for CircomCoprocessor<F, C> {
/// TODO: Generalize
fn eval_arity(&self) -> usize {
0
}
Copy link
Member

Choose a reason for hiding this comment

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

Yep, those todos are going to be the next steps to improve on.

@tchataigner
Copy link
Member Author

Have you created a small toy repo where we'd be able to test this import mechanism? Perhaps try to run a GH import on nightly?

Not 100% sure about what you mean but I have adapted the template in a keccak example. The keccak example is also using this remote logic.

I like the approach of grabbing the files from an online location: even if I'm a bit sceptical about the bit about doing, specifically, a Github release, which I feel might add friction.

Would you have a recommendation of another place to host them?

@huitseeker
Copy link
Member

huitseeker commented Dec 22, 2023

I have adapted the template in a keccak example. The keccak example is also using this remote logic.

Oh, nice!

Would you have a recommendation of another place to host them?

For us at Lurk Lab? I'm fine with releases, for now (those gadgets can get huge)

But in I was thinking user may just give us 2 URLs to their S3 bucket, and perhaps we could work with that. OTOH your template repo completely removes any issue re: friction, only the price of hosting large files on GH remains as an issue.

Copy link
Member

@arthurpaulino arthurpaulino left a comment

Choose a reason for hiding this comment

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

This is amazing work! I can see that it's already quite polished.

Suggestion: print the output of the computation in the Keccak example so we can see the hash that Lurk spits out. The output is in the last frame, in the first component of the output attribute. You can print it with the fmt_to_string_simple method.

examples/keccak.rs Outdated Show resolved Hide resolved
examples/keccak.rs Show resolved Hide resolved
examples/keccak.rs Show resolved Hide resolved
src/lem/circuit.rs Outdated Show resolved Hide resolved
arthurpaulino
arthurpaulino previously approved these changes Mar 8, 2024
Copy link
Member

@arthurpaulino arthurpaulino left a comment

Choose a reason for hiding this comment

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

🎉

huitseeker
huitseeker previously approved these changes Mar 11, 2024
Copy link
Member

@huitseeker huitseeker left a comment

Choose a reason for hiding this comment

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

This is excellent, I <3 the careful error messages. I left a few inline comments, nothing blocking. Thank you so much!

Comment on lines 78 to 94
fn bytes_to_bits(bytes: &[u8]) -> Vec<bool> {
let mut bits = Vec::new(); // Create a new, empty vector to store bits

for &byte in bytes.iter() {
// Iterate over each byte in the input slice
for j in 0..8 {
// For each bit in the byte
if byte & (1 << j) > 0 {
// Check if the bit is set
bits.push(true); // If the bit is set, push 1 to the vector
} else {
bits.push(false); // If the bit is not set, push 0
}
}
}
bits // Return the vector of bits
}
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

Switched to bytes_to_bit_le

Comment on lines +62 to +74
fn bits_to_bytes(bits: &[bool]) -> Vec<u8> {
let mut bytes = vec![0; (bits.len() + 7) / 8]; // Initialize a vector with zeroes

for (i, &bit) in bits.iter().enumerate() {
// Iterate over each bit with its index
let byte_index = i / 8; // Calculate the byte index for the current bit
if bit {
// If the current bit is true,
bytes[byte_index] |= 1 << (i % 8); // Set the corresponding bit in the byte
}
}
bytes // Return the array of bytes
}
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

This seems to be only available in the crate context, not publicly

src/coprocessor/circom/mod.rs Outdated Show resolved Hide resolved
huitseeker
huitseeker previously approved these changes Mar 12, 2024
Copy link
Member

@huitseeker huitseeker left a comment

Choose a reason for hiding this comment

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

Thanks a ton!

tchataigner and others added 20 commits March 13, 2024 09:14
- With the new implementation, we first try to fetc hthe gadget locally and fallback on remote when needed.
- Introduced a version over Circom Gadgets. Useful to point to a particular remote release.
- Better error propagation
- Precise error message based on where the flow is failing
This is WIP as there is a need to fix circom-scotia before being able to make the Keccak example work.

- Update framework -> stack in the README
- Create an example skeleton for a remote Keccak example
- Fix the circom example to use an imported Circom Gadget from the CLI
- Removed deleted keccak mod
- Fixed clippy warning
- Adapted circom-scotia to use main instead of dev
- Created a structure, CircomReference to qualify an identifier for CircomGadget
- Better check on CircomReference initialization
Arity is now inherited directly from the gadget, making it declarative by the gadget developer.
Co-authored-by: François Garillot <4142+huitseeker@users.noreply.github.com>
@tchataigner
Copy link
Member Author

@arthurpaulino thanks for noticing! Fixed this!

@tchataigner tchataigner added this pull request to the merge queue Mar 13, 2024
Merged via the queue into argumentcomputer:main with commit ddf26a1 Mar 13, 2024
11 checks passed
@tchataigner tchataigner deleted the feature/keccac-coproc branch March 13, 2024 23:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants