mirror of
https://github.com/logos-blockchain/logos-blockchain-specs.git
synced 2026-01-08 08:03:13 +00:00
* feat(cl/noir): provide an ergonomic Noir api for use within Python. * Add a python wrapper over the bigger constraint * Add the Bigger wrapper * cl/noir: document the NargoConstraint wrapper api * rewrite Bigger constraint as a dataclass * WIP: Cl/executable spec (#93) * wip: executable spec * WIP: test_1_to_1_transfer * hack: Vacous hash function * crypto: make sure prf returns field elements, fix ECC math in pedcom * hack(crypto): mock up a hash_to_curve implementation * feat(cl/1to1_xfr): fungibility domain uses hash_to_curve * cl: add type checking to InnerNote * cl/ptx: get balance commitments working * cl/noir: mv noir_constraint wrapper into cl/constraints/... * cl/ptx-note-proofs: start data modelling input and outputs * cl/ptx: 1-to-1 test is passing, but still, not quite finished * cl: remove tx_output from 1-to-1 transfer * cl: remove unused classes * cl: testing the balance commitments * wip: cl * cl: split main.rs into crypto.rs and note.rs * cl: split balance test * cl: add nullifier module * cl: partial_tx; input; output * cl: output proof tests * cl: partial transactions can now be built and verified * drop python cl spec * cl: test partial transaction balance commitment * cl: reverse partial tx balance (inputs are neg, outputs are pos) * cl: bundle of ptx * cl: verify bundle isn't balanced with just one unbalanced partial tx * cl: swap out ExtendedPoint for SubgroupPoint * cl: integrate groth16 death constraint validation * add risc0 zone * refactor risc0 zone * fix zone PoC * Add separate bin for stark2snark conv * cl: rename Note to NoteWitness * cl: merkle proofs * cl: merkle tree helper to pad elements * cl: ptx root implemented via merkle roots over inputs and outputs * cl: move from Commitment::from_witness to Witness::commit() * cl: tests passing again * cl: turn data model into library * cl: partial tx can compute paths to inputs / outputs * cl: begin integrating zone into cl data modal * cl: integrate simple zone into CL data model * cl: add missing cl patches * cl: swap jubjub for accel k256 * cl: pre-compute balance unit point outside stark * switch balance commitment to linear combination * cl: pre-compute pederson blinding * fix risc0 patching * switch to curve25519-dalek * cl: drop blake2; print prover time --------- Co-authored-by: Giacomo Pasini <g.pasini98@gmail.com> --------- Co-authored-by: Giacomo Pasini <g.pasini98@gmail.com>
189 lines
5.9 KiB
Rust
189 lines
5.9 KiB
Rust
/// This module defines the partial transaction structure.
|
|
///
|
|
/// Partial transactions, as the name suggests, are transactions
|
|
/// which on their own may not balance (i.e. \sum inputs != \sum outputs)
|
|
use crate::{
|
|
balance::Balance,
|
|
error::Error,
|
|
note::{NoteCommitment, NoteWitness},
|
|
nullifier::{Nullifier, NullifierNonce, NullifierSecret},
|
|
partial_tx::PtxRoot,
|
|
};
|
|
use rand_core::RngCore;
|
|
// use risc0_groth16::{PublicInputsJson, Verifier};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Input {
|
|
pub note_comm: NoteCommitment,
|
|
pub nullifier: Nullifier,
|
|
pub balance: Balance,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
|
pub struct InputWitness {
|
|
pub note: NoteWitness,
|
|
pub nf_sk: NullifierSecret,
|
|
pub nonce: NullifierNonce,
|
|
}
|
|
|
|
impl InputWitness {
|
|
pub fn random(note: NoteWitness, mut rng: impl RngCore) -> Self {
|
|
Self {
|
|
note,
|
|
nf_sk: NullifierSecret::random(&mut rng),
|
|
nonce: NullifierNonce::random(&mut rng),
|
|
}
|
|
}
|
|
|
|
pub fn commit(&self) -> Input {
|
|
Input {
|
|
note_comm: self.note.commit(self.nf_sk.commit(), self.nonce),
|
|
nullifier: Nullifier::new(self.nf_sk, self.nonce),
|
|
balance: self.note.balance(),
|
|
}
|
|
}
|
|
}
|
|
|
|
// as we don't have SNARKS hooked up yet, the witness will be our proof
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
pub struct InputProof {
|
|
input: InputWitness,
|
|
ptx_root: PtxRoot,
|
|
death_proof: Vec<u8>,
|
|
}
|
|
|
|
impl Input {
|
|
pub fn prove(
|
|
&self,
|
|
w: &InputWitness,
|
|
ptx_root: PtxRoot,
|
|
death_proof: Vec<u8>,
|
|
) -> Result<InputProof, Error> {
|
|
if bincode::serialize(&w.commit()).unwrap() != bincode::serialize(&self).unwrap() {
|
|
Err(Error::ProofFailed)
|
|
} else {
|
|
Ok(InputProof {
|
|
input: w.clone(),
|
|
ptx_root,
|
|
death_proof,
|
|
})
|
|
}
|
|
}
|
|
|
|
pub fn verify(&self, ptx_root: PtxRoot, proof: &InputProof) -> bool {
|
|
// verification checks the relation
|
|
// - nf_pk == hash(nf_sk)
|
|
// - note_comm == commit(note || nf_pk)
|
|
// - nullifier == hash(nf_sk || nonce)
|
|
// - balance == v * hash_to_curve(Unit) + blinding * H
|
|
// - ptx_root is the same one that was used in proving.
|
|
|
|
let witness = &proof.input;
|
|
|
|
let nf_pk = witness.nf_sk.commit();
|
|
|
|
// let death_constraint_was_committed_to =
|
|
// witness.note.death_constraint == bincode::serialize(&death_constraint).unwrap();
|
|
|
|
// let death_constraint_is_satisfied: bool = Verifier::from_json(
|
|
// bincode::deserialize(&proof.death_proof).unwrap(),
|
|
// PublicInputsJson {
|
|
// values: vec![ptx_root.hex()],
|
|
// },
|
|
// bincode::deserialize(&witness.note.death_constraint).unwrap(),
|
|
// )
|
|
// .unwrap()
|
|
// .verify()
|
|
// .is_ok();
|
|
let death_constraint_is_satisfied = true;
|
|
self.note_comm == witness.note.commit(nf_pk, witness.nonce)
|
|
&& self.nullifier == Nullifier::new(witness.nf_sk, witness.nonce)
|
|
&& self.balance == witness.note.balance()
|
|
&& ptx_root == proof.ptx_root
|
|
// && death_constraint_was_committed_to
|
|
&& death_constraint_is_satisfied
|
|
}
|
|
|
|
pub fn to_bytes(&self) -> [u8; 96] {
|
|
let mut bytes = [0u8; 96];
|
|
bytes[..32].copy_from_slice(self.note_comm.as_bytes());
|
|
bytes[32..64].copy_from_slice(self.nullifier.as_bytes());
|
|
bytes[64..96].copy_from_slice(&self.balance.to_bytes());
|
|
bytes
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
use crate::{nullifier::NullifierNonce, test_util::seed_rng};
|
|
|
|
#[test]
|
|
fn test_input_proof() {
|
|
let mut rng = seed_rng(0);
|
|
|
|
let ptx_root = PtxRoot::default();
|
|
|
|
let note = NoteWitness::new(10, "NMO", [0u8; 32], &mut rng);
|
|
let nf_sk = NullifierSecret::random(&mut rng);
|
|
let nonce = NullifierNonce::random(&mut rng);
|
|
|
|
let input_witness = InputWitness { note, nf_sk, nonce };
|
|
|
|
let input = input_witness.commit();
|
|
let proof = input.prove(&input_witness, ptx_root, vec![]).unwrap();
|
|
|
|
assert!(input.verify(ptx_root, &proof));
|
|
|
|
let wrong_witnesses = [
|
|
InputWitness {
|
|
note: NoteWitness::new(11, "NMO", [0u8; 32], &mut rng),
|
|
..input_witness.clone()
|
|
},
|
|
InputWitness {
|
|
note: NoteWitness::new(10, "ETH", [0u8; 32], &mut rng),
|
|
..input_witness.clone()
|
|
},
|
|
InputWitness {
|
|
nf_sk: NullifierSecret::random(&mut rng),
|
|
..input_witness.clone()
|
|
},
|
|
InputWitness {
|
|
nonce: NullifierNonce::random(&mut rng),
|
|
..input_witness.clone()
|
|
},
|
|
];
|
|
|
|
for wrong_witness in wrong_witnesses {
|
|
assert!(input.prove(&wrong_witness, ptx_root, vec![]).is_err());
|
|
|
|
let wrong_input = wrong_witness.commit();
|
|
let wrong_proof = wrong_input.prove(&wrong_witness, ptx_root, vec![]).unwrap();
|
|
assert!(!input.verify(ptx_root, &wrong_proof));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_input_ptx_coupling() {
|
|
let mut rng = seed_rng(0);
|
|
|
|
let note = NoteWitness::new(10, "NMO", [0u8; 32], &mut rng);
|
|
let nf_sk = NullifierSecret::random(&mut rng);
|
|
let nonce = NullifierNonce::random(&mut rng);
|
|
|
|
let witness = InputWitness { note, nf_sk, nonce };
|
|
|
|
let input = witness.commit();
|
|
|
|
let ptx_root = PtxRoot::random(&mut rng);
|
|
let proof = input.prove(&witness, ptx_root, vec![]).unwrap();
|
|
|
|
assert!(input.verify(ptx_root, &proof));
|
|
|
|
// The same input proof can not be used in another partial transaction.
|
|
let another_ptx_root = PtxRoot::random(&mut rng);
|
|
assert!(!input.verify(another_ptx_root, &proof));
|
|
}
|
|
}
|