use bitcoin::hashes::{sha256t::Tag, Hash, HashEngine};
use bitcoin::secp256k1;
use bitcoin::secp256k1::scalar::OutOfRangeError;
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, Signing, Verification};
use crate::{LabelHash, LabelTag, ScanSecretKey, SpendPublicKey};
const CHANGE_LABEL_INDEX: u32 = 0;
#[derive(Copy, Clone, Hash, Debug, Ord, PartialEq, PartialOrd, Eq)]
pub enum Label {
Change,
Index(LabelIndex),
}
impl Label {
pub fn tweak(&self, scan_key: &ScanSecretKey) -> Result<LabelTweak, OutOfRangeError> {
match self {
Label::Change => LabelTweak::change(scan_key),
Label::Index(i) => LabelTweak::from_index(scan_key, *i),
}
}
}
#[derive(Copy, Clone, Debug, Hash, Ord, PartialEq, PartialOrd, Eq)]
pub struct LabelIndex(u32);
impl TryFrom<u32> for LabelIndex {
type Error = LabelZeroError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
if value == CHANGE_LABEL_INDEX {
Err(LabelZeroError)
} else {
Ok(Self(value))
}
}
}
impl TryFrom<&u32> for LabelIndex {
type Error = LabelZeroError;
fn try_from(value: &u32) -> Result<Self, Self::Error> {
if *value == CHANGE_LABEL_INDEX {
Err(LabelZeroError)
} else {
Ok(Self(*value))
}
}
}
impl From<LabelIndex> for u32 {
fn from(val: LabelIndex) -> Self {
val.0
}
}
#[derive(Debug)]
pub struct LabelZeroError;
pub struct LabelTweak {
tweak: Scalar,
label: Label,
}
impl LabelTweak {
pub fn from_index(
scan_key: &ScanSecretKey,
index: LabelIndex,
) -> Result<LabelTweak, OutOfRangeError> {
Self::label_tweak(scan_key, index.into()).map(|tweak| LabelTweak {
tweak,
label: Label::Index(index),
})
}
pub fn change(scan_key: &ScanSecretKey) -> Result<LabelTweak, OutOfRangeError> {
Self::label_tweak(scan_key, CHANGE_LABEL_INDEX).map(|tweak| LabelTweak {
tweak,
label: Label::Change,
})
}
fn label_tweak(b_scan: &ScanSecretKey, m: u32) -> Result<Scalar, OutOfRangeError> {
let mut engine = LabelTag::engine();
engine.input(&b_scan.to_secret_key().secret_bytes());
engine.input(&m.to_be_bytes());
Scalar::from_be_bytes(LabelHash::from_engine(engine).to_byte_array())
}
pub fn apply_to_key<C: Verification>(
&self,
spend_key: &SpendPublicKey,
secp: &Secp256k1<C>,
) -> Result<SpendPublicKey, secp256k1::Error> {
spend_key.add_tweak(&self.tweak, secp)
}
pub fn to_public_key<C: Signing>(&self, secp: &Secp256k1<C>) -> PublicKey {
SecretKey::from_slice(&self.tweak.to_be_bytes())
.expect("tweak is on curve, so should be the secret key")
.public_key(secp)
}
pub(crate) fn to_scalar(&self) -> Scalar {
self.tweak
}
pub fn label(&self) -> Label {
self.label
}
}