mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-09 04:39:24 +00:00
Uses dalek-ff-group for Ed25519 and Ristretto. Uses minimal-ed448 for Ed448. Adds ciphersuite-kp256 for Secp256k1 and P-256.
104 lines
2.8 KiB
Rust
104 lines
2.8 KiB
Rust
use core::{str::FromStr, fmt};
|
|
|
|
use scale::{Encode, Decode};
|
|
|
|
use dalek_ff_group::Ed25519;
|
|
use ciphersuite::Ciphersuite;
|
|
|
|
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()
|
|
}
|
|
}
|