feat: add more curves

This commit is contained in:
rymnc
2024-03-12 13:23:07 +05:30
parent 69d478146b
commit 531cbf54ce
9 changed files with 499 additions and 172 deletions

26
Cargo.lock generated
View File

@@ -48,6 +48,29 @@ dependencies = [
"memchr",
]
[[package]]
name = "ark-bls12-377"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb00293ba84f51ce3bd026bd0de55899c4e68f0a39a5728cebae3a73ffdc0a4f"
dependencies = [
"ark-ec",
"ark-ff",
"ark-std",
]
[[package]]
name = "ark-bls12-381"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488"
dependencies = [
"ark-ec",
"ark-ff",
"ark-serialize",
"ark-std",
]
[[package]]
name = "ark-bn254"
version = "0.4.0"
@@ -736,6 +759,8 @@ dependencies = [
name = "erc-5564-bn254"
version = "0.1.0"
dependencies = [
"ark-bls12-377",
"ark-bls12-381",
"ark-bn254",
"ark-ec",
"ark-ff",
@@ -746,6 +771,7 @@ dependencies = [
"num-traits",
"rln",
"serde_json",
"tiny-keccak",
]
[[package]]

View File

@@ -11,7 +11,7 @@ crate-type = ["staticlib"]
[features]
ffi = []
include_rln_ffi = []
default = ["ffi"]
default = []
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -22,6 +22,9 @@ num-bigint = "0.4.3"
num-traits = "0.2.15"
ark-ff = "0.4.1"
ark-bn254 = "0.4.0"
ark-bls12-381 = "0.4.0"
ark-bls12-377 = "0.4.0"
tiny-keccak = { version = "=2.0.2", features = ["keccak"] }
ark-ec = "0.4.1"
ark-serialize = "0.4.1"

View File

@@ -1,7 +1,13 @@
# erc-5564-bn254
# erc-5564-rs
Uses the [arkworks-rs](https://github.com/arkworks-rs/curves) suite of libraries, and utilities from [rln](https://github.com/vacp2p/zerokit)
## Existing Implementations
1. `ark_bn254`
2. `ark_bls_12_381`
3. `ark_bls_12_377`
## Usage
Note: this scheme should be used with the fork of [circom-rln](https://github.com/rymnc/circom-rln-erc5564).

118
src/bls12_377_impl.rs Normal file
View File

@@ -0,0 +1,118 @@
use crate::stealth_commitments::{AffineWrapper, RawFr, StealthAddressOnCurve};
use ark_bls12_377::g1::{G1_GENERATOR_X, G1_GENERATOR_Y};
use ark_bls12_377::{Fq, Fr, G1Affine, G1Projective};
use ark_ff::PrimeField;
use tiny_keccak::{Hasher, Keccak};
#[allow(non_camel_case_types)]
pub struct Bls12_377_G1Affine(G1Affine);
impl AffineWrapper for Bls12_377_G1Affine {
type Fq = Fq;
fn new(x: Self::Fq, y: Self::Fq) -> Self {
Bls12_377_G1Affine(G1Affine::new(x, y))
}
}
impl From<Bls12_377_G1Affine> for G1Projective {
fn from(value: Bls12_377_G1Affine) -> Self {
G1Projective::from(value.0)
}
}
impl RawFr for Fr {
type Fr = Fr;
fn as_u64(&self) -> u64 {
self.0 .0[0]
}
}
impl StealthAddressOnCurve for ark_bls12_377::Bls12_377 {
type Projective = G1Projective;
type Affine = Bls12_377_G1Affine;
type Fr = Fr;
fn derive_public_key(private_key: &Self::Fr) -> Self::Projective {
let g1_generator_affine = Self::Affine::new(G1_GENERATOR_X, G1_GENERATOR_Y);
(Self::Projective::from(g1_generator_affine)) * *private_key
}
fn hash_to_fr(input: &[u8]) -> Self::Fr {
let mut hash = [0; 32];
let mut hasher = Keccak::v256();
hasher.update(input);
hasher.finalize(&mut hash);
// We export the hash as a field element
Self::Fr::from_le_bytes_mod_order(hash.as_slice())
}
}
#[cfg(test)]
mod tests {
use super::*;
use ark_ec::CurveGroup;
type Curve = ark_bls12_377::Bls12_377;
#[test]
fn test_random_keypair() {
let (key, pub_key) = Curve::random_keypair();
// Check the derived key matches the one generated from original key
assert_eq!(Curve::derive_public_key(&key), pub_key);
}
#[test]
fn test_hash_to_fr() {
// Test that hash_to_fr(input_1) != hash_to_fr(input_2) when input_1 != input_2
let input_1 = b"input_1";
let input_2 = b"input_2";
assert_ne!(Curve::hash_to_fr(input_1), Curve::hash_to_fr(input_2));
}
#[test]
fn test_compute_shared_point() {
// In a multiple participant scenario, any participant's public key
// combined with any other participant's private key should arrive at the same shared key
let (key1, pub_key1) = Curve::random_keypair();
let (key2, pub_key2) = Curve::random_keypair();
let shared1 = Curve::compute_shared_point(key1, pub_key2);
let shared2 = Curve::compute_shared_point(key2, pub_key1);
// Convert Projective to Affine for equality comparison
let shared1_affine = shared1.into_affine();
let shared2_affine = shared2.into_affine();
assert_eq!(shared1_affine.x, shared2_affine.x);
assert_eq!(shared1_affine.y, shared2_affine.y);
}
#[test]
fn test_stealth_commitment_generation() {
let (spending_key, spending_public_key) = Curve::random_keypair();
let (viewing_key, viewing_public_key) = Curve::random_keypair();
// generate ephemeral keypair
let (ephemeral_private_key, ephemeral_public_key) = Curve::random_keypair();
let (stealth_commitment, view_tag) = Curve::generate_stealth_commitment(
viewing_public_key,
spending_public_key,
ephemeral_private_key,
);
let stealth_private_key_opt = Curve::generate_stealth_private_key(
ephemeral_public_key,
viewing_key,
spending_key,
view_tag,
);
if stealth_private_key_opt.is_none() {
panic!("View tags did not match");
}
let derived_commitment = Curve::derive_public_key(&stealth_private_key_opt.unwrap());
assert_eq!(derived_commitment, stealth_commitment);
}
}

118
src/bls12_381_impl.rs Normal file
View File

@@ -0,0 +1,118 @@
use crate::stealth_commitments::{AffineWrapper, RawFr, StealthAddressOnCurve};
use ark_bls12_381::g1::{G1_GENERATOR_X, G1_GENERATOR_Y};
use ark_bls12_381::{Fq, Fr, G1Affine, G1Projective};
use ark_ff::PrimeField;
use tiny_keccak::{Hasher, Keccak};
#[allow(non_camel_case_types)]
pub struct Bls12_381_G1Affine(G1Affine);
impl AffineWrapper for Bls12_381_G1Affine {
type Fq = Fq;
fn new(x: Self::Fq, y: Self::Fq) -> Self {
Bls12_381_G1Affine(G1Affine::new(x, y))
}
}
impl From<Bls12_381_G1Affine> for G1Projective {
fn from(value: Bls12_381_G1Affine) -> Self {
G1Projective::from(value.0)
}
}
impl RawFr for Fr {
type Fr = Fr;
fn as_u64(&self) -> u64 {
self.0 .0[0]
}
}
impl StealthAddressOnCurve for ark_bls12_381::Bls12_381 {
type Projective = G1Projective;
type Affine = Bls12_381_G1Affine;
type Fr = Fr;
fn derive_public_key(private_key: &Self::Fr) -> Self::Projective {
let g1_generator_affine = Self::Affine::new(G1_GENERATOR_X, G1_GENERATOR_Y);
(Self::Projective::from(g1_generator_affine)) * *private_key
}
fn hash_to_fr(input: &[u8]) -> Self::Fr {
let mut hash = [0; 32];
let mut hasher = Keccak::v256();
hasher.update(input);
hasher.finalize(&mut hash);
// We export the hash as a field element
Self::Fr::from_le_bytes_mod_order(hash.as_slice())
}
}
#[cfg(test)]
mod tests {
use super::*;
use ark_ec::CurveGroup;
type Curve = ark_bls12_381::Bls12_381;
#[test]
fn test_random_keypair() {
let (key, pub_key) = Curve::random_keypair();
// Check the derived key matches the one generated from original key
assert_eq!(Curve::derive_public_key(&key), pub_key);
}
#[test]
fn test_hash_to_fr() {
// Test that hash_to_fr(input_1) != hash_to_fr(input_2) when input_1 != input_2
let input_1 = b"input_1";
let input_2 = b"input_2";
assert_ne!(Curve::hash_to_fr(input_1), Curve::hash_to_fr(input_2));
}
#[test]
fn test_compute_shared_point() {
// In a multiple participant scenario, any participant's public key
// combined with any other participant's private key should arrive at the same shared key
let (key1, pub_key1) = Curve::random_keypair();
let (key2, pub_key2) = Curve::random_keypair();
let shared1 = Curve::compute_shared_point(key1, pub_key2);
let shared2 = Curve::compute_shared_point(key2, pub_key1);
// Convert Projective to Affine for equality comparison
let shared1_affine = shared1.into_affine();
let shared2_affine = shared2.into_affine();
assert_eq!(shared1_affine.x, shared2_affine.x);
assert_eq!(shared1_affine.y, shared2_affine.y);
}
#[test]
fn test_stealth_commitment_generation() {
let (spending_key, spending_public_key) = Curve::random_keypair();
let (viewing_key, viewing_public_key) = Curve::random_keypair();
// generate ephemeral keypair
let (ephemeral_private_key, ephemeral_public_key) = Curve::random_keypair();
let (stealth_commitment, view_tag) = Curve::generate_stealth_commitment(
viewing_public_key,
spending_public_key,
ephemeral_private_key,
);
let stealth_private_key_opt = Curve::generate_stealth_private_key(
ephemeral_public_key,
viewing_key,
spending_key,
view_tag,
);
if stealth_private_key_opt.is_none() {
panic!("View tags did not match");
}
let derived_commitment = Curve::derive_public_key(&stealth_private_key_opt.unwrap());
assert_eq!(derived_commitment, stealth_commitment);
}
}

161
src/bn254_impl.rs Normal file
View File

@@ -0,0 +1,161 @@
use crate::stealth_commitments::{AffineWrapper, RawFr, StealthAddressOnCurve};
use ark_bn254::g1::{G1_GENERATOR_X, G1_GENERATOR_Y};
use ark_bn254::{Fq, Fr, G1Affine, G1Projective};
use rln::hashers::{hash_to_field, poseidon_hash};
impl AffineWrapper for G1Affine {
type Fq = Fq;
fn new(x: Self::Fq, y: Self::Fq) -> Self {
G1Affine::new(x, y)
}
}
impl RawFr for Fr {
type Fr = Fr;
fn as_u64(&self) -> u64 {
self.0 .0[0]
}
}
impl StealthAddressOnCurve for ark_bn254::Bn254 {
type Projective = G1Projective;
type Affine = G1Affine;
type Fr = Fr;
fn derive_public_key(private_key: &Self::Fr) -> Self::Projective {
let g1_generator_affine = Self::Affine::new(G1_GENERATOR_X, G1_GENERATOR_Y);
(Self::Projective::from(g1_generator_affine)) * *private_key
}
fn hash_to_fr(input: &[u8]) -> Self::Fr {
poseidon_hash(&[hash_to_field(input)])
}
}
#[cfg(test)]
mod tests {
use super::*;
use ark_ec::CurveGroup;
use ark_std::rand::thread_rng;
use ark_std::UniformRand;
use color_eyre::{Report, Result};
use rln::public::RLN;
use rln::utils::fr_to_bytes_le;
use serde_json::json;
use std::io::Cursor;
type Curve = ark_bn254::Bn254;
#[test]
fn test_random_keypair() {
let (key, pub_key) = Curve::random_keypair();
// Check the derived key matches the one generated from original key
assert_eq!(Curve::derive_public_key(&key), pub_key);
}
#[test]
fn test_hash_to_fr() {
// Test that hash_to_fr(input_1) != hash_to_fr(input_2) when input_1 != input_2
let input_1 = b"input_1";
let input_2 = b"input_2";
assert_ne!(Curve::hash_to_fr(input_1), Curve::hash_to_fr(input_2));
}
#[test]
fn test_compute_shared_point() {
// In a multiple participant scenario, any participant's public key
// combined with any other participant's private key should arrive at the same shared key
let (key1, pub_key1) = Curve::random_keypair();
let (key2, pub_key2) = Curve::random_keypair();
let shared1 = Curve::compute_shared_point(key1, pub_key2);
let shared2 = Curve::compute_shared_point(key2, pub_key1);
// Convert Projective to Affine for equality comparison
let shared1_affine = shared1.into_affine();
let shared2_affine = shared2.into_affine();
assert_eq!(shared1_affine.x, shared2_affine.x);
assert_eq!(shared1_affine.y, shared2_affine.y);
}
#[test]
fn test_stealth_commitment_generation() {
let (spending_key, spending_public_key) = Curve::random_keypair();
let (viewing_key, viewing_public_key) = Curve::random_keypair();
// generate ephemeral keypair
let (ephemeral_private_key, ephemeral_public_key) = Curve::random_keypair();
let (stealth_commitment, view_tag) = Curve::generate_stealth_commitment(
viewing_public_key,
spending_public_key,
ephemeral_private_key,
);
let stealth_private_key_opt = Curve::generate_stealth_private_key(
ephemeral_public_key,
viewing_key,
spending_key,
view_tag,
);
if stealth_private_key_opt.is_none() {
panic!("View tags did not match");
}
let derived_commitment = Curve::derive_public_key(&stealth_private_key_opt.unwrap());
assert_eq!(derived_commitment, stealth_commitment);
}
// this can only be tested for bn254 since that is the curve supported by RLN
#[test]
fn apply_stealth_membership_from_one_tree_to_another() -> Result<()> {
let test_tree_height = 20;
let resources = Cursor::new(json!({"resources_folder": "tree_height_20"}).to_string());
let mut rln = RLN::new(test_tree_height, resources.clone())?;
let alice_leaf = Fr::rand(&mut thread_rng());
let (alice_known_spending_sk, alice_known_spending_pk) = Curve::random_keypair();
let alice_leaf_buffer = Cursor::new(fr_to_bytes_le(&alice_leaf));
rln.set_leaf(0, alice_leaf_buffer)?;
// now the application sees that a user has been inserted into the tree
let mut rln_app_tree = RLN::new(test_tree_height, resources)?;
// the application generates a stealth commitment for alice
let (ephemeral_private_key, ephemeral_public_key) = Curve::random_keypair();
let (alice_stealth_commitment, view_tag) = Curve::generate_stealth_commitment(
alice_known_spending_pk,
alice_known_spending_pk,
ephemeral_private_key,
);
let parts = [alice_stealth_commitment.x, alice_stealth_commitment.y];
let fr_parts = parts.map(|x| Fr::from(x.0));
let alice_stealth_commitment_buffer =
Cursor::new(fr_to_bytes_le(&poseidon_hash(&fr_parts)));
rln_app_tree.set_leaf(0, alice_stealth_commitment_buffer)?;
// now alice's stealth commitment has been inserted into the tree, but alice has not
// yet derived the secret for it -
let alice_stealth_private_key_opt = Curve::generate_stealth_private_key(
ephemeral_public_key,
alice_known_spending_sk,
alice_known_spending_sk,
view_tag,
);
if alice_stealth_private_key_opt.is_none() {
return Err(Report::msg("Invalid view tag"));
}
let alice_stealth_private_key = alice_stealth_private_key_opt.unwrap();
assert_eq!(
Curve::derive_public_key(&alice_stealth_private_key),
alice_stealth_commitment
);
// now alice may generate valid rln proofs for the rln app tree, using a commitment
// derived from her commitment on the other tree
Ok(())
}
}

View File

@@ -1,3 +1,7 @@
use crate::ffi::CErrorCode::{
NoError, SerializationErrorInvalidData, SerializationErrorIoError,
SerializationErrorNotEnoughSpace, SerializationErrorUnexpectedFlags,
};
use crate::stealth_commitments::{
derive_public_key, generate_random_fr, generate_stealth_commitment,
generate_stealth_private_key, random_keypair,
@@ -6,10 +10,6 @@ use ark_bn254::{Fr, G1Projective};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError};
use num_traits::Zero;
use std::ops::Add;
use crate::ffi::CErrorCode::{
NoError, SerializationErrorInvalidData, SerializationErrorIoError,
SerializationErrorNotEnoughSpace, SerializationErrorUnexpectedFlags,
};
// we import this to prevent using multiple static libs
#[cfg(feature = "include_rln_ffi")]
#[allow(unused_imports)]

View File

@@ -1,4 +1,7 @@
mod stealth_commitments;
mod bls12_381_impl;
mod bn254_impl;
#[cfg(feature = "ffi")]
mod ffi;
mod bls12_377_impl;

View File

@@ -1,184 +1,76 @@
use ark_bn254::g1::{G1_GENERATOR_X, G1_GENERATOR_Y};
use ark_bn254::{Fr, G1Affine, G1Projective};
use ark_ff::UniformRand;
use ark_std::rand::rngs::OsRng;
use rln::hashers::{hash_to_field, poseidon_hash};
use ark_std::UniformRand;
use std::fmt::Display;
use std::ops::{Add, Mul};
pub fn derive_public_key(private_key: Fr) -> G1Projective {
let g1_generator_affine = G1Affine::new(G1_GENERATOR_X, G1_GENERATOR_Y);
(G1Projective::from(g1_generator_affine)) * private_key
pub trait AffineWrapper {
type Fq: ark_ff::PrimeField;
fn new(x: Self::Fq, y: Self::Fq) -> Self;
}
pub fn random_keypair() -> (Fr, G1Projective) {
let private_key = generate_random_fr();
let public_key = derive_public_key(private_key);
(private_key, public_key)
pub trait RawFr {
type Fr;
fn as_u64(&self) -> u64;
}
pub fn generate_random_fr() -> Fr {
let mut rng = OsRng;
Fr::rand(&mut rng)
}
pub trait StealthAddressOnCurve {
type Projective: Display
+ Add<Output = Self::Projective>
+ Mul<Self::Fr, Output = Self::Projective>;
type Affine: AffineWrapper;
type Fr: Add<Self::Fr, Output = Self::Fr> + ark_ff::PrimeField + RawFr;
pub fn hash_to_fr(input: &[u8]) -> Fr {
poseidon_hash(&[hash_to_field(input)])
}
fn derive_public_key(private_key: &Self::Fr) -> Self::Projective;
pub fn compute_shared_point(private_key: Fr, other_public_key: G1Projective) -> G1Projective {
other_public_key * private_key
}
pub fn generate_stealth_commitment(
viewing_public_key: G1Projective,
spending_public_key: G1Projective,
ephemeral_private_key: Fr,
) -> (G1Projective, u64) {
let q = compute_shared_point(ephemeral_private_key, viewing_public_key);
let inputs = q.to_string();
let q_hashed = hash_to_fr(inputs.as_bytes());
let q_hashed_in_g1 = derive_public_key(q_hashed);
let view_tag = q_hashed.0 .0[0];
(q_hashed_in_g1 + spending_public_key, view_tag)
}
pub fn generate_stealth_private_key(
ephemeral_public_key: G1Projective,
viewing_key: Fr,
spending_key: Fr,
expected_view_tag: u64,
) -> Option<Fr> {
let q_receiver = compute_shared_point(viewing_key, ephemeral_public_key);
let inputs_receiver = q_receiver.to_string();
let q_receiver_hashed = hash_to_fr(inputs_receiver.as_bytes());
// Check if retrieved view tag matches the expected view tag
let view_tag = q_receiver_hashed.0 .0[0];
if view_tag == expected_view_tag {
let stealth_private_key = spending_key + q_receiver_hashed;
Some(stealth_private_key)
} else {
None
fn random_keypair() -> (Self::Fr, Self::Projective) {
let private_key = Self::generate_random_fr();
let public_key = Self::derive_public_key(&private_key);
(private_key, public_key)
}
}
#[cfg(test)]
mod tests {
use super::*;
use ark_ec::CurveGroup;
use ark_std::rand::thread_rng;
use color_eyre::{Report, Result};
use rln::public::RLN;
use rln::utils::fr_to_bytes_le;
use serde_json::json;
use std::io::Cursor;
#[test]
fn test_random_keypair() {
let (key, pub_key) = random_keypair();
// Check the derived key matches the one generated from original key
assert_eq!(derive_public_key(key), pub_key);
fn generate_random_fr() -> Self::Fr {
let mut rng = OsRng;
Self::Fr::rand(&mut rng)
}
fn hash_to_fr(input: &[u8]) -> Self::Fr;
fn compute_shared_point(
private_key: Self::Fr,
public_key: Self::Projective,
) -> Self::Projective {
public_key * private_key
}
#[test]
fn test_hash_to_fr() {
// Test that hash_to_fr(input_1) != hash_to_fr(input_2) when input_1 != input_2
let input_1 = b"input_1";
let input_2 = b"input_2";
assert_ne!(hash_to_fr(input_1), hash_to_fr(input_2));
fn generate_stealth_commitment(
viewing_public_key: Self::Projective,
spending_public_key: Self::Projective,
ephemeral_private_key: Self::Fr,
) -> (Self::Projective, u64) {
let q = Self::compute_shared_point(ephemeral_private_key, viewing_public_key);
let inputs = q.to_string();
let q_hashed = Self::hash_to_fr(inputs.as_bytes());
let q_hashed_in_g1 = Self::derive_public_key(&q_hashed);
let view_tag = q_hashed.as_u64();
(q_hashed_in_g1 + spending_public_key, view_tag)
}
#[test]
fn test_compute_shared_point() {
// In a multiple participant scenario, any participant's public key
// combined with any other participant's private key should arrive at the same shared key
let (key1, pub_key1) = random_keypair();
let (key2, pub_key2) = random_keypair();
fn generate_stealth_private_key(
ephemeral_public_key: Self::Projective,
viewing_key: Self::Fr,
spending_key: Self::Fr,
expected_view_tag: u64,
) -> Option<Self::Fr> {
let q_receiver = Self::compute_shared_point(viewing_key, ephemeral_public_key);
let shared1 = compute_shared_point(key1, pub_key2);
let shared2 = compute_shared_point(key2, pub_key1);
let inputs_receiver = q_receiver.to_string();
let q_receiver_hashed = Self::hash_to_fr(inputs_receiver.as_bytes());
// Convert Projective to Affine for equality comparison
let shared1_affine = shared1.into_affine();
let shared2_affine = shared2.into_affine();
assert_eq!(shared1_affine.x, shared2_affine.x);
assert_eq!(shared1_affine.y, shared2_affine.y);
}
#[test]
fn test_stealth_commitment_generation() {
let (spending_key, spending_public_key) = random_keypair();
let (viewing_key, viewing_public_key) = random_keypair();
// generate ephemeral keypair
let (ephemeral_private_key, ephemeral_public_key) = random_keypair();
let (stealth_commitment, view_tag) = generate_stealth_commitment(
viewing_public_key,
spending_public_key,
ephemeral_private_key,
);
let stealth_private_key_opt =
generate_stealth_private_key(ephemeral_public_key, viewing_key, spending_key, view_tag);
if stealth_private_key_opt.is_none() {
panic!("View tags did not match");
// Check if retrieved view tag matches the expected view tag
let view_tag: u64 = q_receiver_hashed.as_u64();
if view_tag == expected_view_tag {
let stealth_private_key = spending_key + q_receiver_hashed;
Some(stealth_private_key)
} else {
None
}
let derived_commitment = derive_public_key(stealth_private_key_opt.unwrap());
assert_eq!(derived_commitment, stealth_commitment);
}
#[test]
fn apply_stealth_membership_from_one_tree_to_another() -> Result<()> {
let test_tree_height = 20;
let resources = Cursor::new(json!({"resources_folder": "tree_height_20"}).to_string());
let mut rln = RLN::new(test_tree_height, resources.clone())?;
let alice_leaf = Fr::rand(&mut thread_rng());
let (alice_known_spending_sk, alice_known_spending_pk) = random_keypair();
let alice_leaf_buffer = Cursor::new(fr_to_bytes_le(&alice_leaf));
rln.set_leaf(0, alice_leaf_buffer)?;
// now the application sees that a user has been inserted into the tree
let mut rln_app_tree = RLN::new(test_tree_height, resources)?;
// the application generates a stealth commitment for alice
let (ephemeral_private_key, ephemeral_public_key) = random_keypair();
let (alice_stealth_commitment, view_tag) = generate_stealth_commitment(
alice_known_spending_pk,
alice_known_spending_pk,
ephemeral_private_key,
);
let parts = [alice_stealth_commitment.x, alice_stealth_commitment.y];
let fr_parts = parts.map(|x| Fr::from(x.0));
let alice_stealth_commitment_buffer =
Cursor::new(fr_to_bytes_le(&poseidon_hash(&fr_parts)));
rln_app_tree.set_leaf(0, alice_stealth_commitment_buffer)?;
// now alice's stealth commitment has been inserted into the tree, but alice has not
// yet derived the secret for it -
let alice_stealth_private_key_opt = generate_stealth_private_key(
ephemeral_public_key,
alice_known_spending_sk,
alice_known_spending_sk,
view_tag,
);
if alice_stealth_private_key_opt.is_none() {
return Err(Report::msg("Invalid view tag"));
}
let alice_stealth_private_key = alice_stealth_private_key_opt.unwrap();
assert_eq!(
derive_public_key(alice_stealth_private_key),
alice_stealth_commitment
);
// now alice may generate valid rln proofs for the rln app tree, using a commitment
// derived from her commitment on the other tree
Ok(())
}
}