mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-13 14:39:25 +00:00
Guaranteed addresses are in fact guaranteed even with this due to prefixing key images causing zeroing the ECDH to not zero the shared key.
103 lines
2.8 KiB
Rust
103 lines
2.8 KiB
Rust
use core::{str::FromStr, fmt};
|
|
|
|
use scale::{Encode, Decode};
|
|
|
|
use ciphersuite::{Ciphersuite, Ed25519};
|
|
|
|
use monero_wallet::address::{AddressError, Network, AddressType, MoneroAddress};
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
pub struct Address(MoneroAddress);
|
|
impl Address {
|
|
pub fn new(address: MoneroAddress) -> Option<Address> {
|
|
if address.payment_id().is_some() {
|
|
return None;
|
|
}
|
|
Some(Address(address))
|
|
}
|
|
}
|
|
|
|
impl FromStr for Address {
|
|
type Err = AddressError;
|
|
fn from_str(str: &str) -> Result<Address, AddressError> {
|
|
MoneroAddress::from_str(Network::Mainnet, str).map(Address)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Address {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.0.fmt(f)
|
|
}
|
|
}
|
|
|
|
// SCALE-encoded variant of Monero addresses.
|
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
|
enum EncodedAddressType {
|
|
Legacy,
|
|
Subaddress,
|
|
Featured(u8),
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
|
struct EncodedAddress {
|
|
kind: EncodedAddressType,
|
|
spend: [u8; 32],
|
|
view: [u8; 32],
|
|
}
|
|
|
|
impl TryFrom<Vec<u8>> for Address {
|
|
type Error = ();
|
|
fn try_from(data: Vec<u8>) -> Result<Address, ()> {
|
|
// Decode as SCALE
|
|
let addr = EncodedAddress::decode(&mut data.as_ref()).map_err(|_| ())?;
|
|
// Convert over
|
|
Ok(Address(MoneroAddress::new(
|
|
Network::Mainnet,
|
|
match addr.kind {
|
|
EncodedAddressType::Legacy => AddressType::Legacy,
|
|
EncodedAddressType::Subaddress => AddressType::Subaddress,
|
|
EncodedAddressType::Featured(flags) => {
|
|
let subaddress = (flags & 1) != 0;
|
|
let integrated = (flags & (1 << 1)) != 0;
|
|
let guaranteed = (flags & (1 << 2)) != 0;
|
|
if integrated {
|
|
Err(())?;
|
|
}
|
|
AddressType::Featured { subaddress, payment_id: None, guaranteed }
|
|
}
|
|
},
|
|
Ed25519::read_G::<&[u8]>(&mut addr.spend.as_ref()).map_err(|_| ())?.0,
|
|
Ed25519::read_G::<&[u8]>(&mut addr.view.as_ref()).map_err(|_| ())?.0,
|
|
)))
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::from_over_into)]
|
|
impl Into<MoneroAddress> for Address {
|
|
fn into(self) -> MoneroAddress {
|
|
self.0
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::from_over_into)]
|
|
impl Into<Vec<u8>> for Address {
|
|
fn into(self) -> Vec<u8> {
|
|
EncodedAddress {
|
|
kind: match self.0.kind() {
|
|
AddressType::Legacy => EncodedAddressType::Legacy,
|
|
AddressType::LegacyIntegrated(_) => {
|
|
panic!("integrated address became Serai Monero address")
|
|
}
|
|
AddressType::Subaddress => EncodedAddressType::Subaddress,
|
|
AddressType::Featured { subaddress, payment_id, guaranteed } => {
|
|
debug_assert!(payment_id.is_none());
|
|
EncodedAddressType::Featured(u8::from(*subaddress) + (u8::from(*guaranteed) << 2))
|
|
}
|
|
},
|
|
spend: self.0.spend().compress().0,
|
|
view: self.0.view().compress().0,
|
|
}
|
|
.encode()
|
|
}
|
|
}
|