use bitcoin::hashes::{sha256t_hash_newtype, Hash};
use elements::encode::Encodable;
use elements::secp256k1_zkp;
use crate::ToPublicKey;
sha256t_hash_newtype! {
pub struct TapTweakTag = hash_str("CT-Blinding-Key/1.0");
#[hash_newtype(forward)]
pub struct TapTweakHash(_);
}
pub fn tweak_key<'a, Pk, V>(
secp: &secp256k1_zkp::Secp256k1<V>,
spk: &elements::Script,
pk: &Pk,
) -> secp256k1_zkp::PublicKey
where
Pk: ToPublicKey + 'a,
V: secp256k1_zkp::Verification,
{
let mut eng = TapTweakHash::engine();
pk.to_public_key()
.write_into(&mut eng)
.expect("engines don't error");
spk.consensus_encode(&mut eng).expect("engines don't error");
let hash_bytes = TapTweakHash::from_engine(eng).to_byte_array();
let hash_scalar = secp256k1_zkp::Scalar::from_be_bytes(hash_bytes).expect("bytes from hash");
pk.to_public_key()
.inner
.add_exp_tweak(secp, &hash_scalar)
.unwrap()
}
pub fn tweak_private_key<V>(
secp: &secp256k1_zkp::Secp256k1<V>,
spk: &elements::Script,
sk: &secp256k1_zkp::SecretKey,
) -> secp256k1_zkp::SecretKey
where
V: secp256k1_zkp::Signing,
{
let mut eng = TapTweakHash::engine();
bitcoin::PublicKey::new(sk.public_key(secp))
.write_into(&mut eng)
.expect("engines don't error");
spk.consensus_encode(&mut eng).expect("engines don't error");
let hash_bytes = TapTweakHash::from_engine(eng).to_byte_array();
let hash_scalar = secp256k1_zkp::Scalar::from_be_bytes(hash_bytes).expect("bytes from hash");
sk.add_tweak(&hash_scalar).unwrap()
}
#[cfg(test)]
mod tests {
use bitcoin::hashes::sha256t::Tag;
use bitcoin::hashes::{sha256, HashEngine};
use super::*;
const MIDSTATE_HASH_TO_PRIVATE_HASH: [u8; 32] = [
0x2f, 0x85, 0x61, 0xec, 0x30, 0x88, 0xad, 0xa9, 0x5a, 0xe7, 0x43, 0xcd, 0x3c, 0x5f, 0x59,
0x7d, 0xc0, 0x4b, 0xd0, 0x7f, 0x06, 0x5f, 0x1c, 0x06, 0x47, 0x89, 0x36, 0x63, 0xf3, 0x92,
0x6e, 0x65,
];
#[test]
fn tagged_hash() {
let mut engine = sha256::Hash::engine();
let tag_hash = sha256::Hash::hash(b"CT-Blinding-Key/1.0");
engine.input(&tag_hash[..]);
engine.input(&tag_hash[..]);
assert_eq!(
MIDSTATE_HASH_TO_PRIVATE_HASH,
engine.midstate().to_byte_array()
);
assert_eq!(
TapTweakHash::from_engine(TapTweakTag::engine()).to_string(),
"d12a140aca856fbb917b931f263c42f064608985e2ce17ae5157daa17c55e8d9",
);
assert_eq!(
TapTweakHash::hash(&[]).to_string(),
"d12a140aca856fbb917b931f263c42f064608985e2ce17ae5157daa17c55e8d9",
);
let data: Vec<u8> = (0..80).collect();
assert_eq!(
TapTweakHash::hash(&data).to_string(),
"e1e52419a2934d278c50e29608969d2f23c1bd1243a09bfc8026d4ed4b085e39",
);
}
#[test]
fn tweak() {
let secp = secp256k1_zkp::Secp256k1::new();
let sk = secp256k1_zkp::SecretKey::from_slice(&[1u8; 32]).unwrap();
let pk = sk.public_key(&secp);
let spk = elements::Script::default();
let tweaked_pk = tweak_key(&secp, &spk, &pk);
let tweaked_sk = tweak_private_key(&secp, &spk, &sk);
assert_eq!(tweaked_pk, tweaked_sk.public_key(&secp));
}
}