cl: partial tx can compute paths to inputs / outputs
This commit is contained in:
parent
9c1317f55b
commit
0b2ca37f59
|
@ -12,10 +12,11 @@ lazy_static! {
|
|||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct Balance(#[serde(with = "serde_point")] pub SubgroupPoint);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct BalanceWitness {
|
||||
pub value: u64,
|
||||
pub unit: String,
|
||||
#[serde(with = "serde_scalar")]
|
||||
pub blinding: Scalar,
|
||||
}
|
||||
|
||||
|
@ -56,6 +57,51 @@ pub fn balance(value: u64, unit: &str, blinding: Scalar) -> SubgroupPoint {
|
|||
unit_point(unit) * value_scalar + *PEDERSON_COMMITMENT_BLINDING_POINT * blinding
|
||||
}
|
||||
|
||||
mod serde_scalar {
|
||||
use super::Scalar;
|
||||
use serde::de::{self, Visitor};
|
||||
use serde::{Deserializer, Serializer};
|
||||
use std::fmt;
|
||||
|
||||
// Serialize a SubgroupPoint by converting it to bytes.
|
||||
pub fn serialize<S>(scalar: &Scalar, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let bytes = scalar.to_bytes();
|
||||
serializer.serialize_bytes(&bytes)
|
||||
}
|
||||
|
||||
// Deserialize a SubgroupPoint by converting it from bytes.
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Scalar, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct BytesVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for BytesVisitor {
|
||||
type Value = Scalar;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a valid Scalar in byte representation")
|
||||
}
|
||||
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
let mut bytes = <jubjub::SubgroupPoint as group::GroupEncoding>::Repr::default();
|
||||
assert_eq!(bytes.len(), v.len());
|
||||
bytes.copy_from_slice(v);
|
||||
|
||||
Ok(Scalar::from_bytes(&bytes).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_bytes(BytesVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
mod serde_point {
|
||||
use super::SubgroupPoint;
|
||||
use group::GroupEncoding;
|
||||
|
|
|
@ -99,11 +99,11 @@ mod test {
|
|||
let mut rng = seed_rng(0);
|
||||
|
||||
let nmo_10_in =
|
||||
InputWitness::random(NoteWitness::new(10, "NMO", vec![], &mut rng), &mut rng);
|
||||
InputWitness::random(NoteWitness::new(10, "NMO", [0u8; 32], &mut rng), &mut rng);
|
||||
let eth_23_in =
|
||||
InputWitness::random(NoteWitness::new(23, "ETH", vec![], &mut rng), &mut rng);
|
||||
InputWitness::random(NoteWitness::new(23, "ETH", [0u8; 32], &mut rng), &mut rng);
|
||||
let crv_4840_out = OutputWitness::random(
|
||||
NoteWitness::new(4840, "CRV", vec![], &mut rng),
|
||||
NoteWitness::new(4840, "CRV", [0u8; 32], &mut rng),
|
||||
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
|
||||
&mut rng,
|
||||
);
|
||||
|
@ -132,14 +132,14 @@ mod test {
|
|||
);
|
||||
|
||||
let crv_4840_in =
|
||||
InputWitness::random(NoteWitness::new(4840, "CRV", vec![], &mut rng), &mut rng);
|
||||
InputWitness::random(NoteWitness::new(4840, "CRV", [0u8; 32], &mut rng), &mut rng);
|
||||
let nmo_10_out = OutputWitness::random(
|
||||
NoteWitness::new(10, "NMO", vec![], &mut rng),
|
||||
NoteWitness::new(10, "NMO", [0u8; 32], &mut rng),
|
||||
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
|
||||
&mut rng,
|
||||
);
|
||||
let eth_23_out = OutputWitness::random(
|
||||
NoteWitness::new(23, "ETH", vec![], &mut rng),
|
||||
NoteWitness::new(23, "ETH", [0u8; 32], &mut rng),
|
||||
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
|
||||
&mut rng,
|
||||
);
|
||||
|
|
|
@ -125,7 +125,7 @@ mod test {
|
|||
|
||||
let ptx_root = PtxRoot::default();
|
||||
|
||||
let note = NoteWitness::new(10, "NMO", vec![], &mut rng);
|
||||
let note = NoteWitness::new(10, "NMO", [0u8; 32], &mut rng);
|
||||
let nf_sk = NullifierSecret::random(&mut rng);
|
||||
let nonce = NullifierNonce::random(&mut rng);
|
||||
|
||||
|
@ -138,11 +138,11 @@ mod test {
|
|||
|
||||
let wrong_witnesses = [
|
||||
InputWitness {
|
||||
note: NoteWitness::new(11, "NMO", vec![], &mut rng),
|
||||
note: NoteWitness::new(11, "NMO", [0u8; 32], &mut rng),
|
||||
..input_witness.clone()
|
||||
},
|
||||
InputWitness {
|
||||
note: NoteWitness::new(10, "ETH", vec![], &mut rng),
|
||||
note: NoteWitness::new(10, "ETH", [0u8; 32], &mut rng),
|
||||
..input_witness.clone()
|
||||
},
|
||||
InputWitness {
|
||||
|
@ -168,7 +168,7 @@ mod test {
|
|||
fn test_input_ptx_coupling() {
|
||||
let mut rng = seed_rng(0);
|
||||
|
||||
let note = NoteWitness::new(10, "NMO", vec![], &mut rng);
|
||||
let note = NoteWitness::new(10, "NMO", [0u8; 32], &mut rng);
|
||||
let nf_sk = NullifierSecret::random(&mut rng);
|
||||
let nonce = NullifierNonce::random(&mut rng);
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use blake2::{Blake2s256, Digest};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub fn padded_leaves<const N: usize>(elements: &[Vec<u8>]) -> [[u8; 32]; N] {
|
||||
let mut leaves = [[0u8; 32]; N];
|
||||
|
||||
for (i, element) in elements.into_iter().enumerate() {
|
||||
for (i, element) in elements.iter().enumerate() {
|
||||
assert!(i < N);
|
||||
leaves[i] = leaf(element);
|
||||
}
|
||||
|
@ -42,7 +43,7 @@ pub fn root<const N: usize>(elements: [[u8; 32]; N]) -> [u8; 32] {
|
|||
nodes[0]
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum PathNode {
|
||||
Left([u8; 32]),
|
||||
Right([u8; 32]),
|
||||
|
@ -65,12 +66,12 @@ pub fn verify_path(leaf: [u8; 32], path: &[PathNode], root: [u8; 32]) -> bool {
|
|||
computed_hash == root
|
||||
}
|
||||
|
||||
pub fn path<const N: usize>(elements: [&[u8]; N], idx: usize) -> Vec<PathNode> {
|
||||
let n = elements.len();
|
||||
pub fn path<const N: usize>(leaves: [[u8; 32]; N], idx: usize) -> Vec<PathNode> {
|
||||
let n = leaves.len();
|
||||
assert!(n.is_power_of_two());
|
||||
assert!(idx < n);
|
||||
|
||||
let mut nodes = Vec::from_iter(elements.into_iter().map(leaf));
|
||||
let mut nodes = leaves;
|
||||
let mut path = Vec::new();
|
||||
let mut idx = idx;
|
||||
|
||||
|
@ -157,9 +158,10 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_path_height_1() {
|
||||
let r = root::<1>(padded_leaves(&[b"desert".into()]));
|
||||
let leaves = padded_leaves(&[b"desert".into()]);
|
||||
let r = root::<1>(leaves);
|
||||
|
||||
let p = path([b"desert"], 0);
|
||||
let p = path::<1>(leaves, 0);
|
||||
let expected = vec![];
|
||||
assert_eq!(p, expected);
|
||||
assert!(verify_path(leaf(b"desert"), &p, r));
|
||||
|
@ -167,18 +169,19 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_path_height_2() {
|
||||
let r = root::<2>(padded_leaves(&[b"desert".into(), b"sand".into()]));
|
||||
let leaves = padded_leaves(&[b"desert".into(), b"sand".into()]);
|
||||
let r = root::<2>(leaves);
|
||||
|
||||
// --- proof for element at idx 0
|
||||
|
||||
let p0 = path([b"desert", b"sand"], 0);
|
||||
let p0 = path(leaves, 0);
|
||||
let expected0 = vec![PathNode::Right(leaf(b"sand"))];
|
||||
assert_eq!(p0, expected0);
|
||||
assert!(verify_path(leaf(b"desert"), &p0, r));
|
||||
|
||||
// --- proof for element at idx 1
|
||||
|
||||
let p1 = path([b"desert", b"sand"], 1);
|
||||
let p1 = path(leaves, 1);
|
||||
let expected1 = vec![PathNode::Left(leaf(b"desert"))];
|
||||
assert_eq!(p1, expected1);
|
||||
assert!(verify_path(leaf(b"sand"), &p1, r));
|
||||
|
@ -186,16 +189,17 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_path_height_3() {
|
||||
let r = root::<4>(padded_leaves(&[
|
||||
let leaves = padded_leaves(&[
|
||||
b"desert".into(),
|
||||
b"sand".into(),
|
||||
b"feels".into(),
|
||||
b"warm".into(),
|
||||
]));
|
||||
]);
|
||||
let r = root::<4>(leaves);
|
||||
|
||||
// --- proof for element at idx 0
|
||||
|
||||
let p0 = path([b"desert", b"sand", b"feels", b"warm"], 0);
|
||||
let p0 = path(leaves, 0);
|
||||
let expected0 = vec![
|
||||
PathNode::Right(leaf(b"sand")),
|
||||
PathNode::Right(node(leaf(b"feels"), leaf(b"warm"))),
|
||||
|
@ -205,7 +209,7 @@ mod test {
|
|||
|
||||
// --- proof for element at idx 1
|
||||
|
||||
let p1 = path([b"desert", b"sand", b"feels", b"warm"], 1);
|
||||
let p1 = path(leaves, 1);
|
||||
let expected1 = vec![
|
||||
PathNode::Left(leaf(b"desert")),
|
||||
PathNode::Right(node(leaf(b"feels"), leaf(b"warm"))),
|
||||
|
@ -215,7 +219,7 @@ mod test {
|
|||
|
||||
// --- proof for element at idx 2
|
||||
|
||||
let p2 = path([b"desert", b"sand", b"feels", b"warm"], 2);
|
||||
let p2 = path(leaves, 2);
|
||||
let expected2 = vec![
|
||||
PathNode::Right(leaf(b"warm")),
|
||||
PathNode::Left(node(leaf(b"desert"), leaf(b"sand"))),
|
||||
|
@ -225,7 +229,7 @@ mod test {
|
|||
|
||||
// --- proof for element at idx 3
|
||||
|
||||
let p3 = path([b"desert", b"sand", b"feels", b"warm"], 3);
|
||||
let p3 = path(leaves, 3);
|
||||
let expected3 = vec![
|
||||
PathNode::Left(leaf(b"feels")),
|
||||
PathNode::Left(node(leaf(b"desert"), leaf(b"sand"))),
|
||||
|
|
|
@ -19,7 +19,7 @@ impl NoteCommitment {
|
|||
|
||||
// TODO: Rename Note to NoteWitness and NoteCommitment to Note
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct NoteWitness {
|
||||
pub balance: BalanceWitness,
|
||||
pub death_constraint: Vec<u8>, // serialized verification key of death constraint
|
||||
|
@ -27,16 +27,11 @@ pub struct NoteWitness {
|
|||
}
|
||||
|
||||
impl NoteWitness {
|
||||
pub fn new(
|
||||
value: u64,
|
||||
unit: impl Into<String>,
|
||||
death_constraint: Vec<u8>,
|
||||
rng: impl RngCore,
|
||||
) -> Self {
|
||||
pub fn new(value: u64, unit: impl Into<String>, state: [u8; 32], rng: impl RngCore) -> Self {
|
||||
Self {
|
||||
balance: BalanceWitness::random(value, unit, rng),
|
||||
death_constraint,
|
||||
state: [0u8; 32],
|
||||
death_constraint: vec![],
|
||||
state,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,8 +72,8 @@ mod test {
|
|||
#[test]
|
||||
fn test_note_commitments_dont_commit_to_balance_blinding() {
|
||||
let mut rng = seed_rng(0);
|
||||
let n1 = NoteWitness::new(12, "NMO", vec![], &mut rng);
|
||||
let n2 = NoteWitness::new(12, "NMO", vec![], &mut rng);
|
||||
let n1 = NoteWitness::new(12, "NMO", [0u8; 32], &mut rng);
|
||||
let n2 = NoteWitness::new(12, "NMO", [0u8; 32], &mut rng);
|
||||
|
||||
let nf_pk = NullifierSecret::random(&mut rng).commit();
|
||||
let nonce = NullifierNonce::random(&mut rng);
|
||||
|
|
|
@ -78,7 +78,7 @@ mod test {
|
|||
fn test_output_proof() {
|
||||
let mut rng = seed_rng(0);
|
||||
|
||||
let note = NoteWitness::new(10, "NMO", vec![], &mut rng);
|
||||
let note = NoteWitness::new(10, "NMO", [0u8; 32], &mut rng);
|
||||
let nf_pk = NullifierSecret::random(&mut rng).commit();
|
||||
let nonce = NullifierNonce::random(&mut rng);
|
||||
|
||||
|
@ -91,11 +91,11 @@ mod test {
|
|||
|
||||
let wrong_witnesses = [
|
||||
OutputWitness {
|
||||
note: NoteWitness::new(11, "NMO", vec![], &mut rng),
|
||||
note: NoteWitness::new(11, "NMO", [0u8; 32], &mut rng),
|
||||
..witness.clone()
|
||||
},
|
||||
OutputWitness {
|
||||
note: NoteWitness::new(10, "ETH", vec![], &mut rng),
|
||||
note: NoteWitness::new(10, "ETH", [0u8; 32], &mut rng),
|
||||
..witness.clone()
|
||||
},
|
||||
OutputWitness {
|
||||
|
|
|
@ -56,14 +56,14 @@ impl PartialTx {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn root(&self) -> PtxRoot {
|
||||
// COMMITMENT TO INPUTS
|
||||
pub fn input_root(&self) -> [u8; 32] {
|
||||
let input_bytes =
|
||||
Vec::from_iter(self.inputs.iter().map(Input::to_bytes).map(Vec::from_iter));
|
||||
let input_merkle_leaves = merkle::padded_leaves(&input_bytes);
|
||||
let input_root = merkle::root::<MAX_INPUTS>(input_merkle_leaves);
|
||||
merkle::root::<MAX_INPUTS>(input_merkle_leaves)
|
||||
}
|
||||
|
||||
// COMMITMENT TO OUTPUTS
|
||||
pub fn output_root(&self) -> [u8; 32] {
|
||||
let output_bytes = Vec::from_iter(
|
||||
self.outputs
|
||||
.iter()
|
||||
|
@ -71,8 +71,30 @@ impl PartialTx {
|
|||
.map(Vec::from_iter),
|
||||
);
|
||||
let output_merkle_leaves = merkle::padded_leaves(&output_bytes);
|
||||
let output_root = merkle::root::<MAX_OUTPUTS>(output_merkle_leaves);
|
||||
merkle::root::<MAX_OUTPUTS>(output_merkle_leaves)
|
||||
}
|
||||
|
||||
pub fn input_merkle_path(&self, idx: usize) -> Vec<merkle::PathNode> {
|
||||
let input_bytes =
|
||||
Vec::from_iter(self.inputs.iter().map(Input::to_bytes).map(Vec::from_iter));
|
||||
let input_merkle_leaves = merkle::padded_leaves::<MAX_INPUTS>(&input_bytes);
|
||||
merkle::path(input_merkle_leaves, idx)
|
||||
}
|
||||
|
||||
pub fn output_merkle_path(&self, idx: usize) -> Vec<merkle::PathNode> {
|
||||
let output_bytes = Vec::from_iter(
|
||||
self.outputs
|
||||
.iter()
|
||||
.map(Output::to_bytes)
|
||||
.map(Vec::from_iter),
|
||||
);
|
||||
let output_merkle_leaves = merkle::padded_leaves::<MAX_OUTPUTS>(&output_bytes);
|
||||
merkle::path(output_merkle_leaves, idx)
|
||||
}
|
||||
|
||||
pub fn root(&self) -> PtxRoot {
|
||||
let input_root = self.input_root();
|
||||
let output_root = self.output_root();
|
||||
let root = merkle::node(input_root, output_root);
|
||||
PtxRoot(root)
|
||||
}
|
||||
|
@ -154,10 +176,12 @@ mod test {
|
|||
fn test_partial_tx_proof() {
|
||||
let mut rng = seed_rng(0);
|
||||
|
||||
let nmo_10 = InputWitness::random(NoteWitness::new(10, "NMO", vec![], &mut rng), &mut rng);
|
||||
let eth_23 = InputWitness::random(NoteWitness::new(23, "ETH", vec![], &mut rng), &mut rng);
|
||||
let nmo_10 =
|
||||
InputWitness::random(NoteWitness::new(10, "NMO", [0u8; 32], &mut rng), &mut rng);
|
||||
let eth_23 =
|
||||
InputWitness::random(NoteWitness::new(23, "ETH", [0u8; 32], &mut rng), &mut rng);
|
||||
let crv_4840 = OutputWitness::random(
|
||||
NoteWitness::new(4840, "CRV", vec![], &mut rng),
|
||||
NoteWitness::new(4840, "CRV", [0u8; 32], &mut rng),
|
||||
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
|
||||
&mut rng,
|
||||
);
|
||||
|
@ -178,10 +202,12 @@ mod test {
|
|||
fn test_partial_tx_balance() {
|
||||
let mut rng = seed_rng(0);
|
||||
|
||||
let nmo_10 = InputWitness::random(NoteWitness::new(10, "NMO", vec![], &mut rng), &mut rng);
|
||||
let eth_23 = InputWitness::random(NoteWitness::new(23, "ETH", vec![], &mut rng), &mut rng);
|
||||
let nmo_10 =
|
||||
InputWitness::random(NoteWitness::new(10, "NMO", [0u8; 32], &mut rng), &mut rng);
|
||||
let eth_23 =
|
||||
InputWitness::random(NoteWitness::new(23, "ETH", [0u8; 32], &mut rng), &mut rng);
|
||||
let crv_4840 = OutputWitness::random(
|
||||
NoteWitness::new(4840, "CRV", vec![], &mut rng),
|
||||
NoteWitness::new(4840, "CRV", [0u8; 32], &mut rng),
|
||||
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
|
||||
&mut rng,
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue