Skip to content

Commit

Permalink
fixes #7
Browse files Browse the repository at this point in the history
  • Loading branch information
Holindauer committed Apr 21, 2024
1 parent 3494e7a commit 66c96b2
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 14 deletions.
8 changes: 4 additions & 4 deletions src/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ use crate::validation::ValidatorNode;
},
Transaction {
sender_public_key: String,
sender_obfuscated_private_key_part1: String,
sender_obfuscated_private_key_part2: String,
encoded_key_curve_point_1: String,
encoded_key_curve_point_2: String,
recipient_public_key: String,
amount: String,
},
Expand Down Expand Up @@ -117,8 +117,8 @@ pub async fn send_transaction_request(sender_private_key: String, recipient_publ
// Package the message
let request = NetworkRequest::Transaction {
sender_public_key,
sender_obfuscated_private_key_part1: encoded_key_point_1,
sender_obfuscated_private_key_part2: encoded_key_point_2,
encoded_key_curve_point_1: encoded_key_point_1,
encoded_key_curve_point_2: encoded_key_point_2,
recipient_public_key,
amount,
};
Expand Down
18 changes: 14 additions & 4 deletions src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ use crate::adopt_network_state::PeerLedgerResponse;
* @param client_port_address: String - The port address that the client is listening on for incoming connections.
* This is used to establish a connection with the client from the network.
*
* @param used_zk_proofs: Arc<Mutex<Vec<u8>, String>> - A hashmap that stores the zk-proofs that hahses of the
* zk-proofs have been used by a requester to verify transactions. The key is the address of the client and the
* value is a hash of the zk-proof. This datastructure is used to prevent replay attacks with zk-proofs. This
* is because the same curve points will always add to the third curve point (the obscured private key) so
* allowing the resuse of the same zk-proof would enable a listener to replay the same transaction multiple times.
*
* @param active_peers: Arc<Mutex<Vec<(String, u64)>>> - A vector of (String, u64) tuples containing the addresses
* of all active peers (as represented by their port address) on the network and the timestamp of the last recieved
* heartbeat from this peer. This datastructure is maintained by the validator node via a blocked on heartbeat
Expand Down Expand Up @@ -87,6 +93,7 @@ pub struct ValidatorNode {
pub peer_decisions: Arc<Mutex<HashMap<Vec<u8>, (u32, u32)>>>,
pub client_decisions: Arc<Mutex<HashMap<Vec<u8>, bool>>>,
pub client_port_address: String,
pub used_zk_proofs: Arc<Mutex<HashMap<Vec<u8>, Vec<String>>>>, // address -> vec of hashes of zk-proofs
pub active_peers: Arc<Mutex<Vec<(String, u64)>>>,
pub total_peers: Arc<Mutex<usize>>,
pub peer_ledger_states: Arc<Mutex<Vec<PeerLedgerResponse>>>,
Expand All @@ -102,6 +109,7 @@ impl ValidatorNode { // initializes datastructures
peer_decisions: Arc::new(Mutex::new(HashMap::new())),
client_decisions: Arc::new(Mutex::new(HashMap::new())),
client_port_address: String::new(),
used_zk_proofs: Arc::new(Mutex::new(HashMap::new())),
active_peers: Arc::new(Mutex::new(Vec::new())),
total_peers: Arc::new(Mutex::new(0)), // Init to zero, will be set when peers are know
peer_ledger_states: Arc::new(Mutex::new(Vec::new())),
Expand Down Expand Up @@ -385,10 +393,12 @@ async fn verify_transaction_independently(request: Value, validator_node: Valida
}
// Reject decision if the zk-proof fails
else if zk_proof::verify_points_sum_hash(
&request["sender_obfuscated_private_key_part1"].as_str().unwrap_or_default().to_string(),
&request["sender_obfuscated_private_key_part2"].as_str().unwrap_or_default().to_string(),
merkle_tree_guard.get_private_key_hash(sender_address.clone()).unwrap()
) != true {
&request["encoded_key_curve_point_1"].as_str().unwrap_or_default().to_string(),
&request["encoded_key_curve_point_2"].as_str().unwrap_or_default().to_string(),
merkle_tree_guard.get_private_key_hash(sender_address.clone()).unwrap(),
sender_address.clone(),
validator_node.clone()
).await != true {
decision = false;
}
// Reject decision if the sender does not have enough balance to send the transaction
Expand Down
69 changes: 63 additions & 6 deletions src/zk_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ use secp256k1::{SecretKey, PublicKey, Secp256k1};
use sha2::{Digest, Sha256};
use base64::decode;
use std::convert::TryInto;
use std::collections::HashMap;
use std::io;
use rand::rngs::OsRng; // cryptographically secure RNG
use rand::{thread_rng, RngCore}; // Ensure thread_rng is imported here
use rand::{thread_rng, RngCore};
use tokio::sync::Mutex;
use std::sync::Arc;

use crate::validation::ValidatorNode; // Ensure thread_rng is imported here

/**
* @notice zk_proof.rs contains the logic for generating a simple zero-knowledge proof for verification of knowledge of
Expand Down Expand Up @@ -123,9 +128,29 @@ fn decompress_curve_points( encoded_key_curve_point_1: &str, encoded_key_curve_p
* compresses the sum, hashes it, and compares the hash to the expected hash. If they match, the function returns true.
* If they do not match, the function returns false.
*/
pub fn verify_points_sum_hash(encoded_key_curve_point_1: &str, encoded_key_curve_point_2: &str, expected_hash: Vec<u8>) -> bool {
pub async fn verify_points_sum_hash(
encoded_key_curve_point_1: &str,
encoded_key_curve_point_2: &str,
expected_hash: Vec<u8>,
sender_address: Vec<u8>,
validator_node: ValidatorNode
) -> bool {
println!("zk_proof::verify_points_sum_hash() : Verifying hash of curve point sum adds to private key obfuscated curve point hash...");

// Retrieve the zk_proofs that have already been used
let used_zk_proofs: Arc<Mutex<HashMap<Vec<u8>, Vec<String>>>> = validator_node.used_zk_proofs.clone();
let mut used_zk_proofs_guard = used_zk_proofs.lock().await;

// hash the proof as it was recieved
let hash_of_proof: String = hash_zk_proof(encoded_key_curve_point_1, encoded_key_curve_point_2);

// Reject the proof if it has already been used
if let Some(used_proofs) = used_zk_proofs_guard.get(&sender_address) {
if used_proofs.contains(&hash_of_proof) {
return false;
}
}

// convert encoded points (str) to Ristretto points
let (point1, point2) = decompress_curve_points(
encoded_key_curve_point_1,
Expand All @@ -139,8 +164,42 @@ pub fn verify_points_sum_hash(encoded_key_curve_point_1: &str, encoded_key_curve
let sum_point_bytes: [u8; 32] = sum_point.compress().to_bytes();
let hash_of_sum = Sha256::digest(&sum_point_bytes);

// Compare the generated hash with the expected hash
hash_of_sum.as_slice() == expected_hash.as_slice()
// The generated hash with the expected hash
if hash_of_sum.as_slice() == expected_hash.as_slice() {

// add the hash of the proof to the used zk_proofs hash map
if let Some(proofs) = used_zk_proofs_guard.get_mut(&sender_address) {
proofs.push(hash_of_proof);
} else {
used_zk_proofs_guard.insert(sender_address, vec![hash_of_proof]);
}

// indicate valid knowledge of private key
return true;
}

// indicate invalid knowledge of private key
false
}

/**
* @notice this function hashes the encoded string representation of the two curve points that make
* up the zk proof provided by the requester. The points are hashed using sha256 and returned.
*/
fn hash_zk_proof(encoded_key_curve_point_1: &str, encoded_key_curve_point_2: &str) -> String {

// Decode the Base64 encoded points
let point1_bytes: Vec<u8> = decode(encoded_key_curve_point_1).unwrap();
let point2_bytes: Vec<u8> = decode(encoded_key_curve_point_2).unwrap();

// Hash the two points
let mut hasher = Sha256::new();
hasher.update(point1_bytes);
hasher.update(point2_bytes);
let hash = hasher.finalize();

// Return the hash as a hex string
hex::encode(hash)
}


Expand All @@ -149,7 +208,6 @@ pub fn verify_points_sum_hash(encoded_key_curve_point_1: &str, encoded_key_curve
* @return a tuple of the secret and public key generated for the new account.
*/
pub fn generate_keypair() -> Result<(SecretKey, PublicKey), io::Error> {
println!("zk_proof::generate_keypair() : Generating new publlic and private keypair...");

// Create a new secp256k1 context
let secp = Secp256k1::new();
Expand Down Expand Up @@ -177,7 +235,6 @@ pub fn generate_keypair() -> Result<(SecretKey, PublicKey), io::Error> {
* derived from the private key as a hex encoded string.
*/
pub fn derive_public_key_from_private_key( private_key: &String ) -> String {
println!("zk_proof::derive_public_key_from_private_key()...");

// Create a new secp256k1 context
let secp = Secp256k1::new();
Expand Down

0 comments on commit 66c96b2

Please sign in to comment.