-
Notifications
You must be signed in to change notification settings - Fork 1
Risc0 circuit #98
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
Risc0 circuit #98
Conversation
Co-authored-by: jonas pauli <[email protected]>
I personally liked my initial structure a lot better. It also doesn't look like there are any tests for the proving process. |
No tests yet. But they compile with different toolchains so some separation is required. The bare minimum logic is in the risc0 workspace since that workspace is still broken for RA. |
Also how am I supposed to construct a Snapshot when there are no tests? |
Proving a Batch should not be an API endpoint. It should happen automatically on the kairos-server every n seconds / era / transactions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without tests this can't be merged. We need an integration test for a scenario where a few Transactions have occurred and the batch proof is generated. Then the proof should be verified.
Such a tests is a requirement to
- see that the circuit code works
- develop the contract entry point that verifies the proof
fine grained tests will come soon, snapshot is constructed by existing code in the server (batch manager) if you want to take a crack at constructing one take a look at that, and or the property tests i have in the trie. We need a good property test generator like i have in the ops trie tests. with that being said if you look at the signature of the trait I introduced in the prover server and implement in the prover, that is what you should build the contract end point around. it should provide snapshot, ProofInputs and handle the output. |
I can't begin to write the contract entry point before this PR has been merged and this PR can't be merged without tests. The entry point will depend on types exposed by this PR so therefore we need to merge it first and it must be obvious how a proof is constructed and we must know for sure that the circuit code works as is. |
i'd suggest pulling this branch into a branch based on the refactored deposit contract and following the types from there. alternatively just copy the ProofInputs/Outputs types directly into your file and don't worry about the function that turns one to the other. just make the body todo and make the logic all type check and lint. I can write an initial prop test later today or tomorrow. but don't let that block you the proof Inputs and Outputs types are fixed. if you write the logic around those types it will work. |
This workflow is incredibly messy, we already have too many branches and I'm sure that most of us have lost track of what's happening on them. Merging all of this will be a huge pain since Marijan is also currently trying to merge something from his deposit contract into my deposit contract. |
it is indeed, maybe one day pijul or stacked diffs will save us, but until then we have to live with the trade offs. complete features in a single pr are going to result in branches that live a while. on the other hand merging incomplete code results in churn and quality issues. copying two types is about the minimum amount of coupling between branches you could do. but yes it's always a mess, until the rebase. |
66b841a
to
3bcc368
Compare
Minimum allowed coverage is Generated by 🐒 cobertura-action against 3bcc368 |
Co-Authored-By: Marijan Petričević <[email protected]>
kairos-prover/kairos-tx/Cargo.toml
Outdated
sha2 = "0.10" | ||
num-traits = { version = "0.2", default-features = false } | ||
rasn = { version = "0.14", default-features = false, features = ["macros"] } | ||
sha2 = { version = "0.10", default-features = false } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that mean we have to add the patch to the workspace or is it enough if one crate patches it?
impl Default for AccountsState { | ||
fn default() -> Self { | ||
unreachable!("AccountsState should always be created with AccountsState::new()"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this considered good practice?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never seen it before, but it successfully caught usage of proptest::any instead of any_with.
Proptest calls default if you don't provide a the argument.
I don't think I use AccountsState as a Arbitrary parameter any more but it's easy to accidentally do when writing proptests with the test-strategy macros.
|
||
#[derive(Debug, Clone)] | ||
pub struct Accounts<A: Debug> { | ||
pub pub_keys: Vec<Rc<PublicKey>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we maintain an additional list of pub_keys
here? isn't it a duplication of the hashmap below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we sample keys from the vector. hashmaps can't have an efficient random sample function. usize -> PublicKey
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you want random samples? You could just hardcode test vectors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Random samples are part of property testing.
In proptest or quickcheck you write code to generate pseudo random instances of your type and then test your code works as expected.
This is better than fixed testing because you cover more of your functions domain.
If your code fails the your test will be run again with a semantically smaller input. This is called shrinking. It allows you to find a minimal example that breaks your assumptions.
Ok(()) | ||
} | ||
|
||
pub fn deposit(&mut self, deposit: &L1Deposit) -> Result<(), TxnErr> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the choice to make this associated function take the L1Deposit
struct directly but in withdraw
and transfer
you decompose the types. We could make it uniform and use the structs in withdraw
and transfer
too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
L1Deposit
contains the recipient public key, and amount, it's only a payload.
The transfer and withdraw take a payload and the sender public key and a nonce.
The sender account will have the fund removed, and nonce checked/bumped.
A deposit has no sender account, since it comes from the L1 so it's only a payload.
fn transfer(&mut self, sender: &PublicKey, transfer: &Transfer, nonce: u64) -> Result<(), TxnErr>
fn withdraw(&mut self, withdrawer: &PublicKey, withdraw: &Withdraw, nonce: u64) -> Result<(), TxnErr>
I noticed that As clarified with @marijanp, this change was implemented to resolve an issue with Nix sourcing. I am really unhappy about such changes. Nix should be an extra feature, not something that compromises the quality of our codebase. Update: fixed in #112. |
/// TODO consider replacing with a count and hash of the processed deposits | ||
pub deposits: Box<[L1Deposit]>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For future optimization we need hashing deposits, that's for sure. Well, that could be solved later with kairos-tx
used for deposit - #88.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could just make this a Vec of Strings and use hex::encode(deposit.hash())
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is something different.
By hashing here I mean hashing over all the deposits in the batch.
That gives a single hash to serialize, but comes at the cost of an additional sha256 in and outside the vm.
Not sure if it's worth it, but it's a possible future optimization.
If we want (I think we should) to verify signatures inside ZK VM, then I am not sure why we have duplicate types, e.g. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Avi-D-coder Great work, especially trie integration!
I am requesting one change - use explicit prover to avoid Bonsai, then I would be happy to approve this PR.
kairos-tx: move to project root
Risc0 dev when testing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of the suggestions are good, but I think that we can merge this PR already and update bits of it soon after. I don't see any major issues with it and therefore think that it's ready to be made available in the main workspace.
for batch in batches.into_iter() { | ||
let mut account_trie = AccountTrie::new_try_from_db(db.clone(), prior_root_hash) | ||
.expect("Failed to create account trie"); | ||
account_trie | ||
.apply_batch(batch.iter().cloned()) | ||
.expect("Failed to apply batch"); | ||
|
||
let new_root_hash = account_trie | ||
.txn | ||
.commit(&mut DigestHasher::<sha2::Sha256>::default()) | ||
.expect("Failed to commit transaction"); | ||
|
||
let trie_snapshot = account_trie.txn.build_initial_snapshot(); | ||
|
||
let proof_inputs = ProofInputs { | ||
transactions: batch.into_boxed_slice(), | ||
trie_snapshot, | ||
}; | ||
|
||
let ProofOutputs { | ||
pre_batch_trie_root, | ||
post_batch_trie_root, | ||
deposits: _, | ||
withdrawals: _, | ||
} = proving_hook(proof_inputs).expect("Failed to prove execution"); | ||
|
||
assert_eq!(pre_batch_trie_root, prior_root_hash); | ||
assert_eq!(post_batch_trie_root, new_root_hash); | ||
prior_root_hash = new_root_hash; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
db is not updated in this loop => the test fails for the second Batch when using the real proving backend.
Suggestion for improvement: update the db variable or only test with a single Batch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have tested this and got an account does not exist
error since the second Batch doesn't start with a Deposit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Avi-D-coder Have you seen this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
commit updates the db.
Not sure what error you are hitting.
I don't get the error and CI passes.
Let's debug on your machine after our meeting.
|
||
#[derive(Debug, Clone)] | ||
pub struct Accounts<A: Debug> { | ||
pub pub_keys: Vec<Rc<PublicKey>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you want random samples? You could just hardcode test vectors.
/// TODO consider replacing with a count and hash of the processed deposits | ||
pub deposits: Box<[L1Deposit]>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could just make this a Vec of Strings and use hex::encode(deposit.hash())
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
This is the risc0 circuit that me and @jonas089 worked on, the branch is based on @marijanp's nixified risc0 toolchain.
TODO (not necessarily before merge):
tests.kairos-circuit-logic
.The logic in the server does a bunch of prechecks that are important in that setting.
use patched sha256 crate from risc0