use dusk_core::signatures::bls::{
PublicKey as AccountPublicKey, SecretKey as AccountSecretKey,
};
use dusk_core::transfer::data::{ContractCall, TransactionData, MAX_MEMO_SIZE};
use dusk_core::transfer::phoenix::{
Note, NoteOpening, NoteTreeItem, NotesTree, Prove,
PublicKey as PhoenixPublicKey, SecretKey as PhoenixSecretKey, TxCircuitVec,
};
use dusk_core::transfer::Transaction;
use dusk_core::{Error, JubJubScalar};
use ff::Field;
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
const CHAIN_ID: u8 = 0xFA;
const NOTE_VALUE: u64 = 42;
const GAS_LIMIT: u64 = 1;
const GAS_PRICE: u64 = 1;
struct NoProver;
impl Prove for NoProver {
fn prove(&self, tx_circuit_vec_bytes: &[u8]) -> Result<Vec<u8>, Error> {
Ok(TxCircuitVec::from_slice(tx_circuit_vec_bytes)
.expect("serialization should be ok")
.to_var_bytes()
.to_vec())
}
}
fn new_phoenix_tx<const I: usize>(
transfer_value: u64,
deposit: u64,
data: Option<impl Into<TransactionData>>,
) -> Result<Transaction, Error> {
let mut rng = StdRng::seed_from_u64(42);
let sender_sk = PhoenixSecretKey::random(&mut rng);
let sender_pk = PhoenixPublicKey::from(&sender_sk);
let refund_pk = &sender_pk;
let receiver_pk =
PhoenixPublicKey::from(&PhoenixSecretKey::random(&mut rng));
let mut notes_tree = NotesTree::new();
let input_notes: Vec<Note> = (0..I)
.map(|i| {
let value_blinder = JubJubScalar::random(&mut rng);
let sender_blinder = [
JubJubScalar::random(&mut rng),
JubJubScalar::random(&mut rng),
];
let mut note = Note::obfuscated(
&mut rng,
&sender_pk,
&sender_pk,
NOTE_VALUE,
value_blinder,
sender_blinder,
);
note.set_pos(i as u64);
notes_tree.insert(
*note.pos(),
NoteTreeItem {
hash: note.hash(),
data: (),
},
);
note
})
.collect();
let inputs: Vec<(Note, NoteOpening)> = input_notes
.into_iter()
.enumerate()
.map(|(i, note)| {
let opening = notes_tree
.opening(i as u64)
.expect("there should be a note at the given position");
(note, opening)
})
.collect();
let root = notes_tree.root().hash;
Transaction::phoenix(
&mut rng,
&sender_sk,
refund_pk,
&receiver_pk,
inputs,
root,
transfer_value,
false,
deposit,
GAS_LIMIT,
GAS_PRICE,
CHAIN_ID,
data,
&NoProver,
)
}
#[test]
fn phoenix_1_2() {
const I: usize = 1;
let transfer_value = NOTE_VALUE - GAS_LIMIT * GAS_PRICE;
let deposit = 0;
let data: Option<TransactionData> = None;
assert!(new_phoenix_tx::<I>(transfer_value, deposit, data).is_ok());
}
#[test]
fn phoenix_2_2() {
const I: usize = 2;
let transfer_value = NOTE_VALUE - GAS_LIMIT * GAS_PRICE;
let deposit = NOTE_VALUE;
let data = Some(ContractCall::new([0u8; 32], "some fn name"));
assert!(new_phoenix_tx::<I>(transfer_value, deposit, data).is_ok());
}
#[test]
fn phoenix_3_2() {
const I: usize = 3;
let transfer_value = 3 * NOTE_VALUE - GAS_LIMIT * GAS_PRICE;
let deposit = 0;
let data = Some(String::from("Some memo"));
assert!(new_phoenix_tx::<I>(transfer_value, deposit, data).is_ok());
}
#[test]
fn phoenix_4_2() {
const I: usize = 4;
let transfer_value = 3 * NOTE_VALUE;
let deposit = 0;
let data: Option<TransactionData> = None;
assert!(new_phoenix_tx::<I>(transfer_value, deposit, data).is_ok());
}
#[test]
fn phoenix_insufficient_balance() {
const I: usize = 4;
let transfer_value = 3 * NOTE_VALUE;
let deposit = NOTE_VALUE;
let data: Option<TransactionData> = None;
assert_eq!(
new_phoenix_tx::<I>(transfer_value, deposit, data).unwrap_err(),
Error::InsufficientBalance
);
}
#[test]
fn phoenix_no_inputs() {
const I: usize = 0;
let transfer_value = 0;
let deposit = 0;
let data: Option<TransactionData> = None;
assert_eq!(
new_phoenix_tx::<I>(transfer_value, deposit, data).unwrap_err(),
Error::InsufficientBalance
);
}
#[test]
fn phoenix_ownership() {
let mut rng = StdRng::seed_from_u64(42);
let sender_sk = PhoenixSecretKey::random(&mut rng);
let sender_pk = PhoenixPublicKey::from(&sender_sk);
let refund_pk = &sender_pk;
let receiver_sk = PhoenixSecretKey::random(&mut rng);
let receiver_pk = PhoenixPublicKey::from(&receiver_sk);
let mut notes_tree = NotesTree::new();
let value_blinder = JubJubScalar::random(&mut rng);
let sender_blinder = [
JubJubScalar::random(&mut rng),
JubJubScalar::random(&mut rng),
];
let mut note = Note::obfuscated(
&mut rng,
&sender_pk,
&receiver_pk,
NOTE_VALUE,
value_blinder,
sender_blinder,
);
note.set_pos(0);
notes_tree.insert(
*note.pos(),
NoteTreeItem {
hash: note.hash(),
data: (),
},
);
let opening = notes_tree
.opening(0)
.expect("there should be a note at the given position");
let inputs = vec![(note, opening)];
let root = notes_tree.root().hash;
let transfer_value = NOTE_VALUE - 10;
let deposit = 0;
let data: Option<TransactionData> = None;
assert_eq!(
Transaction::phoenix(
&mut rng,
&sender_sk,
refund_pk,
&receiver_pk,
inputs,
root,
transfer_value,
false,
deposit,
GAS_LIMIT,
GAS_PRICE,
CHAIN_ID,
data,
&NoProver,
)
.unwrap_err(),
Error::PhoenixOwnership
);
}
#[test]
fn phoenix_memo_too_large() {
const I: usize = 1;
const MEMO_SIZE: usize = MAX_MEMO_SIZE + 1;
let transfer_value = 0;
let deposit = 0;
let data = Some(vec![1; MEMO_SIZE]);
assert_eq!(
new_phoenix_tx::<I>(transfer_value, deposit, data).unwrap_err(),
Error::MemoTooLarge(MEMO_SIZE)
);
}
fn new_moonlight_tx(
data: Option<impl Into<TransactionData>>,
) -> Result<Transaction, Error> {
let mut rng = StdRng::seed_from_u64(42);
let sender_sk = AccountSecretKey::random(&mut rng);
let receiver_pk =
Some(AccountPublicKey::from(&AccountSecretKey::random(&mut rng)));
let transfer_value: u64 = rng.gen();
let deposit: u64 = rng.gen();
let nonce: u64 = rng.gen();
Transaction::moonlight(
&sender_sk,
receiver_pk,
transfer_value,
deposit,
GAS_LIMIT,
GAS_PRICE,
nonce,
CHAIN_ID,
data,
)
}
#[test]
fn moonlight() {
let data: Option<TransactionData> = None;
assert!(new_moonlight_tx(data).is_ok());
}
#[test]
fn moonlight_memo_too_large() {
const MEMO_SIZE: usize = MAX_MEMO_SIZE + 1;
let data = Some(vec![1; MEMO_SIZE]);
assert_eq!(
new_moonlight_tx(data).unwrap_err(),
Error::MemoTooLarge(MEMO_SIZE)
);
}