Skip to content

Commit

Permalink
Add utils feature (cygnet3#64)
Browse files Browse the repository at this point in the history
* Add utils feature

* Reexport secp256k1

* Add rand feature to secp256k1

* Remove comment

---------

Co-authored-by: cygnet <[email protected]>
  • Loading branch information
Sosthene00 and cygnet3 authored Jan 3, 2024
1 parent 3eeef5c commit 8a843a3
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 113 deletions.
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ name = "silentpayments"
crate-type = ["lib"]

[features]
default = ["sending", "receiving"]
default = ["sending", "receiving", "utils"]
sending = []
receiving = []
utils = []

[dependencies]
secp256k1 = {version = "0.24", features = ["bitcoin-hashes-std"] }
secp256k1 = {version = "0.24", features = ["bitcoin-hashes-std", "rand"] }
hex = "0.4"
bech32 = "0.9"
bimap = "0.6"
Expand Down
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ mod error;
pub mod receiving;
#[cfg(feature = "sending")]
pub mod sending;
#[cfg(feature = "utils")]
pub mod utils;

pub use secp256k1;

pub use crate::error::Error;

Expand Down
2 changes: 1 addition & 1 deletion src/sending.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ pub fn generate_multiple_recipient_pubkeys(
/// # Arguments
///
/// * `recipient` - A `String` of the bech32m-encoded silent payment address to be paid.
/// * `partial_secret` - A `SecretKey` representing the private keys of the outputs to spend multiplied by the hash of its outpoints.
/// * `partial_secret` - A `SecretKey` representing the private keys of the outputs to spend multiplied by the hash of its outpoints.
///
/// # Returns
///
Expand Down
37 changes: 37 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use std::io::Write;

use crate::Result;
use secp256k1::{
hashes::{sha256, Hash},
Scalar,
};

pub mod receiving;
pub mod sending;

pub fn hash_outpoints(sending_data: &Vec<(String, u32)>) -> Result<Scalar> {
let mut outpoints: Vec<Vec<u8>> = vec![];

for outpoint in sending_data {
let mut bytes: Vec<u8> = hex::decode(outpoint.0.as_str())?;

// txid in string format is big endian and we need little endian
bytes.reverse();

bytes.extend_from_slice(&outpoint.1.to_le_bytes());
outpoints.push(bytes);
}

// sort outpoints
outpoints.sort();

let mut engine = sha256::HashEngine::default();

for v in outpoints {
engine.write_all(&v)?;
}

Ok(Scalar::from_be_bytes(
sha256::Hash::from_engine(engine).into_inner(),
)?)
}
28 changes: 28 additions & 0 deletions src/utils/receiving.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::{utils::hash_outpoints, Result};
use secp256k1::{PublicKey, SecretKey};

pub fn recipient_calculate_tweak_data(
input_pub_keys: &Vec<PublicKey>,
outpoints: &Vec<(String, u32)>,
) -> Result<PublicKey> {
let secp = secp256k1::Secp256k1::new();
let A_sum = recipient_get_A_sum_public_keys(input_pub_keys);
let outpoints_hash = hash_outpoints(outpoints)?;

Ok(A_sum.mul_tweak(&secp, &outpoints_hash)?)
}

pub fn recipient_calculate_shared_secret(
tweak_data: PublicKey,
b_scan: SecretKey,
) -> Result<PublicKey> {
let secp = secp256k1::Secp256k1::new();

Ok(tweak_data.mul_tweak(&secp, &b_scan.into())?)
}

fn recipient_get_A_sum_public_keys(input: &Vec<PublicKey>) -> PublicKey {
let keys_refs: &Vec<&PublicKey> = &input.iter().collect();

PublicKey::combine_keys(keys_refs).unwrap()
}
9 changes: 9 additions & 0 deletions src/utils/sending.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use crate::Result;
use secp256k1::{Scalar, SecretKey};

pub fn sender_calculate_partial_secret(
a_sum: SecretKey,
outpoints_hash: Scalar,
) -> Result<SecretKey> {
Ok(a_sum.mul_tweak(&outpoints_hash)?)
}
121 changes: 27 additions & 94 deletions tests/common/utils.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use std::{
collections::HashSet,
fs::File,
io::{Read, Write},
io::Read,
str::FromStr,
};

use secp256k1::{
hashes::{sha256, Hash},
hashes::Hash,
Message, PublicKey, Scalar, SecretKey, XOnlyPublicKey,
};
use serde_json::from_str;
Expand All @@ -20,22 +19,6 @@ pub fn read_file() -> Vec<TestData> {
from_str(&contents).unwrap()
}

pub fn decode_outpoints(outpoints: &Vec<(String, u32)>) -> HashSet<([u8; 32], u32)> {
outpoints
.iter()
.map(|(txid_str, vout)| {
(
hex::decode(txid_str)
.unwrap()
.as_slice()
.try_into()
.unwrap(),
*vout,
)
})
.collect()
}

pub fn decode_priv_keys(input_priv_keys: &Vec<(String, bool)>) -> Vec<(SecretKey, bool)> {
input_priv_keys
.iter()
Expand Down Expand Up @@ -71,81 +54,6 @@ pub fn decode_recipients(recipients: &Vec<(String, f32)>) -> Vec<String> {
.collect()
}

pub fn get_a_sum_secret_keys(input: &Vec<(SecretKey, bool)>) -> SecretKey {
let secp = secp256k1::Secp256k1::new();

let mut negated_keys: Vec<SecretKey> = vec![];

for (key, is_xonly) in input {
let (_, parity) = key.x_only_public_key(&secp);

if *is_xonly && parity == secp256k1::Parity::Odd {
negated_keys.push(key.negate());
} else {
negated_keys.push(key.clone());
}
}

let (head, tail) = negated_keys.split_first().unwrap();

let result: SecretKey = tail
.iter()
.fold(*head, |acc, &item| acc.add_tweak(&item.into()).unwrap());

result
}

pub fn get_A_sum_public_keys(input: &Vec<PublicKey>) -> PublicKey {
let keys_refs: &Vec<&PublicKey> = &input.iter().collect();

PublicKey::combine_keys(keys_refs).unwrap()
}

pub fn calculate_tweak_data_for_recipient(
input_pub_keys: &Vec<PublicKey>,
outpoints: &HashSet<([u8; 32], u32)>,
) -> PublicKey {
let secp = secp256k1::Secp256k1::new();
let A_sum = get_A_sum_public_keys(input_pub_keys);
let outpoints_hash = hash_outpoints(outpoints);

A_sum.mul_tweak(&secp, &outpoints_hash).unwrap()
}

pub fn sender_calculate_partial_secret(a_sum: SecretKey, outpoints_hash: Scalar) -> SecretKey {
a_sum.mul_tweak(&outpoints_hash).unwrap()
}

pub fn receiver_calculate_shared_secret(tweak_data: PublicKey, b_scan: SecretKey) -> PublicKey {
let secp = secp256k1::Secp256k1::new();

tweak_data.mul_tweak(&secp, &b_scan.into()).unwrap()
}

pub fn hash_outpoints(sending_data: &HashSet<([u8; 32], u32)>) -> Scalar {
let mut outpoints: Vec<Vec<u8>> = vec![];

for outpoint in sending_data {
let txid = outpoint.0;
let vout = outpoint.1;

let mut bytes: Vec<u8> = Vec::new();
bytes.extend_from_slice(&txid);
bytes.reverse();
bytes.extend_from_slice(&vout.to_le_bytes());
outpoints.push(bytes);
}
outpoints.sort();

let mut engine = sha256::HashEngine::default();

for v in outpoints {
engine.write_all(&v).unwrap();
}

Scalar::from_be_bytes(sha256::Hash::from_engine(engine).into_inner()).unwrap()
}

pub fn verify_and_calculate_signatures(
key_tweaks: Vec<Scalar>,
b_spend: SecretKey,
Expand Down Expand Up @@ -178,3 +86,28 @@ pub fn verify_and_calculate_signatures(
}
Ok(res)
}


pub fn sender_get_a_sum_secret_keys(input: &Vec<(SecretKey, bool)>) -> SecretKey {
let secp = secp256k1::Secp256k1::new();

let mut negated_keys: Vec<SecretKey> = vec![];

for (key, is_xonly) in input {
let (_, parity) = key.x_only_public_key(&secp);

if *is_xonly && parity == secp256k1::Parity::Odd {
negated_keys.push(key.negate());
} else {
negated_keys.push(key.clone());
}
}

let (head, tail) = negated_keys.split_first().unwrap();

let result: SecretKey = tail
.iter()
.fold(*head, |acc, &item| acc.add_tweak(&item.into()).unwrap());

result
}
32 changes: 16 additions & 16 deletions tests/vector_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,26 @@ mod common;

#[cfg(test)]
mod tests {
use std::{
collections::HashSet,
str::FromStr,
};
use std::{collections::HashSet, str::FromStr};

use secp256k1::{Secp256k1, SecretKey, Scalar};
use secp256k1::{Scalar, Secp256k1, SecretKey};

#[cfg(feature = "receiving")]
use silentpayments::receiving::Receiver;

#[cfg(feature = "sending")]
use silentpayments::sending::generate_multiple_recipient_pubkeys;
use silentpayments::{
utils::receiving::{recipient_calculate_shared_secret, recipient_calculate_tweak_data},
utils::sending::sender_calculate_partial_secret,
utils::hash_outpoints,
};

use crate::common::{
structs::TestData,
utils::{
self, calculate_tweak_data_for_recipient, decode_input_pub_keys, decode_outpoints,
decode_outputs_to_check, decode_priv_keys, decode_recipients, get_a_sum_secret_keys,
hash_outpoints, sender_calculate_partial_secret, verify_and_calculate_signatures, receiver_calculate_shared_secret
self, decode_input_pub_keys, decode_outputs_to_check, decode_priv_keys,
decode_recipients, sender_get_a_sum_secret_keys, verify_and_calculate_signatures,
},
};

Expand Down Expand Up @@ -51,16 +52,15 @@ mod tests {

let input_priv_keys = decode_priv_keys(&given.input_priv_keys);

let outpoints = decode_outpoints(&given.outpoints);
let outpoints = given.outpoints;

let outpoints_hash = hash_outpoints(&outpoints);
let outpoints_hash = hash_outpoints(&outpoints).unwrap();

let silent_addresses = decode_recipients(&given.recipients);

let a_sum = get_a_sum_secret_keys(&input_priv_keys);
let a_sum = sender_get_a_sum_secret_keys(&input_priv_keys);

let partial_secret =
sender_calculate_partial_secret(a_sum, outpoints_hash);
let partial_secret = sender_calculate_partial_secret(a_sum, outpoints_hash).unwrap();

let outputs =
generate_multiple_recipient_pubkeys(silent_addresses, partial_secret).unwrap();
Expand Down Expand Up @@ -98,7 +98,7 @@ mod tests {

let outputs_to_check = decode_outputs_to_check(&given.outputs);

let outpoints = decode_outpoints(&given.outpoints);
let outpoints = &given.outpoints;

let input_pub_keys = decode_input_pub_keys(&given.input_pub_keys);

Expand All @@ -125,8 +125,8 @@ mod tests {
// to the expected addresses
assert_eq!(set1, set2);

let tweak_data = calculate_tweak_data_for_recipient(&input_pub_keys, &outpoints);
let shared_secret = receiver_calculate_shared_secret(tweak_data, b_scan);
let tweak_data = recipient_calculate_tweak_data(&input_pub_keys, &outpoints).unwrap();
let shared_secret = recipient_calculate_shared_secret(tweak_data, b_scan).unwrap();

let scanned_outputs_received = sp_receiver
.scan_transaction_with_labels(&shared_secret, outputs_to_check)
Expand Down

0 comments on commit 8a843a3

Please sign in to comment.