mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-11 21:49:26 +00:00
Update monero-wallet tests to compile
Some are _consistently_ failing due to the inputs we attempt to spend being too young. I'm unsure what's up with that. Most seem to pass _consistently_, implying it's not a random issue yet some configuration/env aspect.
This commit is contained in:
@@ -32,7 +32,7 @@ async fn select_n<'a, R: RngCore + CryptoRng>(
|
|||||||
fingerprintable_canonical: bool,
|
fingerprintable_canonical: bool,
|
||||||
) -> Result<Vec<(u64, [EdwardsPoint; 2])>, RpcError> {
|
) -> Result<Vec<(u64, [EdwardsPoint; 2])>, RpcError> {
|
||||||
// TODO: consider removing this extra RPC and expect the caller to handle it
|
// TODO: consider removing this extra RPC and expect the caller to handle it
|
||||||
if fingerprintable_canonical && height > rpc.get_height().await? {
|
if fingerprintable_canonical && (height > rpc.get_height().await?) {
|
||||||
// TODO: Don't use InternalError for the caller's failure
|
// TODO: Don't use InternalError for the caller's failure
|
||||||
Err(RpcError::InternalError("decoys being requested from too young blocks"))?;
|
Err(RpcError::InternalError("decoys being requested from too young blocks"))?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,140 +0,0 @@
|
|||||||
use std::sync::{Arc, RwLock};
|
|
||||||
|
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
WalletProtocol, address::MoneroAddress, FeeRate, SpendableOutput, Change, Decoys, SignableTransaction,
|
|
||||||
TransactionError, extra::MAX_ARBITRARY_DATA_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
|
|
||||||
struct SignableTransactionBuilderInternal {
|
|
||||||
protocol: WalletProtocol,
|
|
||||||
fee_rate: FeeRate,
|
|
||||||
r_seed: Option<Zeroizing<[u8; 32]>>,
|
|
||||||
inputs: Vec<(SpendableOutput, Decoys)>,
|
|
||||||
payments: Vec<(MoneroAddress, u64)>,
|
|
||||||
change_address: Change,
|
|
||||||
data: Vec<Vec<u8>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SignableTransactionBuilderInternal {
|
|
||||||
// Takes in the change address so users don't miss that they have to manually set one
|
|
||||||
// If they don't, all leftover funds will become part of the fee
|
|
||||||
fn new(protocol: WalletProtocol, fee_rate: FeeRate, change_address: Change) -> Self {
|
|
||||||
Self {
|
|
||||||
protocol,
|
|
||||||
fee_rate,
|
|
||||||
r_seed: None,
|
|
||||||
inputs: vec![],
|
|
||||||
payments: vec![],
|
|
||||||
change_address,
|
|
||||||
data: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_r_seed(&mut self, r_seed: Zeroizing<[u8; 32]>) {
|
|
||||||
self.r_seed = Some(r_seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_input(&mut self, input: (SpendableOutput, Decoys)) {
|
|
||||||
self.inputs.push(input);
|
|
||||||
}
|
|
||||||
fn add_inputs(&mut self, inputs: &[(SpendableOutput, Decoys)]) {
|
|
||||||
self.inputs.extend(inputs.iter().cloned());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_payment(&mut self, dest: MoneroAddress, amount: u64) {
|
|
||||||
self.payments.push((dest, amount));
|
|
||||||
}
|
|
||||||
fn add_payments(&mut self, payments: &[(MoneroAddress, u64)]) {
|
|
||||||
self.payments.extend(payments);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_data(&mut self, data: Vec<u8>) {
|
|
||||||
self.data.push(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Transaction Builder for Monero transactions.
|
|
||||||
/// All methods provided will modify self while also returning a shallow copy, enabling efficient
|
|
||||||
/// chaining with a clean API.
|
|
||||||
/// In order to fork the builder at some point, clone will still return a deep copy.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct SignableTransactionBuilder(Arc<RwLock<SignableTransactionBuilderInternal>>);
|
|
||||||
impl Clone for SignableTransactionBuilder {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self(Arc::new(RwLock::new((*self.0.read().unwrap()).clone())))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for SignableTransactionBuilder {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
*self.0.read().unwrap() == *other.0.read().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Eq for SignableTransactionBuilder {}
|
|
||||||
|
|
||||||
impl Zeroize for SignableTransactionBuilder {
|
|
||||||
fn zeroize(&mut self) {
|
|
||||||
self.0.write().unwrap().zeroize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SignableTransactionBuilder {
|
|
||||||
fn shallow_copy(&self) -> Self {
|
|
||||||
Self(self.0.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(protocol: WalletProtocol, fee_rate: FeeRate, change_address: Change) -> Self {
|
|
||||||
Self(Arc::new(RwLock::new(SignableTransactionBuilderInternal::new(
|
|
||||||
protocol,
|
|
||||||
fee_rate,
|
|
||||||
change_address,
|
|
||||||
))))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_r_seed(&mut self, r_seed: Zeroizing<[u8; 32]>) -> Self {
|
|
||||||
self.0.write().unwrap().set_r_seed(r_seed);
|
|
||||||
self.shallow_copy()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_input(&mut self, input: (SpendableOutput, Decoys)) -> Self {
|
|
||||||
self.0.write().unwrap().add_input(input);
|
|
||||||
self.shallow_copy()
|
|
||||||
}
|
|
||||||
pub fn add_inputs(&mut self, inputs: &[(SpendableOutput, Decoys)]) -> Self {
|
|
||||||
self.0.write().unwrap().add_inputs(inputs);
|
|
||||||
self.shallow_copy()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_payment(&mut self, dest: MoneroAddress, amount: u64) -> Self {
|
|
||||||
self.0.write().unwrap().add_payment(dest, amount);
|
|
||||||
self.shallow_copy()
|
|
||||||
}
|
|
||||||
pub fn add_payments(&mut self, payments: &[(MoneroAddress, u64)]) -> Self {
|
|
||||||
self.0.write().unwrap().add_payments(payments);
|
|
||||||
self.shallow_copy()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_data(&mut self, data: Vec<u8>) -> Result<Self, TransactionError> {
|
|
||||||
if data.len() > MAX_ARBITRARY_DATA_SIZE {
|
|
||||||
Err(TransactionError::TooMuchData)?;
|
|
||||||
}
|
|
||||||
self.0.write().unwrap().add_data(data);
|
|
||||||
Ok(self.shallow_copy())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build(self) -> Result<SignableTransaction, TransactionError> {
|
|
||||||
let read = self.0.read().unwrap();
|
|
||||||
SignableTransaction::new(
|
|
||||||
read.protocol,
|
|
||||||
read.r_seed.clone(),
|
|
||||||
read.inputs.clone(),
|
|
||||||
read.payments.clone(),
|
|
||||||
&read.change_address,
|
|
||||||
read.data.clone(),
|
|
||||||
read.fee_rate,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -321,11 +321,27 @@ impl SignableTransaction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = SignableTransaction { rct_type, sender_view_key, inputs, payments, data, fee_rate };
|
let mut res =
|
||||||
|
SignableTransaction { rct_type, sender_view_key, inputs, payments, data, fee_rate };
|
||||||
res.validate()?;
|
res.validate()?;
|
||||||
|
|
||||||
|
// Shuffle the payments
|
||||||
|
{
|
||||||
|
let mut rng = res.seeded_rng(b"shuffle_payments");
|
||||||
|
res.payments.shuffle(&mut rng);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fee_rate(&self) -> FeeRate {
|
||||||
|
self.fee_rate
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fee(&self) -> u64 {
|
||||||
|
self.weight_and_fee().1
|
||||||
|
}
|
||||||
|
|
||||||
pub fn write<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
|
pub fn write<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
|
||||||
fn write_input<W: io::Write>(input: &(SpendableOutput, Decoys), w: &mut W) -> io::Result<()> {
|
fn write_input<W: io::Write>(input: &(SpendableOutput, Decoys), w: &mut W) -> io::Result<()> {
|
||||||
input.0.write(w)?;
|
input.0.write(w)?;
|
||||||
@@ -424,12 +440,6 @@ impl SignableTransaction {
|
|||||||
key_images.push(key_image);
|
key_images.push(key_image);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shuffle the payments
|
|
||||||
{
|
|
||||||
let mut rng = self.seeded_rng(b"shuffle_payments");
|
|
||||||
self.payments.shuffle(&mut rng);
|
|
||||||
}
|
|
||||||
|
|
||||||
SignableTransactionWithKeyImages { intent: self, key_images }
|
SignableTransactionWithKeyImages { intent: self, key_images }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use monero_serai::transaction::Transaction;
|
use monero_serai::transaction::Transaction;
|
||||||
use monero_wallet::{rpc::Rpc, TransactionError, extra::MAX_ARBITRARY_DATA_SIZE};
|
use monero_wallet::{rpc::Rpc, extra::MAX_ARBITRARY_DATA_SIZE, send::SendError};
|
||||||
|
|
||||||
mod runner;
|
mod runner;
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ test!(
|
|||||||
let mut data = vec![b'a'; MAX_ARBITRARY_DATA_SIZE + 1];
|
let mut data = vec![b'a'; MAX_ARBITRARY_DATA_SIZE + 1];
|
||||||
|
|
||||||
// Make sure we get an error if we try to add it to the TX
|
// Make sure we get an error if we try to add it to the TX
|
||||||
assert_eq!(builder.add_data(data.clone()), Err(TransactionError::TooMuchData));
|
assert_eq!(builder.add_data(data.clone()), Err(SendError::TooMuchData));
|
||||||
|
|
||||||
// Reduce data size and retry. The data will now be 255 bytes long (including the added
|
// Reduce data size and retry. The data will now be 255 bytes long (including the added
|
||||||
// marker), exactly
|
// marker), exactly
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use monero_wallet::{
|
|||||||
DEFAULT_LOCK_WINDOW,
|
DEFAULT_LOCK_WINDOW,
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
rpc::{OutputResponse, Rpc},
|
rpc::{OutputResponse, Rpc},
|
||||||
SpendableOutput,
|
scan::SpendableOutput,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod runner;
|
mod runner;
|
||||||
@@ -24,12 +24,12 @@ test!(
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
// Then make a second tx1
|
// Then make a second tx1
|
||||||
|protocol: Protocol, rpc: SimpleRequestRpc, mut builder: Builder, addr, state: _| async move {
|
|rct_type: RctType, rpc: SimpleRequestRpc, mut builder: Builder, addr, state: _| async move {
|
||||||
let output_tx0: SpendableOutput = state;
|
let output_tx0: SpendableOutput = state;
|
||||||
let decoys = Decoys::fingerprintable_canonical_select(
|
let decoys = Decoys::fingerprintable_canonical_select(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
&rpc,
|
&rpc,
|
||||||
protocol.ring_len(),
|
ring_len(rct_type),
|
||||||
rpc.get_height().await.unwrap(),
|
rpc.get_height().await.unwrap(),
|
||||||
&[output_tx0.clone()],
|
&[output_tx0.clone()],
|
||||||
)
|
)
|
||||||
@@ -40,7 +40,7 @@ test!(
|
|||||||
builder.add_inputs(&inputs);
|
builder.add_inputs(&inputs);
|
||||||
builder.add_payment(addr, 1000000000000);
|
builder.add_payment(addr, 1000000000000);
|
||||||
|
|
||||||
(builder.build().unwrap(), (protocol, output_tx0))
|
(builder.build().unwrap(), (rct_type, output_tx0))
|
||||||
},
|
},
|
||||||
// Then make sure DSA selects freshly unlocked output from tx1 as a decoy
|
// Then make sure DSA selects freshly unlocked output from tx1 as a decoy
|
||||||
|rpc: SimpleRequestRpc, tx: Transaction, mut scanner: Scanner, state: (_, _)| async move {
|
|rpc: SimpleRequestRpc, tx: Transaction, mut scanner: Scanner, state: (_, _)| async move {
|
||||||
@@ -61,14 +61,14 @@ test!(
|
|||||||
|
|
||||||
// Select decoys using spendable output from tx0 as the real, and make sure DSA selects
|
// Select decoys using spendable output from tx0 as the real, and make sure DSA selects
|
||||||
// the freshly unlocked output from tx1 as a decoy
|
// the freshly unlocked output from tx1 as a decoy
|
||||||
let (protocol, output_tx0): (Protocol, SpendableOutput) = state;
|
let (rct_type, output_tx0): (RctType, SpendableOutput) = state;
|
||||||
let mut selected_fresh_decoy = false;
|
let mut selected_fresh_decoy = false;
|
||||||
let mut attempts = 1000;
|
let mut attempts = 1000;
|
||||||
while !selected_fresh_decoy && attempts > 0 {
|
while !selected_fresh_decoy && attempts > 0 {
|
||||||
let decoys = Decoys::fingerprintable_canonical_select(
|
let decoys = Decoys::fingerprintable_canonical_select(
|
||||||
&mut OsRng, // TODO: use a seeded RNG to consistently select the latest output
|
&mut OsRng, // TODO: use a seeded RNG to consistently select the latest output
|
||||||
&rpc,
|
&rpc,
|
||||||
protocol.ring_len(),
|
ring_len(rct_type),
|
||||||
height,
|
height,
|
||||||
&[output_tx0.clone()],
|
&[output_tx0.clone()],
|
||||||
)
|
)
|
||||||
@@ -101,12 +101,12 @@ test!(
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
// Then make a second tx1
|
// Then make a second tx1
|
||||||
|protocol: Protocol, rpc: SimpleRequestRpc, mut builder: Builder, addr, state: _| async move {
|
|rct_type: RctType, rpc: SimpleRequestRpc, mut builder: Builder, addr, state: _| async move {
|
||||||
let output_tx0: SpendableOutput = state;
|
let output_tx0: SpendableOutput = state;
|
||||||
let decoys = Decoys::select(
|
let decoys = Decoys::select(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
&rpc,
|
&rpc,
|
||||||
protocol.ring_len(),
|
ring_len(rct_type),
|
||||||
rpc.get_height().await.unwrap(),
|
rpc.get_height().await.unwrap(),
|
||||||
&[output_tx0.clone()],
|
&[output_tx0.clone()],
|
||||||
)
|
)
|
||||||
@@ -117,7 +117,7 @@ test!(
|
|||||||
builder.add_inputs(&inputs);
|
builder.add_inputs(&inputs);
|
||||||
builder.add_payment(addr, 1000000000000);
|
builder.add_payment(addr, 1000000000000);
|
||||||
|
|
||||||
(builder.build().unwrap(), (protocol, output_tx0))
|
(builder.build().unwrap(), (rct_type, output_tx0))
|
||||||
},
|
},
|
||||||
// Then make sure DSA selects freshly unlocked output from tx1 as a decoy
|
// Then make sure DSA selects freshly unlocked output from tx1 as a decoy
|
||||||
|rpc: SimpleRequestRpc, tx: Transaction, mut scanner: Scanner, state: (_, _)| async move {
|
|rpc: SimpleRequestRpc, tx: Transaction, mut scanner: Scanner, state: (_, _)| async move {
|
||||||
@@ -138,14 +138,14 @@ test!(
|
|||||||
|
|
||||||
// Select decoys using spendable output from tx0 as the real, and make sure DSA selects
|
// Select decoys using spendable output from tx0 as the real, and make sure DSA selects
|
||||||
// the freshly unlocked output from tx1 as a decoy
|
// the freshly unlocked output from tx1 as a decoy
|
||||||
let (protocol, output_tx0): (Protocol, SpendableOutput) = state;
|
let (rct_type, output_tx0): (RctType, SpendableOutput) = state;
|
||||||
let mut selected_fresh_decoy = false;
|
let mut selected_fresh_decoy = false;
|
||||||
let mut attempts = 1000;
|
let mut attempts = 1000;
|
||||||
while !selected_fresh_decoy && attempts > 0 {
|
while !selected_fresh_decoy && attempts > 0 {
|
||||||
let decoys = Decoys::select(
|
let decoys = Decoys::select(
|
||||||
&mut OsRng, // TODO: use a seeded RNG to consistently select the latest output
|
&mut OsRng, // TODO: use a seeded RNG to consistently select the latest output
|
||||||
&rpc,
|
&rpc,
|
||||||
protocol.ring_len(),
|
ring_len(rct_type),
|
||||||
height,
|
height,
|
||||||
&[output_tx0.clone()],
|
&[output_tx0.clone()],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
|
|||||||
use monero_serai::transaction::Transaction;
|
use monero_serai::transaction::Transaction;
|
||||||
use monero_wallet::{
|
use monero_wallet::{
|
||||||
rpc::Rpc,
|
rpc::Rpc,
|
||||||
Eventuality,
|
|
||||||
address::{AddressType, AddressMeta, MoneroAddress},
|
address::{AddressType, AddressMeta, MoneroAddress},
|
||||||
|
send::Eventuality,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod runner;
|
mod runner;
|
||||||
@@ -50,9 +50,8 @@ test!(
|
|||||||
),
|
),
|
||||||
4,
|
4,
|
||||||
);
|
);
|
||||||
builder.set_r_seed(Zeroizing::new([0xbb; 32]));
|
|
||||||
let tx = builder.build().unwrap();
|
let tx = builder.build().unwrap();
|
||||||
let eventuality = tx.eventuality().unwrap();
|
let eventuality = Eventuality::from(tx.clone());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
eventuality,
|
eventuality,
|
||||||
Eventuality::read::<&[u8]>(&mut eventuality.serialize().as_ref()).unwrap()
|
Eventuality::read::<&[u8]>(&mut eventuality.serialize().as_ref()).unwrap()
|
||||||
|
|||||||
85
coins/monero/wallet/tests/runner/builder.rs
Normal file
85
coins/monero/wallet/tests/runner/builder.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
use zeroize::{Zeroize, Zeroizing};
|
||||||
|
|
||||||
|
use curve25519_dalek::Scalar;
|
||||||
|
|
||||||
|
use monero_wallet::{
|
||||||
|
primitives::Decoys,
|
||||||
|
ringct::RctType,
|
||||||
|
rpc::FeeRate,
|
||||||
|
address::MoneroAddress,
|
||||||
|
scan::SpendableOutput,
|
||||||
|
send::{Change, SendError, SignableTransaction},
|
||||||
|
extra::MAX_ARBITRARY_DATA_SIZE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A builder for Monero transactions.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Zeroize, Debug)]
|
||||||
|
pub struct SignableTransactionBuilder {
|
||||||
|
rct_type: RctType,
|
||||||
|
sender_view_key: Zeroizing<Scalar>,
|
||||||
|
inputs: Vec<(SpendableOutput, Decoys)>,
|
||||||
|
payments: Vec<(MoneroAddress, u64)>,
|
||||||
|
change: Change,
|
||||||
|
data: Vec<Vec<u8>>,
|
||||||
|
fee_rate: FeeRate,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SignableTransactionBuilder {
|
||||||
|
pub fn new(
|
||||||
|
rct_type: RctType,
|
||||||
|
sender_view_key: Zeroizing<Scalar>,
|
||||||
|
change: Change,
|
||||||
|
fee_rate: FeeRate,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
rct_type,
|
||||||
|
sender_view_key,
|
||||||
|
inputs: vec![],
|
||||||
|
payments: vec![],
|
||||||
|
change,
|
||||||
|
data: vec![],
|
||||||
|
fee_rate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_input(&mut self, input: (SpendableOutput, Decoys)) -> &mut Self {
|
||||||
|
self.inputs.push(input);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn add_inputs(&mut self, inputs: &[(SpendableOutput, Decoys)]) -> &mut Self {
|
||||||
|
self.inputs.extend(inputs.iter().cloned());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_payment(&mut self, dest: MoneroAddress, amount: u64) -> &mut Self {
|
||||||
|
self.payments.push((dest, amount));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn add_payments(&mut self, payments: &[(MoneroAddress, u64)]) -> &mut Self {
|
||||||
|
self.payments.extend(payments);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn add_data(&mut self, data: Vec<u8>) -> Result<&mut Self, SendError> {
|
||||||
|
if data.len() > MAX_ARBITRARY_DATA_SIZE {
|
||||||
|
Err(SendError::TooMuchData)?;
|
||||||
|
}
|
||||||
|
self.data.push(data);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Result<SignableTransaction, SendError> {
|
||||||
|
SignableTransaction::new(
|
||||||
|
self.rct_type,
|
||||||
|
self.sender_view_key,
|
||||||
|
self.inputs,
|
||||||
|
self.payments,
|
||||||
|
self.change,
|
||||||
|
self.data,
|
||||||
|
self.fee_rate,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,13 +10,25 @@ use tokio::sync::Mutex;
|
|||||||
|
|
||||||
use monero_simple_request_rpc::SimpleRequestRpc;
|
use monero_simple_request_rpc::SimpleRequestRpc;
|
||||||
use monero_wallet::{
|
use monero_wallet::{
|
||||||
|
ringct::RctType,
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
rpc::Rpc,
|
rpc::{Rpc, FeeRate},
|
||||||
ViewPair, Scanner,
|
ViewPair,
|
||||||
address::{Network, AddressType, AddressSpec, AddressMeta, MoneroAddress},
|
address::{Network, AddressType, AddressSpec, AddressMeta, MoneroAddress},
|
||||||
SpendableOutput, FeeRate,
|
scan::{SpendableOutput, Scanner},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod builder;
|
||||||
|
pub use builder::SignableTransactionBuilder;
|
||||||
|
|
||||||
|
pub fn ring_len(rct_type: RctType) -> usize {
|
||||||
|
match rct_type {
|
||||||
|
RctType::ClsagBulletproof => 11,
|
||||||
|
RctType::ClsagBulletproofPlus => 16,
|
||||||
|
_ => panic!("ring size unknown for RctType"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn random_address() -> (Scalar, ViewPair, MoneroAddress) {
|
pub fn random_address() -> (Scalar, ViewPair, MoneroAddress) {
|
||||||
let spend = Scalar::random(&mut OsRng);
|
let spend = Scalar::random(&mut OsRng);
|
||||||
let spend_pub = &spend * ED25519_BASEPOINT_TABLE;
|
let spend_pub = &spend * ED25519_BASEPOINT_TABLE;
|
||||||
@@ -56,7 +68,7 @@ pub async fn mine_until_unlocked(rpc: &SimpleRequestRpc, addr: &str, tx_hash: [u
|
|||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.all(|output| output.is_some())
|
.any(|output| output.is_none())
|
||||||
{
|
{
|
||||||
height = rpc.generate_blocks(addr, 1).await.unwrap().1 + 1;
|
height = rpc.generate_blocks(addr, 1).await.unwrap().1 + 1;
|
||||||
}
|
}
|
||||||
@@ -159,8 +171,6 @@ macro_rules! test {
|
|||||||
|
|
||||||
use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar};
|
use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar};
|
||||||
|
|
||||||
#[cfg(feature = "multisig")]
|
|
||||||
use transcript::{Transcript, RecommendedTranscript};
|
|
||||||
#[cfg(feature = "multisig")]
|
#[cfg(feature = "multisig")]
|
||||||
use frost::{
|
use frost::{
|
||||||
curve::Ed25519,
|
curve::Ed25519,
|
||||||
@@ -169,15 +179,19 @@ macro_rules! test {
|
|||||||
};
|
};
|
||||||
|
|
||||||
use monero_wallet::{
|
use monero_wallet::{
|
||||||
Protocol,
|
primitives::Decoys,
|
||||||
|
ringct::RctType,
|
||||||
|
rpc::FeePriority,
|
||||||
address::{Network, AddressSpec},
|
address::{Network, AddressSpec},
|
||||||
ViewPair, Scanner, Change, DecoySelection, Decoys, FeePriority,
|
ViewPair,
|
||||||
SignableTransaction, SignableTransactionBuilder,
|
DecoySelection,
|
||||||
|
scan::Scanner,
|
||||||
|
send::{Change, SignableTransaction},
|
||||||
};
|
};
|
||||||
|
|
||||||
use runner::{
|
use runner::{
|
||||||
random_address, rpc, mine_until_unlocked, get_miner_tx_output,
|
SignableTransactionBuilder, ring_len, random_address, rpc, mine_until_unlocked,
|
||||||
check_weight_and_fee,
|
get_miner_tx_output, check_weight_and_fee,
|
||||||
};
|
};
|
||||||
|
|
||||||
type Builder = SignableTransactionBuilder;
|
type Builder = SignableTransactionBuilder;
|
||||||
@@ -206,16 +220,21 @@ macro_rules! test {
|
|||||||
|
|
||||||
let rpc = rpc().await;
|
let rpc = rpc().await;
|
||||||
|
|
||||||
let view = ViewPair::new(spend_pub, Zeroizing::new(Scalar::random(&mut OsRng)));
|
let view_priv = Zeroizing::new(Scalar::random(&mut OsRng));
|
||||||
|
let view = ViewPair::new(spend_pub, view_priv.clone());
|
||||||
let addr = view.address(Network::Mainnet, AddressSpec::Standard);
|
let addr = view.address(Network::Mainnet, AddressSpec::Standard);
|
||||||
|
|
||||||
let miner_tx = get_miner_tx_output(&rpc, &view).await;
|
let miner_tx = get_miner_tx_output(&rpc, &view).await;
|
||||||
|
|
||||||
let protocol = Protocol::try_from(rpc.get_protocol().await.unwrap()).unwrap();
|
let rct_type = match rpc.get_hardfork_version().await.unwrap() {
|
||||||
|
14 => RctType::ClsagBulletproof,
|
||||||
|
15 | 16 => RctType::ClsagBulletproofPlus,
|
||||||
|
_ => panic!("unrecognized hardfork version"),
|
||||||
|
};
|
||||||
|
|
||||||
let builder = SignableTransactionBuilder::new(
|
let builder = SignableTransactionBuilder::new(
|
||||||
protocol,
|
rct_type,
|
||||||
rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
|
view_priv,
|
||||||
Change::new(
|
Change::new(
|
||||||
&ViewPair::new(
|
&ViewPair::new(
|
||||||
&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
|
&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
|
||||||
@@ -223,6 +242,7 @@ macro_rules! test {
|
|||||||
),
|
),
|
||||||
false
|
false
|
||||||
),
|
),
|
||||||
|
rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let sign = |tx: SignableTransaction| {
|
let sign = |tx: SignableTransaction| {
|
||||||
@@ -239,16 +259,7 @@ macro_rules! test {
|
|||||||
{
|
{
|
||||||
let mut machines = HashMap::new();
|
let mut machines = HashMap::new();
|
||||||
for i in (1 ..= THRESHOLD).map(|i| Participant::new(i).unwrap()) {
|
for i in (1 ..= THRESHOLD).map(|i| Participant::new(i).unwrap()) {
|
||||||
machines.insert(
|
machines.insert(i, tx.clone().multisig(&keys[&i]).unwrap());
|
||||||
i,
|
|
||||||
tx
|
|
||||||
.clone()
|
|
||||||
.multisig(
|
|
||||||
&keys[&i],
|
|
||||||
RecommendedTranscript::new(b"Monero Serai Test Transaction"),
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
frost::tests::sign_without_caching(&mut OsRng, machines, &[])
|
frost::tests::sign_without_caching(&mut OsRng, machines, &[])
|
||||||
@@ -266,7 +277,7 @@ macro_rules! test {
|
|||||||
let decoys = Decoys::fingerprintable_canonical_select(
|
let decoys = Decoys::fingerprintable_canonical_select(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
&rpc,
|
&rpc,
|
||||||
protocol.ring_len(),
|
ring_len(rct_type),
|
||||||
rpc.get_height().await.unwrap(),
|
rpc.get_height().await.unwrap(),
|
||||||
&[miner_tx.clone()],
|
&[miner_tx.clone()],
|
||||||
)
|
)
|
||||||
@@ -290,7 +301,7 @@ macro_rules! test {
|
|||||||
|
|
||||||
$(
|
$(
|
||||||
let (tx, state) = ($tx)(
|
let (tx, state) = ($tx)(
|
||||||
protocol,
|
rct_type,
|
||||||
rpc.clone(),
|
rpc.clone(),
|
||||||
builder.clone(),
|
builder.clone(),
|
||||||
next_addr,
|
next_addr,
|
||||||
|
|||||||
@@ -2,15 +2,22 @@ use rand_core::OsRng;
|
|||||||
|
|
||||||
use monero_simple_request_rpc::SimpleRequestRpc;
|
use monero_simple_request_rpc::SimpleRequestRpc;
|
||||||
use monero_wallet::{
|
use monero_wallet::{
|
||||||
transaction::Transaction, Protocol, rpc::Rpc, extra::Extra, address::SubaddressIndex,
|
primitives::Decoys,
|
||||||
ReceivedOutput, SpendableOutput, DecoySelection, Decoys, SignableTransactionBuilder,
|
ringct::RctType,
|
||||||
|
transaction::Transaction,
|
||||||
|
rpc::Rpc,
|
||||||
|
address::SubaddressIndex,
|
||||||
|
extra::Extra,
|
||||||
|
scan::{ReceivedOutput, SpendableOutput},
|
||||||
|
DecoySelection,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod runner;
|
mod runner;
|
||||||
|
use runner::{SignableTransactionBuilder, ring_len};
|
||||||
|
|
||||||
// Set up inputs, select decoys, then add them to the TX builder
|
// Set up inputs, select decoys, then add them to the TX builder
|
||||||
async fn add_inputs(
|
async fn add_inputs(
|
||||||
protocol: Protocol,
|
rct_type: RctType,
|
||||||
rpc: &SimpleRequestRpc,
|
rpc: &SimpleRequestRpc,
|
||||||
outputs: Vec<ReceivedOutput>,
|
outputs: Vec<ReceivedOutput>,
|
||||||
builder: &mut SignableTransactionBuilder,
|
builder: &mut SignableTransactionBuilder,
|
||||||
@@ -23,7 +30,7 @@ async fn add_inputs(
|
|||||||
let decoys = Decoys::fingerprintable_canonical_select(
|
let decoys = Decoys::fingerprintable_canonical_select(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
rpc,
|
rpc,
|
||||||
protocol.ring_len(),
|
ring_len(rct_type),
|
||||||
rpc.get_height().await.unwrap(),
|
rpc.get_height().await.unwrap(),
|
||||||
&spendable_outputs,
|
&spendable_outputs,
|
||||||
)
|
)
|
||||||
@@ -66,8 +73,8 @@ test!(
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|protocol: Protocol, rpc, mut builder: Builder, addr, outputs: Vec<ReceivedOutput>| async move {
|
|rct_type: RctType, rpc, mut builder: Builder, addr, outputs: Vec<ReceivedOutput>| async move {
|
||||||
add_inputs(protocol, &rpc, outputs, &mut builder).await;
|
add_inputs(rct_type, &rpc, outputs, &mut builder).await;
|
||||||
builder.add_payment(addr, 6);
|
builder.add_payment(addr, 6);
|
||||||
(builder.build().unwrap(), ())
|
(builder.build().unwrap(), ())
|
||||||
},
|
},
|
||||||
@@ -96,20 +103,20 @@ test!(
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|protocol, rpc: SimpleRequestRpc, _, _, outputs: Vec<ReceivedOutput>| async move {
|
|rct_type, rpc: SimpleRequestRpc, _, _, outputs: Vec<ReceivedOutput>| async move {
|
||||||
use monero_wallet::FeePriority;
|
use monero_wallet::rpc::FeePriority;
|
||||||
|
|
||||||
let change_view = ViewPair::new(
|
let view_priv = Zeroizing::new(Scalar::random(&mut OsRng));
|
||||||
&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
|
let change_view =
|
||||||
Zeroizing::new(Scalar::random(&mut OsRng)),
|
ViewPair::new(&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE, view_priv.clone());
|
||||||
);
|
|
||||||
|
|
||||||
let mut builder = SignableTransactionBuilder::new(
|
let mut builder = SignableTransactionBuilder::new(
|
||||||
protocol,
|
rct_type,
|
||||||
rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
|
view_priv,
|
||||||
Change::new(&change_view, false),
|
Change::new(&change_view, false),
|
||||||
|
rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
|
||||||
);
|
);
|
||||||
add_inputs(protocol, &rpc, vec![outputs.first().unwrap().clone()], &mut builder).await;
|
add_inputs(rct_type, &rpc, vec![outputs.first().unwrap().clone()], &mut builder).await;
|
||||||
|
|
||||||
// Send to a subaddress
|
// Send to a subaddress
|
||||||
let sub_view = ViewPair::new(
|
let sub_view = ViewPair::new(
|
||||||
@@ -161,8 +168,8 @@ test!(
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|protocol: Protocol, rpc, mut builder: Builder, addr, outputs: Vec<ReceivedOutput>| async move {
|
|rct_type: RctType, rpc, mut builder: Builder, addr, outputs: Vec<ReceivedOutput>| async move {
|
||||||
add_inputs(protocol, &rpc, outputs, &mut builder).await;
|
add_inputs(rct_type, &rpc, outputs, &mut builder).await;
|
||||||
builder.add_payment(addr, 2);
|
builder.add_payment(addr, 2);
|
||||||
(builder.build().unwrap(), ())
|
(builder.build().unwrap(), ())
|
||||||
},
|
},
|
||||||
@@ -188,8 +195,8 @@ test!(
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|protocol: Protocol, rpc, mut builder: Builder, addr, outputs: Vec<ReceivedOutput>| async move {
|
|rct_type: RctType, rpc, mut builder: Builder, addr, outputs: Vec<ReceivedOutput>| async move {
|
||||||
add_inputs(protocol, &rpc, outputs, &mut builder).await;
|
add_inputs(rct_type, &rpc, outputs, &mut builder).await;
|
||||||
|
|
||||||
for i in 0 .. 15 {
|
for i in 0 .. 15 {
|
||||||
builder.add_payment(addr, i + 1);
|
builder.add_payment(addr, i + 1);
|
||||||
@@ -228,8 +235,8 @@ test!(
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|protocol: Protocol, rpc, mut builder: Builder, _, outputs: Vec<ReceivedOutput>| async move {
|
|rct_type: RctType, rpc, mut builder: Builder, _, outputs: Vec<ReceivedOutput>| async move {
|
||||||
add_inputs(protocol, &rpc, outputs, &mut builder).await;
|
add_inputs(rct_type, &rpc, outputs, &mut builder).await;
|
||||||
|
|
||||||
let view = runner::random_address().1;
|
let view = runner::random_address().1;
|
||||||
let mut scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
|
let mut scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
|
||||||
@@ -285,15 +292,16 @@ test!(
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|protocol, rpc: SimpleRequestRpc, _, addr, outputs: Vec<ReceivedOutput>| async move {
|
|rct_type, rpc: SimpleRequestRpc, _, addr, outputs: Vec<ReceivedOutput>| async move {
|
||||||
use monero_wallet::FeePriority;
|
use monero_wallet::rpc::FeePriority;
|
||||||
|
|
||||||
let mut builder = SignableTransactionBuilder::new(
|
let mut builder = SignableTransactionBuilder::new(
|
||||||
protocol,
|
rct_type,
|
||||||
rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
|
Zeroizing::new(Scalar::random(&mut OsRng)),
|
||||||
Change::fingerprintable(None),
|
Change::fingerprintable(None),
|
||||||
|
rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
|
||||||
);
|
);
|
||||||
add_inputs(protocol, &rpc, vec![outputs.first().unwrap().clone()], &mut builder).await;
|
add_inputs(rct_type, &rpc, vec![outputs.first().unwrap().clone()], &mut builder).await;
|
||||||
builder.add_payment(addr, 10000);
|
builder.add_payment(addr, 10000);
|
||||||
builder.add_payment(addr, 50000);
|
builder.add_payment(addr, 50000);
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use monero_wallet::{
|
|||||||
rpc::Rpc,
|
rpc::Rpc,
|
||||||
address::{Network, AddressSpec, SubaddressIndex, MoneroAddress},
|
address::{Network, AddressSpec, SubaddressIndex, MoneroAddress},
|
||||||
extra::{MAX_TX_EXTRA_NONCE_SIZE, Extra, PaymentId},
|
extra::{MAX_TX_EXTRA_NONCE_SIZE, Extra, PaymentId},
|
||||||
Scanner,
|
scan::Scanner,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod runner;
|
mod runner;
|
||||||
|
|||||||
Reference in New Issue
Block a user