Skip to content

Commit

Permalink
zkp anonymous account
Browse files Browse the repository at this point in the history
  • Loading branch information
amiyatulu committed Nov 20, 2024
1 parent ade97e7 commit ae203d7
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 39 deletions.
28 changes: 28 additions & 0 deletions custom-pallets/zkp-anonymous-account/docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# anonymous-account-crates


**Zero Knowledge Proof for Anonymous Voting**

In a democracy, voting must remain private to ensure the integrity of the process. However, maintaining privacy in blockchain systems is challenging because transactions are recorded on a public ledger, making it difficult to keep accounts anonymous.

**How can zero-knowledge proofs enable anonymous voting?**

**Anonymous Voting Account Creation**

1. Start with 100-200 accounts that have undergone KYC (Know Your Customer) verification.
2. Generate a cryptographic hash for these accounts and store it on the blockchain.
3. Create a signature from your crypto account using a password. Keep the password secret and publish the signature on the blockchain. Publishing the signature does not allow the user to change the password.

**In Risczero:**

1. Take the hash of the accounts and signatures as input.
2. Retrieve the full list of accounts and signatures.
3. Use the hash and cryptographically sign only one account, which will be used to create an anonymous voting account.
4. Push the following to the journal:
- The newly created anonymous account.
- The input hash.
- A total hash counter (e.g., a hash of 0000010000000 combined with the password).

To ensure each anonymous account is unique, the hash counter must also be unique, allowing only one anonymous account per verified identity.

Finally, add the zero-knowledge proof to the blockchain, ensuring that the anonymous voting process is secure and private without revealing the identity of the voter.
1 change: 1 addition & 0 deletions custom-pallets/zkp-anonymous-account/host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ subxt-signer = "0.37.0"
sp-io = "38.0.0"
sp-core = "34.0.0"
subxt-core = "0.37.0"
serde-big-array = "0.5"
45 changes: 30 additions & 15 deletions custom-pallets/zkp-anonymous-account/host/src/accounts_hash.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
use sp_io::hashing::blake2_256;
use subxt_core::utils::AccountId32;
use subxt_signer::{bip39::Mnemonic, sr25519::Keypair};

