Skip to content

Commit

Permalink
update to new tests
Browse files Browse the repository at this point in the history
* use the new test structure
  this adds some helpers for deserializing scripts and witness stacks into bytes
* use the newly added functions to extract the pubkeys from the tests
* update to use tagged hashes
* update to the new input hashing method (smallest outpoint || A_sum)

Co-authored-by: Sosthene00 <[email protected]>
  • Loading branch information
2 people authored and cygnet3 committed Jan 24, 2024
1 parent eb83d8c commit ed13c87
Show file tree
Hide file tree
Showing 9 changed files with 1,513 additions and 995 deletions.
14 changes: 7 additions & 7 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use secp256k1::{
PublicKey, Scalar, Secp256k1, SecretKey,
};
use bitcoin_hashes::{sha256, Hash};
use bitcoin_hashes::Hash;
use crate::Result;
use crate::utils::SharedSecretHash;

pub(crate) fn calculate_t_n(ecdh_shared_secret: &[u8; 33], n: u32) -> Result<SecretKey> {
let mut bytes = [0u8; 37];
bytes[..33].copy_from_slice(ecdh_shared_secret);
bytes[33..].copy_from_slice(&n.to_be_bytes());

let hash = sha256::Hash::hash(&bytes).to_byte_array();
pub(crate) fn calculate_t_n(ecdh_shared_secret: &PublicKey, k: u32) -> Result<SecretKey> {
let hash = SharedSecretHash::from_ecdh_and_k(
ecdh_shared_secret,
k,
).to_byte_array();
let sk = SecretKey::from_slice(&hash)?;

Ok(sk)
Expand Down
4 changes: 2 additions & 2 deletions src/receiving.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ impl Receiver {
let mut n_found: u32 = 0;
let mut n: u32 = 0;
while n_found == n {
let t_n: SecretKey = calculate_t_n(&ecdh_shared_secret.serialize(), n)?;
let t_n: SecretKey = calculate_t_n(&ecdh_shared_secret, n)?;
let P_n: PublicKey = calculate_P_n(&self.spend_pubkey, t_n.into())?;
let P_n_xonly = P_n.x_only_public_key().0;
if pubkeys_to_check
Expand Down Expand Up @@ -411,7 +411,7 @@ impl Receiver {
&self,
ecdh_shared_secret: &PublicKey,
) -> Result<[u8; 34]> {
let t_n: SecretKey = calculate_t_n(&ecdh_shared_secret.serialize(), 0)?;
let t_n: SecretKey = calculate_t_n(&ecdh_shared_secret, 0)?;
let P_n: PublicKey = calculate_P_n(&self.spend_pubkey, t_n.into())?;
let output_key_bytes = P_n.x_only_public_key().0.serialize();

Expand Down
4 changes: 2 additions & 2 deletions src/sending.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl SilentPaymentAddress {
version,
})
}

pub fn get_scan_key(&self) -> PublicKey {
self.scan_pubkey
}
Expand Down Expand Up @@ -164,7 +164,7 @@ pub fn generate_multiple_recipient_pubkeys(
let (ecdh_shared_secret, recipients) = group;

for addr in recipients {
let t_n = calculate_t_n(&ecdh_shared_secret.serialize(), n)?;
let t_n = calculate_t_n(&ecdh_shared_secret, n)?;

let res = t_n.public_key(&secp);
let reskey = res.combine(&addr.m_pubkey)?;
Expand Down
43 changes: 23 additions & 20 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use std::io::Write;

use crate::Error;
use bitcoin_hashes::{sha256t_hash_newtype, sha256, Hash, HashEngine};
use bitcoin_hashes::{sha256t_hash_newtype, Hash, HashEngine};
use secp256k1::{
PublicKey,
Scalar,
Expand Down Expand Up @@ -39,11 +37,11 @@ sha256t_hash_newtype! {

impl InputsHash {
pub fn from_outpoint_and_A_sum(
smallest_outpoint: &Vec<u8>,
smallest_outpoint: &[u8;36],
A_sum: &PublicKey,
) -> InputsHash {
let mut eng = InputsHash::engine();
eng.input(&smallest_outpoint);
eng.input(smallest_outpoint);
eng.input(&A_sum.serialize());
InputsHash::from_engine(eng)
}
Expand Down Expand Up @@ -81,29 +79,34 @@ impl SharedSecretHash {
}
}

pub fn hash_outpoints(sending_data: &Vec<(String, u32)>) -> Result<Scalar, Error> {
let mut outpoints: Vec<Vec<u8>> = vec![];
pub fn hash_outpoints(outpoints_data: &[(String, u32)], A_sum: PublicKey) -> Result<Scalar, Error> {
if outpoints_data.is_empty() {return Err(Error::GenericError("No outpoints provided".to_owned()))}

let mut outpoints: Vec<[u8;36]> = Vec::with_capacity(outpoints_data.len());

// should probably just use an OutPoints type properly at some point
for (txid, vout) in outpoints_data {
let mut bytes: Vec<u8> = hex::decode(txid.as_str())?;

for outpoint in sending_data {
let mut bytes: Vec<u8> = hex::decode(outpoint.0.as_str())?;
if bytes.len() != 32 {return Err(Error::GenericError(format!("Invalid outpoint hex representation: {}", txid)))}

// 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);
let mut buffer = [0u8;36];

buffer[..32].copy_from_slice(&bytes);
buffer[32..].copy_from_slice(&vout.to_le_bytes());
outpoints.push(buffer);
}

// sort outpoints
outpoints.sort();

let mut engine = sha256::HashEngine::default();
outpoints.sort_unstable();

for v in outpoints {
engine.write_all(&v)?;
if let Some(smallest_outpoint) = outpoints.get(0) {
Ok(InputsHash::from_outpoint_and_A_sum(&smallest_outpoint, &A_sum).to_scalar())
} else {
// This should never happen
Err(Error::GenericError("Unexpected empty outpoints vector".to_owned()))
}

Ok(Scalar::from_be_bytes(
sha256::Hash::from_engine(engine).to_byte_array(),
)?)
}
2 changes: 1 addition & 1 deletion src/utils/receiving.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub fn recipient_calculate_tweak_data(
) -> 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)?;
let outpoints_hash = hash_outpoints(outpoints, A_sum)?;

Ok(A_sum.mul_tweak(&secp, &outpoints_hash)?)
}
Expand Down
48 changes: 38 additions & 10 deletions tests/common/structs.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#![allow(non_snake_case)]
use serde::Deserialize;

use std::collections::HashMap;

#[derive(Debug, Deserialize)]
pub struct TestData {
pub comment: String,
Expand All @@ -12,19 +10,40 @@ pub struct TestData {

#[derive(Debug, Deserialize)]
pub struct ReceivingData {
pub supports_labels: bool,
pub given: ReceivingDataGiven,
pub expected: ReceivingDataExpected,
}

#[derive(Debug, Deserialize)]
pub struct ReceivingDataGiven {
pub outpoints: Vec<(String, u32)>,
pub input_pub_keys: Vec<String>,
pub bip32_seed: String,
pub struct ReceivingKeyMaterial {
pub scan_priv_key: String,
pub spend_priv_key: String,
pub labels: HashMap<String, String>,
}

#[derive(Debug, Deserialize)]
pub struct HexStr {
pub hex: String,
}

#[derive(Debug, Deserialize)]
pub struct ScriptPubKey {
pub scriptPubKey: HexStr,
}

#[derive(Debug, Deserialize)]
pub struct ReceivingVinData {
pub txid: String,
pub vout: u32,
pub scriptSig: String,
pub txinwitness: String,
pub prevout: ScriptPubKey,
}

#[derive(Debug, Deserialize)]
pub struct ReceivingDataGiven {
pub vin: Vec<ReceivingVinData>,
pub key_material: ReceivingKeyMaterial,
pub labels: Vec<u32>,
pub outputs: Vec<String>,
}

Expand All @@ -42,11 +61,20 @@ pub struct SendingData {

#[derive(Debug, Deserialize)]
pub struct SendingDataGiven {
pub outpoints: Vec<(String, u32)>,
pub input_priv_keys: Vec<(String, bool)>,
pub vin: Vec<SendingVinData>,
pub recipients: Vec<(String, f32)>,
}

#[derive(Debug, Deserialize)]
pub struct SendingVinData {
pub txid: String,
pub vout: u32,
pub scriptSig: String,
pub txinwitness: String,
pub prevout: ScriptPubKey,
pub private_key: String,
}

#[derive(Debug, Deserialize)]
pub struct SendingDataExpected {
pub outputs: Vec<(String, f32)>,
Expand Down
63 changes: 42 additions & 21 deletions tests/common/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,48 @@ use serde_json::from_str;
use super::structs::{OutputWithSignature, TestData};

use silentpayments::Error;
use std::io::{self, Cursor};
use std::convert::TryInto;

fn deser_compact_size(f: &mut Cursor<&Vec<u8>>) -> io::Result<u64> {
let mut buf = [0; 8];
f.read_exact(&mut buf[..1])?;
match buf[0] {
0xfd => {
f.read_exact(&mut buf[..2])?;
Ok(u16::from_le_bytes(buf[..2].try_into().unwrap()) as u64)
},
0xfe => {
f.read_exact(&mut buf[..4])?;
Ok(u32::from_le_bytes(buf[..4].try_into().unwrap()) as u64)
},
0xff => {
f.read_exact(&mut buf)?;
Ok(u64::from_le_bytes(buf))
},
_ => Ok(buf[0] as u64),
}
}

fn deser_string(f: &mut Cursor<&Vec<u8>>) -> io::Result<Vec<u8>> {
let size = deser_compact_size(f)? as usize;
let mut buf = vec![0; size];
f.read_exact(&mut buf)?;
Ok(buf)
}

pub fn deser_string_vector(f: &mut Cursor<&Vec<u8>>) -> io::Result<Vec<Vec<u8>>> {
// Check if the buffer is empty before attempting to deserialize the size
if f.get_ref().is_empty() {
return Ok(Vec::new()); // Return an empty vector if the buffer is empty
}
let size = deser_compact_size(f)? as usize;
let mut vec = Vec::with_capacity(size);
for _ in 0..size {
vec.push(deser_string(f)?);
}
Ok(vec)
}

// ** Putting all the pubkey extraction logic in the test utils for now. **
// NUMS_H (defined in BIP340)
Expand Down Expand Up @@ -142,27 +184,6 @@ pub fn read_file() -> Vec<TestData> {
from_str(&contents).unwrap()
}

pub fn decode_priv_keys(input_priv_keys: &Vec<(String, bool)>) -> Vec<(SecretKey, bool)> {
input_priv_keys
.iter()
.map(|(keystr, x_only)| (SecretKey::from_str(&keystr).unwrap(), *x_only))
.collect()
}

pub fn decode_input_pub_keys(input_pub_keys: &Vec<String>) -> Vec<PublicKey> {
input_pub_keys
.iter()
.map(|x| match PublicKey::from_str(&x) {
Ok(key) => key,
Err(_) => {
// we always assume even pairing for input public keys if they are omitted
let x_only_public_key = XOnlyPublicKey::from_str(&x).unwrap();
PublicKey::from_x_only_public_key(x_only_public_key, secp256k1::Parity::Even)
}
})
.collect()
}

pub fn decode_outputs_to_check(outputs: &Vec<String>) -> Vec<XOnlyPublicKey> {
outputs
.iter()
Expand Down
Loading

0 comments on commit ed13c87

Please sign in to comment.