monero_wallet/
view_pair.rs1use core::ops::Deref;
2
3use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
4
5use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, Scalar, EdwardsPoint};
6
7use crate::{
8 primitives::keccak256_to_scalar,
9 address::{Network, AddressType, SubaddressIndex, MoneroAddress},
10};
11
12#[derive(Clone, PartialEq, Eq, Debug, thiserror::Error)]
14pub enum ViewPairError {
15 #[error("torsioned spend key")]
23 TorsionedSpendKey,
24}
25
26#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
30pub struct ViewPair {
31 spend: EdwardsPoint,
32 pub(crate) view: Zeroizing<Scalar>,
33}
34
35impl ViewPair {
36 pub fn new(spend: EdwardsPoint, view: Zeroizing<Scalar>) -> Result<Self, ViewPairError> {
38 if !spend.is_torsion_free() {
39 Err(ViewPairError::TorsionedSpendKey)?;
40 }
41 Ok(ViewPair { spend, view })
42 }
43
44 pub fn spend(&self) -> EdwardsPoint {
46 self.spend
47 }
48
49 pub fn view(&self) -> EdwardsPoint {
51 self.view.deref() * ED25519_BASEPOINT_TABLE
52 }
53
54 pub(crate) fn subaddress_derivation(&self, index: SubaddressIndex) -> Scalar {
55 keccak256_to_scalar(Zeroizing::new(
56 [
57 b"SubAddr\0".as_ref(),
58 Zeroizing::new(self.view.to_bytes()).as_ref(),
59 &index.account().to_le_bytes(),
60 &index.address().to_le_bytes(),
61 ]
62 .concat(),
63 ))
64 }
65
66 pub(crate) fn subaddress_keys(&self, index: SubaddressIndex) -> (EdwardsPoint, EdwardsPoint) {
67 let scalar = self.subaddress_derivation(index);
68 let spend = self.spend + (&scalar * ED25519_BASEPOINT_TABLE);
69 let view = self.view.deref() * spend;
70 (spend, view)
71 }
72
73 pub fn legacy_address(&self, network: Network) -> MoneroAddress {
77 MoneroAddress::new(network, AddressType::Legacy, self.spend, self.view())
78 }
79
80 pub fn legacy_integrated_address(&self, network: Network, payment_id: [u8; 8]) -> MoneroAddress {
84 MoneroAddress::new(network, AddressType::LegacyIntegrated(payment_id), self.spend, self.view())
85 }
86
87 pub fn subaddress(&self, network: Network, subaddress: SubaddressIndex) -> MoneroAddress {
89 let (spend, view) = self.subaddress_keys(subaddress);
90 MoneroAddress::new(network, AddressType::Subaddress, spend, view)
91 }
92}
93
94#[derive(Clone, PartialEq, Eq, Zeroize)]
102pub struct GuaranteedViewPair(pub(crate) ViewPair);
103
104impl GuaranteedViewPair {
105 pub fn new(spend: EdwardsPoint, view: Zeroizing<Scalar>) -> Result<Self, ViewPairError> {
107 ViewPair::new(spend, view).map(GuaranteedViewPair)
108 }
109
110 pub fn spend(&self) -> EdwardsPoint {
112 self.0.spend()
113 }
114
115 pub fn view(&self) -> EdwardsPoint {
117 self.0.view()
118 }
119
120 pub fn address(
125 &self,
126 network: Network,
127 subaddress: Option<SubaddressIndex>,
128 payment_id: Option<[u8; 8]>,
129 ) -> MoneroAddress {
130 let (spend, view) = if let Some(index) = subaddress {
131 self.0.subaddress_keys(index)
132 } else {
133 (self.spend(), self.view())
134 };
135
136 MoneroAddress::new(
137 network,
138 AddressType::Featured { subaddress: subaddress.is_some(), payment_id, guaranteed: true },
139 spend,
140 view,
141 )
142 }
143}