mirror of
https://github.com/serai-dex/serai.git
synced 2024-11-17 01:17:36 +00:00
update address public API design
This commit is contained in:
parent
d9fa88fa76
commit
3a319b229f
6 changed files with 97 additions and 101 deletions
|
@ -80,6 +80,15 @@ impl<B: AddressBytes> Zeroize for AddressMeta<B> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Specifies Address Properties
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||
pub enum AddressSpec {
|
||||
Standard,
|
||||
Integrated([u8; 8]),
|
||||
Subaddress(u32, u32),
|
||||
Featured(Option<(u32, u32)>, Option<[u8; 8]>, bool),
|
||||
}
|
||||
|
||||
/// Error when decoding an address.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Error)]
|
||||
pub enum AddressError {
|
||||
|
|
|
@ -16,7 +16,7 @@ pub(crate) use extra::{PaymentId, ExtraField, Extra};
|
|||
|
||||
/// Address encoding and decoding functionality.
|
||||
pub mod address;
|
||||
use address::{Network, AddressType, AddressMeta, MoneroAddress};
|
||||
use address::{Network, AddressType, AddressMeta, MoneroAddress, AddressSpec};
|
||||
|
||||
mod scan;
|
||||
pub use scan::{ReceivedOutput, SpendableOutput};
|
||||
|
@ -29,8 +29,6 @@ pub use send::{Fee, TransactionError, SignableTransaction, SignableTransactionBu
|
|||
#[cfg(feature = "multisig")]
|
||||
pub use send::TransactionMachine;
|
||||
|
||||
use self::address::MoneroAddressBytes;
|
||||
|
||||
fn key_image_sort(x: &EdwardsPoint, y: &EdwardsPoint) -> std::cmp::Ordering {
|
||||
x.compress().to_bytes().cmp(&y.compress().to_bytes()).reverse()
|
||||
}
|
||||
|
@ -108,7 +106,7 @@ impl ViewPair {
|
|||
ViewPair { spend, view }
|
||||
}
|
||||
|
||||
pub(crate) fn subaddress(&self, index: (u32, u32)) -> Scalar {
|
||||
fn subaddress_keys(&self, index: (u32, u32)) -> Scalar {
|
||||
if index == (0, 0) {
|
||||
return Scalar::zero();
|
||||
}
|
||||
|
@ -124,9 +122,45 @@ impl ViewPair {
|
|||
))
|
||||
}
|
||||
|
||||
/// Returns an address with the provided metadata.
|
||||
pub fn address(&self, meta: AddressMeta<MoneroAddressBytes>) -> MoneroAddress {
|
||||
MoneroAddress::new(meta, self.spend, self.view.deref() * &ED25519_BASEPOINT_TABLE)
|
||||
fn subaddress_derivation(&self, index: (u32, u32)) -> (EdwardsPoint, EdwardsPoint) {
|
||||
let scalar = self.subaddress_keys(index);
|
||||
let spend = self.spend + (&scalar * &ED25519_BASEPOINT_TABLE);
|
||||
let view = self.view.deref() * spend;
|
||||
(spend, view)
|
||||
}
|
||||
|
||||
/// Returns an address with the provided specification.
|
||||
pub fn address(&self, network: Network, spec: AddressSpec) -> MoneroAddress {
|
||||
let mut spend = self.spend;
|
||||
let mut view: EdwardsPoint = self.view.deref() * &ED25519_BASEPOINT_TABLE;
|
||||
|
||||
// construct the address meta
|
||||
let meta = match spec {
|
||||
AddressSpec::Standard => AddressMeta::new(network, AddressType::Standard),
|
||||
AddressSpec::Integrated(payment_id) => {
|
||||
AddressMeta::new(network, AddressType::Integrated(payment_id))
|
||||
}
|
||||
AddressSpec::Subaddress(i1, i2) => {
|
||||
if i1 == 0 && i2 == 0 {
|
||||
AddressMeta::new(network, AddressType::Standard)
|
||||
} else {
|
||||
(spend, view) = self.subaddress_derivation((i1, i2));
|
||||
AddressMeta::new(network, AddressType::Subaddress)
|
||||
}
|
||||
}
|
||||
AddressSpec::Featured(subaddress, payment_id, guaranteed) => {
|
||||
let mut is_subaddress = false;
|
||||
if let Some(index) = subaddress {
|
||||
if index != (0, 0) {
|
||||
(spend, view) = self.subaddress_derivation(index);
|
||||
is_subaddress = true;
|
||||
}
|
||||
}
|
||||
AddressMeta::new(network, AddressType::Featured(is_subaddress, payment_id, guaranteed))
|
||||
}
|
||||
};
|
||||
|
||||
MoneroAddress::new(meta, spend, view)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,80 +221,22 @@ impl Scanner {
|
|||
Scanner { pair, network, subaddresses, burning_bug }
|
||||
}
|
||||
|
||||
/// Returns the main address for this view pair.
|
||||
pub fn address(&self) -> MoneroAddress {
|
||||
let meta = AddressMeta::new(
|
||||
self.network,
|
||||
if self.burning_bug.is_none() {
|
||||
AddressType::Featured(false, None, true)
|
||||
} else {
|
||||
AddressType::Standard
|
||||
},
|
||||
);
|
||||
self.pair.address(meta)
|
||||
}
|
||||
|
||||
/// Returns the integrated address for a given payment ID.
|
||||
pub fn integrated_address(&self, payment_id: [u8; 8]) -> MoneroAddress {
|
||||
let meta = AddressMeta::new(
|
||||
self.network,
|
||||
if self.burning_bug.is_none() {
|
||||
AddressType::Featured(false, Some(payment_id), true)
|
||||
} else {
|
||||
AddressType::Integrated(payment_id)
|
||||
},
|
||||
);
|
||||
self.pair.address(meta)
|
||||
}
|
||||
|
||||
/// Returns the specified subaddress for this view pair.
|
||||
pub fn subaddress(&mut self, index: (u32, u32)) -> MoneroAddress {
|
||||
if index == (0, 0) {
|
||||
return self.address();
|
||||
}
|
||||
|
||||
let spend = self.pair.spend + (&self.pair.subaddress(index) * &ED25519_BASEPOINT_TABLE);
|
||||
self.subaddresses.insert(spend.compress(), index);
|
||||
|
||||
MoneroAddress::new(
|
||||
AddressMeta::new(
|
||||
self.network,
|
||||
if self.burning_bug.is_none() {
|
||||
AddressType::Featured(true, None, true)
|
||||
} else {
|
||||
AddressType::Subaddress
|
||||
},
|
||||
),
|
||||
spend,
|
||||
self.pair.view.deref() * spend,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a featured address.
|
||||
/// Returns the specified address.
|
||||
///
|
||||
/// A `Scanner` will fail to scan this address unless it is appropriate to the `Scanner`.
|
||||
/// If the `Scanner` was defined as not handling the burning bug, by passing `None` for
|
||||
/// `burning_bug` upon construction, this must be a guaranteed address
|
||||
/// in order to be scanned by it.
|
||||
pub fn featured_address(
|
||||
&mut self,
|
||||
subaddress: Option<(u32, u32)>,
|
||||
payment_id: Option<[u8; 8]>,
|
||||
guaranteed: bool,
|
||||
) -> MoneroAddress {
|
||||
let is_subaddress = subaddress.is_some() && (subaddress.unwrap() != (0, 0));
|
||||
pub fn address(&mut self, spec: AddressSpec) -> MoneroAddress {
|
||||
let addr = self.pair.address(self.network, spec);
|
||||
|
||||
let mut spend = self.pair.spend;
|
||||
let mut view: EdwardsPoint = self.pair.view.deref() * &ED25519_BASEPOINT_TABLE;
|
||||
if is_subaddress {
|
||||
spend =
|
||||
self.pair.spend + (&self.pair.subaddress(subaddress.unwrap()) * &ED25519_BASEPOINT_TABLE);
|
||||
self.subaddresses.insert(spend.compress(), subaddress.unwrap());
|
||||
view = self.pair.view.deref() * spend;
|
||||
let mut index = (0, 0);
|
||||
if let AddressSpec::Subaddress(i1, i2) = spec {
|
||||
index = (i1, i2);
|
||||
} else if let AddressSpec::Featured(Some(index_inner), _, _) = spec {
|
||||
index = index_inner;
|
||||
}
|
||||
|
||||
let meta =
|
||||
AddressMeta::new(self.network, AddressType::Featured(is_subaddress, payment_id, guaranteed));
|
||||
MoneroAddress::new(meta, spend, view)
|
||||
self.subaddresses.insert(addr.spend.compress(), index);
|
||||
addr
|
||||
}
|
||||
}
|
||||
|
|
|
@ -293,7 +293,7 @@ impl Scanner {
|
|||
// If we did though, it'd enable bypassing the included burning bug protection
|
||||
debug_assert!(output_key.is_torsion_free());
|
||||
|
||||
let key_offset = shared_key + self.pair.subaddress(subaddress);
|
||||
let key_offset = shared_key + self.pair.subaddress_keys(subaddress);
|
||||
// Since we've found an output to us, get its amount
|
||||
let mut commitment = Commitment::zero();
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ use monero_serai::{
|
|||
Protocol, random_scalar,
|
||||
wallet::{
|
||||
ViewPair, Scanner,
|
||||
address::{Network, AddressType, AddressMeta, MoneroAddress},
|
||||
address::{Network, AddressType, AddressMeta, MoneroAddress, AddressSpec},
|
||||
SpendableOutput,
|
||||
},
|
||||
rpc::Rpc,
|
||||
|
@ -64,7 +64,7 @@ pub async fn get_miner_tx_output(rpc: &Rpc, view: &ViewPair) -> SpendableOutput
|
|||
|
||||
// mine 60 blocks to unlock a miner tx
|
||||
let start = rpc.get_height().await.unwrap();
|
||||
rpc.generate_blocks(&scanner.address().to_string(), 60).await.unwrap();
|
||||
rpc.generate_blocks(&scanner.address(AddressSpec::Standard).to_string(), 60).await.unwrap();
|
||||
|
||||
let block = rpc.get_block(start).await.unwrap();
|
||||
scanner.scan(rpc, &block).await.unwrap().swap_remove(0).ignore_timelock().swap_remove(0)
|
||||
|
@ -151,7 +151,7 @@ macro_rules! test {
|
|||
use monero_serai::{
|
||||
random_scalar,
|
||||
wallet::{
|
||||
address::{Network, AddressMeta, AddressType}, ViewPair, Scanner, SignableTransaction,
|
||||
address::{Network, AddressSpec}, ViewPair, Scanner, SignableTransaction,
|
||||
SignableTransactionBuilder,
|
||||
},
|
||||
};
|
||||
|
@ -185,7 +185,7 @@ macro_rules! test {
|
|||
let rpc = rpc().await;
|
||||
|
||||
let view = ViewPair::new(spend_pub, Zeroizing::new(random_scalar(&mut OsRng)));
|
||||
let addr = view.address(AddressMeta::new(Network::Mainnet, AddressType::Standard));
|
||||
let addr = view.address(Network::Mainnet, AddressSpec::Standard);
|
||||
|
||||
let miner_tx = get_miner_tx_output(&rpc, &view).await;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use rand::RngCore;
|
||||
|
||||
use monero_serai::{transaction::Transaction};
|
||||
use monero_serai::transaction::Transaction;
|
||||
|
||||
mod runner;
|
||||
|
||||
|
@ -8,9 +8,9 @@ test!(
|
|||
scan_standard_address,
|
||||
(
|
||||
|_, mut builder: Builder, _| async move {
|
||||
let scanner =
|
||||
let mut scanner =
|
||||
Scanner::from_view(runner::random_address().1, Network::Mainnet, Some(HashSet::new()));
|
||||
builder.add_payment(scanner.address(), 5);
|
||||
builder.add_payment(scanner.address(AddressSpec::Standard), 5);
|
||||
(builder.build().unwrap(), (scanner,))
|
||||
},
|
||||
|_, tx: Transaction, _, mut state: (Scanner,)| async move {
|
||||
|
@ -27,7 +27,10 @@ test!(
|
|||
let mut scanner =
|
||||
Scanner::from_view(runner::random_address().1, Network::Mainnet, Some(HashSet::new()));
|
||||
let subaddress_index = (0, 1);
|
||||
builder.add_payment(scanner.subaddress(subaddress_index), 5);
|
||||
builder.add_payment(
|
||||
scanner.address(AddressSpec::Subaddress(subaddress_index.0, subaddress_index.1)),
|
||||
5,
|
||||
);
|
||||
(builder.build().unwrap(), (scanner, subaddress_index))
|
||||
},
|
||||
|_, tx: Transaction, _, mut state: (Scanner, (u32, u32))| async move {
|
||||
|
@ -42,12 +45,12 @@ test!(
|
|||
scan_integrated_address,
|
||||
(
|
||||
|_, mut builder: Builder, _| async move {
|
||||
let scanner =
|
||||
let mut scanner =
|
||||
Scanner::from_view(runner::random_address().1, Network::Mainnet, Some(HashSet::new()));
|
||||
let mut payment_id = [0u8; 8];
|
||||
OsRng.fill_bytes(&mut payment_id);
|
||||
|
||||
builder.add_payment(scanner.integrated_address(payment_id), 5);
|
||||
builder.add_payment(scanner.address(AddressSpec::Integrated(payment_id)), 5);
|
||||
(builder.build().unwrap(), (scanner, payment_id))
|
||||
},
|
||||
|_, tx: Transaction, _, mut state: (Scanner, [u8; 8])| async move {
|
||||
|
@ -64,7 +67,7 @@ test!(
|
|||
|_, mut builder: Builder, _| async move {
|
||||
let mut scanner =
|
||||
Scanner::from_view(runner::random_address().1, Network::Mainnet, Some(HashSet::new()));
|
||||
builder.add_payment(scanner.featured_address(None, None, false), 5);
|
||||
builder.add_payment(scanner.address(AddressSpec::Featured(None, None, false)), 5);
|
||||
(builder.build().unwrap(), (scanner,))
|
||||
},
|
||||
|_, tx: Transaction, _, mut state: (Scanner,)| async move {
|
||||
|
@ -81,7 +84,10 @@ test!(
|
|||
let mut scanner =
|
||||
Scanner::from_view(runner::random_address().1, Network::Mainnet, Some(HashSet::new()));
|
||||
let subaddress_index = (0, 2);
|
||||
builder.add_payment(scanner.featured_address(Some(subaddress_index), None, false), 5);
|
||||
builder.add_payment(
|
||||
scanner.address(AddressSpec::Featured(Some(subaddress_index), None, false)),
|
||||
5,
|
||||
);
|
||||
(builder.build().unwrap(), (scanner, subaddress_index))
|
||||
},
|
||||
|_, tx: Transaction, _, mut state: (Scanner, (u32, u32))| async move {
|
||||
|
@ -101,7 +107,7 @@ test!(
|
|||
let mut payment_id = [0u8; 8];
|
||||
OsRng.fill_bytes(&mut payment_id);
|
||||
|
||||
builder.add_payment(scanner.featured_address(None, Some(payment_id), false), 5);
|
||||
builder.add_payment(scanner.address(AddressSpec::Featured(None, Some(payment_id), false)), 5);
|
||||
(builder.build().unwrap(), (scanner, payment_id))
|
||||
},
|
||||
|_, tx: Transaction, _, mut state: (Scanner, [u8; 8])| async move {
|
||||
|
@ -123,8 +129,10 @@ test!(
|
|||
let mut payment_id = [0u8; 8];
|
||||
OsRng.fill_bytes(&mut payment_id);
|
||||
|
||||
builder
|
||||
.add_payment(scanner.featured_address(Some(subaddress_index), Some(payment_id), false), 5);
|
||||
builder.add_payment(
|
||||
scanner.address(AddressSpec::Featured(Some(subaddress_index), Some(payment_id), false)),
|
||||
5,
|
||||
);
|
||||
(builder.build().unwrap(), (scanner, payment_id, subaddress_index))
|
||||
},
|
||||
|_, tx: Transaction, _, mut state: (Scanner, [u8; 8], (u32, u32))| async move {
|
||||
|
@ -140,9 +148,9 @@ test!(
|
|||
scan_guaranteed_standard,
|
||||
(
|
||||
|_, mut builder: Builder, _| async move {
|
||||
let scanner = Scanner::from_view(runner::random_address().1, Network::Mainnet, None);
|
||||
let mut scanner = Scanner::from_view(runner::random_address().1, Network::Mainnet, None);
|
||||
|
||||
builder.add_payment(scanner.address(), 5);
|
||||
builder.add_payment(scanner.address(AddressSpec::Featured(None, None, true)), 5);
|
||||
(builder.build().unwrap(), (scanner,))
|
||||
},
|
||||
|_, tx: Transaction, _, mut state: (Scanner,)| async move {
|
||||
|
@ -159,7 +167,8 @@ test!(
|
|||
let mut scanner = Scanner::from_view(runner::random_address().1, Network::Mainnet, None);
|
||||
let subaddress_index = (0, 1);
|
||||
|
||||
builder.add_payment(scanner.subaddress(subaddress_index), 5);
|
||||
builder
|
||||
.add_payment(scanner.address(AddressSpec::Featured(Some(subaddress_index), None, true)), 5);
|
||||
(builder.build().unwrap(), (scanner, subaddress_index))
|
||||
},
|
||||
|_, tx: Transaction, _, mut state: (Scanner, (u32, u32))| async move {
|
||||
|
@ -174,11 +183,11 @@ test!(
|
|||
scan_guaranteed_integrated,
|
||||
(
|
||||
|_, mut builder: Builder, _| async move {
|
||||
let scanner = Scanner::from_view(runner::random_address().1, Network::Mainnet, None);
|
||||
let mut scanner = Scanner::from_view(runner::random_address().1, Network::Mainnet, None);
|
||||
let mut payment_id = [0u8; 8];
|
||||
OsRng.fill_bytes(&mut payment_id);
|
||||
|
||||
builder.add_payment(scanner.integrated_address(payment_id), 5);
|
||||
builder.add_payment(scanner.address(AddressSpec::Featured(None, Some(payment_id), true)), 5);
|
||||
(builder.build().unwrap(), (scanner, payment_id))
|
||||
},
|
||||
|_, tx: Transaction, _, mut state: (Scanner, [u8; 8])| async move {
|
||||
|
@ -199,8 +208,10 @@ test!(
|
|||
let mut payment_id = [0u8; 8];
|
||||
OsRng.fill_bytes(&mut payment_id);
|
||||
|
||||
builder
|
||||
.add_payment(scanner.featured_address(Some(subaddress_index), Some(payment_id), true), 5);
|
||||
builder.add_payment(
|
||||
scanner.address(AddressSpec::Featured(Some(subaddress_index), Some(payment_id), true)),
|
||||
5,
|
||||
);
|
||||
(builder.build().unwrap(), (scanner, payment_id, subaddress_index))
|
||||
},
|
||||
|_, tx: Transaction, _, mut state: (Scanner, [u8; 8], (u32, u32))| async move {
|
||||
|
|
|
@ -14,7 +14,7 @@ use monero_serai::{
|
|||
rpc::Rpc,
|
||||
wallet::{
|
||||
ViewPair, Scanner,
|
||||
address::{Network, MoneroAddress},
|
||||
address::{Network, MoneroAddress, AddressSpec},
|
||||
Fee, SpendableOutput, SignableTransaction as MSignableTransaction, TransactionMachine,
|
||||
},
|
||||
};
|
||||
|
@ -91,7 +91,7 @@ impl Monero {
|
|||
|
||||
#[cfg(test)]
|
||||
fn empty_address() -> MoneroAddress {
|
||||
Self::empty_scanner().address()
|
||||
Self::empty_scanner().address(AddressSpec::Standard)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ impl Coin for Monero {
|
|||
const MAX_OUTPUTS: usize = 16;
|
||||
|
||||
fn address(&self, key: dfg::EdwardsPoint) -> Self::Address {
|
||||
self.scanner(key).address()
|
||||
self.scanner(key).address(AddressSpec::Featured(None, None, true))
|
||||
}
|
||||
|
||||
async fn get_latest_block_number(&self) -> Result<usize, CoinError> {
|
||||
|
|
Loading…
Reference in a new issue