#[cfg(feature = "serde")]
use serde_crate::{Deserialize, Serialize};
use std::fmt;
use std::io::Cursor;
use crate::consensus::encode::Encodable;
use crate::cryptonote::hash;
use crate::network::Network;
use crate::util::address::Address;
use crate::util::key::{KeyPair, PrivateKey, PublicKey, ViewPair};
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub struct Index {
pub major: u32,
pub minor: u32,
}
impl Index {
pub fn is_zero(self) -> bool {
self.major == 0 && self.minor == 0
}
}
impl fmt::Display for Index {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}/{}", self.major, self.minor)
}
}
pub fn get_secret_scalar(view: &PrivateKey, index: Index) -> PrivateKey {
let mut encoder = Cursor::new(vec![]);
view.consensus_encode(&mut encoder).unwrap();
index.major.consensus_encode(&mut encoder).unwrap();
index.minor.consensus_encode(&mut encoder).unwrap();
let mut prefix: Vec<_> = b"SubAddr\x00"[..].into();
prefix.extend_from_slice(&encoder.into_inner());
hash::Hash::hash_to_scalar(&prefix)
}
pub fn get_spend_secret_key(keys: &KeyPair, index: Index) -> PrivateKey {
if index.is_zero() {
return keys.spend;
}
keys.spend + get_secret_scalar(&keys.view, index)
}
pub fn get_view_secret_key(keys: &KeyPair, index: Index) -> PrivateKey {
if index.is_zero() {
return keys.view;
}
keys.view * get_spend_secret_key(keys, index)
}
pub fn get_secret_keys(keys: &KeyPair, index: Index) -> KeyPair {
let view = get_view_secret_key(keys, index);
let spend = get_spend_secret_key(keys, index);
KeyPair { view, spend }
}
pub fn get_spend_public_key(keys: &ViewPair, index: Index) -> PublicKey {
if index.is_zero() {
return keys.spend;
}
let m = get_secret_scalar(&keys.view, index);
keys.spend + PublicKey::from_private_key(&m)
}
pub fn get_public_keys(keys: &ViewPair, index: Index) -> (PublicKey, PublicKey) {
if index.is_zero() {
let view = PublicKey::from_private_key(&keys.view);
return (view, keys.spend);
}
let spend = get_spend_public_key(keys, index);
let view = keys.view * &spend;
(view, spend)
}
pub fn get_subaddress(keys: &ViewPair, index: Index, network: Option<Network>) -> Address {
let net = network.unwrap_or_default();
let (view, spend) = get_public_keys(keys, index);
Address::subaddress(net, spend, view)
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::{get_public_keys, get_subaddress, Index};
use crate::network::Network;
use crate::util::key::{PrivateKey, PublicKey, ViewPair};
#[test]
#[allow(non_snake_case)]
fn get_subkeys_test() {
let a = PrivateKey::from_str(
"77916d0cd56ed1920aef6ca56d8a41bac915b68e4c46a589e0956e27a7b77404",
)
.unwrap();
let b = PrivateKey::from_str(
"8163466f1883598e6dd14027b8da727057165da91485834314f5500a65846f09",
)
.unwrap();
let B = PublicKey::from_private_key(&b);
let viewpair = ViewPair { view: a, spend: B };
let index = Index {
major: 2,
minor: 18,
};
let (sub_view_pub, sub_spend_pub) = get_public_keys(&viewpair, index);
assert_eq!(
"601782bdde614e9ba664048a27b7407df4b76ae2e50a85fcc168a4c1766b3edf",
sub_view_pub.to_string()
);
assert_eq!(
"c25179ddef2ca4728fb691dd71561dc9f2e7e6b2a14284a4fe5441d7757aea02",
sub_spend_pub.to_string()
);
}
#[test]
#[allow(non_snake_case)]
fn get_subaddress_test() {
let a = PrivateKey::from_str(
"77916d0cd56ed1920aef6ca56d8a41bac915b68e4c46a589e0956e27a7b77404",
)
.unwrap();
let b = PrivateKey::from_str(
"8163466f1883598e6dd14027b8da727057165da91485834314f5500a65846f09",
)
.unwrap();
let B = PublicKey::from_private_key(&b);
let viewpair = ViewPair { view: a, spend: B };
let index = Index {
major: 2,
minor: 18,
};
let address = get_subaddress(&viewpair, index, Some(Network::Mainnet));
assert_eq!("89pMNxzcCo5LAPZDX4qaTeanA6ZiS3VRdUbeKHzbDZkD1Q3YsDDfmXbT2zyjLeHWuuN4vxKne8kNpjH3cMk7nmhwSALCxsd", address.to_string());
}
}