use bitcoin::hashes::Hash;
use bitcoin::hashes::HashEngine;
use bitcoin::secp256k1::Scalar;
use bitcoin::secp256k1::SecretKey;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::secp256k1;
use crate::ln::msgs::DecodeError;
use crate::util::ser::Readable;
use crate::io;
use crate::util::ser::Writer;
use crate::util::ser::Writeable;
use bitcoin::secp256k1::PublicKey;
use bitcoin::hashes::sha256::Hash as Sha256;
macro_rules! doc_comment {
($x:expr, $($tt:tt)*) => {
#[doc = $x]
$($tt)*
};
}
macro_rules! basepoint_impl {
($BasepointT:ty) => {
impl $BasepointT {
pub fn to_public_key(&self) -> PublicKey {
self.0
}
}
impl From<PublicKey> for $BasepointT {
fn from(value: PublicKey) -> Self {
Self(value)
}
}
}
}
macro_rules! key_impl {
($BasepointT:ty, $KeyName:expr) => {
doc_comment! {
concat!("Derive a public ", $KeyName, " using one node's `per_commitment_point` and its countersignatory's `basepoint`"),
pub fn from_basepoint<T: secp256k1::Signing>(
secp_ctx: &Secp256k1<T>,
countersignatory_basepoint: &$BasepointT,
per_commitment_point: &PublicKey,
) -> Self {
Self(derive_public_key(secp_ctx, per_commitment_point, &countersignatory_basepoint.0))
}
}
doc_comment! {
concat!("Build a ", $KeyName, " directly from an already-derived private key"),
pub fn from_secret_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, sk: &SecretKey) -> Self {
Self(PublicKey::from_secret_key(&secp_ctx, &sk))
}
}
pub fn to_public_key(&self) -> PublicKey {
self.0
}
}
}
macro_rules! key_read_write {
($SelfT:ty) => {
impl Writeable for $SelfT {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
self.0.serialize().write(w)
}
}
impl Readable for $SelfT {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
let key: PublicKey = Readable::read(r)?;
Ok(Self(key))
}
}
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
pub struct DelayedPaymentBasepoint(pub PublicKey);
basepoint_impl!(DelayedPaymentBasepoint);
key_read_write!(DelayedPaymentBasepoint);
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct DelayedPaymentKey(pub PublicKey);
impl DelayedPaymentKey {
key_impl!(DelayedPaymentBasepoint, "delayedpubkey");
}
key_read_write!(DelayedPaymentKey);
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
pub struct HtlcBasepoint(pub PublicKey);
basepoint_impl!(HtlcBasepoint);
key_read_write!(HtlcBasepoint);
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct HtlcKey(pub PublicKey);
impl HtlcKey {
key_impl!(HtlcBasepoint, "htlcpubkey");
}
key_read_write!(HtlcKey);
fn derive_public_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, base_point: &PublicKey) -> PublicKey {
let mut sha = Sha256::engine();
sha.input(&per_commitment_point.serialize());
sha.input(&base_point.serialize());
let res = Sha256::from_engine(sha).to_byte_array();
let hashkey = PublicKey::from_secret_key(&secp_ctx,
&SecretKey::from_slice(&res).expect("Hashes should always be valid keys unless SHA-256 is broken"));
base_point.combine(&hashkey)
.expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak contains the hash of the key.")
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
pub struct RevocationBasepoint(pub PublicKey);
basepoint_impl!(RevocationBasepoint);
key_read_write!(RevocationBasepoint);
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
pub struct RevocationKey(pub PublicKey);
impl RevocationKey {
pub fn from_basepoint<T: secp256k1::Verification>(
secp_ctx: &Secp256k1<T>,
countersignatory_basepoint: &RevocationBasepoint,
per_commitment_point: &PublicKey,
) -> Self {
let rev_append_commit_hash_key = {
let mut sha = Sha256::engine();
sha.input(&countersignatory_basepoint.to_public_key().serialize());
sha.input(&per_commitment_point.serialize());
Sha256::from_engine(sha).to_byte_array()
};
let commit_append_rev_hash_key = {
let mut sha = Sha256::engine();
sha.input(&per_commitment_point.serialize());
sha.input(&countersignatory_basepoint.to_public_key().serialize());
Sha256::from_engine(sha).to_byte_array()
};
let countersignatory_contrib = countersignatory_basepoint.to_public_key().mul_tweak(&secp_ctx, &Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap())
.expect("Multiplying a valid public key by a hash is expected to never fail per secp256k1 docs");
let broadcaster_contrib = (&per_commitment_point).mul_tweak(&secp_ctx, &Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap())
.expect("Multiplying a valid public key by a hash is expected to never fail per secp256k1 docs");
let pk = countersignatory_contrib.combine(&broadcaster_contrib)
.expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak commits to the key.");
Self(pk)
}
pub fn to_public_key(&self) -> PublicKey {
self.0
}
}
key_read_write!(RevocationKey);
#[cfg(test)]
mod test {
use bitcoin::secp256k1::{Secp256k1, SecretKey, PublicKey};
use bitcoin::hashes::hex::FromHex;
use super::derive_public_key;
#[test]
fn test_key_derivation() {
let secp_ctx = Secp256k1::new();
let base_secret = SecretKey::from_slice(&<Vec<u8>>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap()[..]).unwrap();
let per_commitment_secret = SecretKey::from_slice(&<Vec<u8>>::from_hex("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap();
let base_point = PublicKey::from_secret_key(&secp_ctx, &base_secret);
assert_eq!(base_point.serialize()[..], <Vec<u8>>::from_hex("036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2").unwrap()[..]);
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
assert_eq!(per_commitment_point.serialize()[..], <Vec<u8>>::from_hex("025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486").unwrap()[..]);
assert_eq!(derive_public_key(&secp_ctx, &per_commitment_point, &base_point).serialize()[..],
<Vec<u8>>::from_hex("0235f2dbfaa89b57ec7b055afe29849ef7ddfeb1cefdb9ebdc43f5494984db29e5").unwrap()[..]);
}
}