#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Clone)]
pub struct ByteArray64(#[serde(with = "BigArray")] pub [u8; 64]);

#[derive(Serialize, Deserialize, Clone)]
pub struct AccountData {
pub account_addresses: Vec<AccountId32>,
pub account_addresses: Vec<(AccountId32, ByteArray64)>,
pub current_hash: [u8; 32],
pub index: usize,
pub public_key_of_account: [u8; 32],
pub signature: Vec<u8>, // Use Vec<u8> instead of [u8; 64]
pub signature: ByteArray64, // Use Vec<u8> instead of [u8; 64]
pub password: String,
}

fn update_hash_incrementally(current_hash: [u8; 32], account_id: &AccountId32) -> [u8; 32] {
pub fn calculate_hash_for_accounts(accounts: &[(AccountId32, ByteArray64)]) -> [u8; 32] {
let mut input_data = Vec::new();

// Extend input data with the current hash and the new account ID
input_data.extend_from_slice(&current_hash);
input_data.extend_from_slice(account_id.as_ref());
// Concatenate all account IDs and ByteArray64 contents into a single byte vector
for account in accounts {
input_data.extend_from_slice(account.0.as_ref()); // AccountId32 as bytes
input_data.extend_from_slice(&(account.1).0); // ByteArray64's inner array
}

// Recalculate the hash with the new account ID
// Compute the hash of the combined data
blake2_256(&input_data)
}

Expand All @@ -36,17 +42,26 @@ pub fn keypair_func() -> AccountData {
phrases.push("resemble timber picnic stage must video amount price sport help good stable");
let phrases_clone = phrases.clone();
let mut account_addresses = Vec::new();
let mut current_hash: [u8; 32] = [0; 32];
for phrase in phrases {
let mnemonic = Mnemonic::parse(phrase).unwrap();
let keypair = Keypair::from_phrase(&mnemonic, None).unwrap();
let account_address = keypair.public_key().to_account_id();
println!("{:?}", account_address);
account_addresses.push(account_address.clone());
current_hash = update_hash_incrementally(current_hash, &account_address);

let password = "password-signature".to_owned();

let signature = keypair.sign(password.as_bytes());
account_addresses.push((account_address.clone(), ByteArray64(signature.0)));
}

let mut account_addresses_new = Vec::new();
let copies = 250;

for _ in 0..copies {
account_addresses_new.extend(account_addresses.clone());
}

println!("current_hash:{:?}", current_hash);
let current_hash = calculate_hash_for_accounts(&account_addresses_new.clone());

let index = 2;

Expand All @@ -61,14 +76,14 @@ pub fn keypair_func() -> AccountData {
let signature = keypair.sign(password.as_bytes());

// Make the signature public in blockchain, so that person can't enter another password
let signature_array = signature.0.to_vec();
let signature_array = signature.0;

AccountData {
account_addresses: account_addresses,
account_addresses: account_addresses_new,
current_hash: current_hash,
index: index,
public_key_of_account: public_key_of_account,
signature: signature_array,
signature: ByteArray64(signature_array),
password: password,
}
}
19 changes: 19 additions & 0 deletions custom-pallets/zkp-anonymous-account/host/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@
use host::accounts_hash::keypair_func;
use methods::{GUEST_ANONYMOUS_ACCOUNT_ELF, GUEST_ANONYMOUS_ACCOUNT_ID};
use risc0_zkvm::{default_prover, ExecutorEnv};
use std::time::{Instant, SystemTime};

fn main() {
// Initialize tracing. In order to view logs, run `RUST_LOG=info cargo run`
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::filter::EnvFilter::from_default_env())
.init();

let start_time = SystemTime::now();
println!("Start Time: {:?}", start_time);

let start = Instant::now();

// An executor environment describes the configurations for the zkVM
// including program inputs.
// An default ExecutorEnv can be created like so:
Expand All @@ -25,6 +31,8 @@ fn main() {
// For example:
let account_data = keypair_func();

println!("image id: {:?}", GUEST_ANONYMOUS_ACCOUNT_ID);

let env = ExecutorEnv::builder()
.write(&account_data)
.unwrap()
Expand Down Expand Up @@ -53,4 +61,15 @@ fn main() {
// The receipt was verified at the end of proving, but the below code is an
// example of how someone else could verify this receipt.
receipt.verify(GUEST_ANONYMOUS_ACCOUNT_ID).unwrap();

println!("image id: {:?}", GUEST_ANONYMOUS_ACCOUNT_ID);

let duration = start.elapsed();

// Get the end time (system time)
let end_time = SystemTime::now();
println!("End Time: {:?}", end_time);

// Print the total elapsed time
println!("Total Execution Time: {:?}", duration);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ sp-io = "38.0.0"
sp-core = "34.0.0"
subxt-core = "0.37.0"
serde = "1.0"
serde-big-array = "0.5"
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
use sp_io::hashing::blake2_256;
use subxt_core::utils::AccountId32;

#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Clone)]
pub struct ByteArray64(#[serde(with = "BigArray")] pub [u8; 64]);

#[derive(Serialize, Deserialize, Clone)]
pub struct AccountData {
pub account_addresses: Vec<AccountId32>,
pub account_addresses: Vec<(AccountId32, ByteArray64)>,
pub current_hash: [u8; 32],
pub index: usize,
pub public_key_of_account: [u8; 32],
pub signature: Vec<u8>, // Use Vec<u8> instead of [u8; 64]
pub signature: ByteArray64, // Use Vec<u8> instead of [u8; 64]
pub password: String,
}

pub fn update_hash_incrementally(current_hash: [u8; 32], account_id: &AccountId32) -> [u8; 32] {
pub fn calculate_hash_for_accounts(accounts: &[(AccountId32, ByteArray64)]) -> [u8; 32] {
let mut input_data = Vec::new();

// Extend input data with the current hash and the new account ID
input_data.extend_from_slice(&current_hash);
input_data.extend_from_slice(account_id.as_ref());
// Concatenate all account IDs and ByteArray64 contents into a single byte vector
for account in accounts {
input_data.extend_from_slice(account.0.as_ref()); // AccountId32 as bytes
input_data.extend_from_slice(&(account.1).0); // ByteArray64's inner array
}

// Recalculate the hash with the new account ID
// Compute the hash of the combined data
blake2_256(&input_data)
}

pub fn password_hash_fn(index: usize, password: String) -> [u8; 32] {
// Convert the index to bytes (using 8 bytes to represent usize)
let mut index_bytes = index.to_le_bytes().to_vec();
Expand Down
23 changes: 9 additions & 14 deletions custom-pallets/zkp-anonymous-account/methods/guest/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use guest_anonymous_account::accounts_hash::{
password_hash_fn, update_hash_incrementally, AccountData,
calculate_hash_for_accounts, password_hash_fn, AccountData,
};
use risc0_zkvm::guest::env;
use subxt_signer::sr25519;
Expand All @@ -10,23 +10,16 @@ fn main() {

let account_addresses = account_data.account_addresses.clone();

let mut current_hash: [u8; 32] = [0; 32];
for account_address in &account_addresses {
current_hash = update_hash_incrementally(current_hash, &account_address);
}

assert_eq!(current_hash, account_data.current_hash);
// assert_ne!(current_hash, hash);
let hash = calculate_hash_for_accounts(&account_addresses);
let current_hash = account_data.current_hash;
assert_eq!(current_hash, hash);

let public_key_of_account = PublicKey(account_data.public_key_of_account);

let public_key_of_account2 = PublicKey(account_data.public_key_of_account);

// Ensure the Vec<u8> has exactly 64 bytes and convert it to [u8; 64]
let signature_array: [u8; 64] = account_data
.signature
.try_into()
.expect("Invalid length: Vec<u8> must be 64 bytes to convert to Signature");
let signature_array: [u8; 64] = account_data.signature.0;

// Create a Signature from the [u8; 64] array
let signature_of_account = Signature(signature_array);
Expand All @@ -37,7 +30,7 @@ fn main() {
// // let keypair = Keypair::from_phrase(&mnemonic, None).unwrap();
// // let account_address_of_your_account = keypair.public_key().to_account_id();

assert_eq!(account_addresses[account_data.index], your_account_id);
assert_eq!(account_addresses[account_data.index].0, your_account_id);
let password = account_data.password.as_bytes();
let password_string = account_data.password.clone();
assert!(sr25519::verify(
Expand All @@ -49,5 +42,7 @@ fn main() {
let password_hash = password_hash_fn(account_data.index, password_string);

// write public output to the journal
env::commit(&(current_hash, password_hash));
// env::commit(&(current_hash, password_hash));

env::commit(&(hash, password_hash));
}
2 changes: 1 addition & 1 deletion runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ parameter_types! {
pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength
::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);
pub const SS58Prefix: u8 = 42;
pub const AnonymousAccountImageId: [u32; 8] = [1512492500, 2753161227, 4049970770, 2674496521, 3333553514, 2059402670, 1701049823, 2725882521];
pub const AnonymousAccountImageId: [u32; 8] = [957845215, 2138764848, 3436027531, 926522363, 4221982768, 3434214646, 699310563, 1063924749];
}

/// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from
Expand Down

0 comments on commit ae203d7

Please sign in to comment.