Skip to content

Commit

Permalink
test: Arbitrary non-negative NeptuneCoins
Browse files Browse the repository at this point in the history
Co-authored-by: Alan Szepieniec <[email protected]>
  • Loading branch information
Sword-Smith and aszepieniec committed Dec 19, 2024
1 parent 93efd2c commit d0df57c
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ pub(crate) mod test {
pub(crate) fn arbitrary() -> BoxedStrategy<BlockPrimitiveWitness> {
const NUM_INPUTS: usize = 2;
(
arb::<NeptuneCoins>(),
NeptuneCoins::arbitrary_non_negative(),
vec(0f64..1f64, NUM_INPUTS - 1),
vec(arb::<Digest>(), NUM_INPUTS),
vec(arb::<Digest>(), NUM_INPUTS),
Expand Down
2 changes: 1 addition & 1 deletion src/models/blockchain/transaction/lock_script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ mod test {
fn lock_script_halts_gracefully_prop(
#[strategy(arb::<Digest>())] txk_mast_hash: Digest,
#[strategy(arb::<Digest>())] seed: Digest,
#[strategy(arb::<NeptuneCoins>())] amount: NeptuneCoins,
#[strategy(NeptuneCoins::arbitrary_non_negative())] amount: NeptuneCoins,
) {
let (_utxos, lock_scripts_and_witnesses) =
PrimitiveWitness::transaction_inputs_from_address_seeds_and_amounts(&[seed], &[amount]);
Expand Down
58 changes: 42 additions & 16 deletions src/models/blockchain/transaction/primitive_witness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,24 @@ impl PrimitiveWitness {
output_amounts_suggestion: &mut [NeptuneCoins],
fee_suggestion: &mut NeptuneCoins,
) {
assert!(
coinbase.is_none_or(|x| !x.is_negative()),
"If coinbase is set, it must be non-negative. Got:\n{coinbase:?}"
);
assert!(
!fee_suggestion.is_negative(),
"Amount balancer only accepts non-negative fee suggestions. Got:\n{fee_suggestion}"
);
assert!(
!total_input_amount.is_negative(),
"Amount balancer only accepts non-negative total input amount. Got:\n{total_input_amount}"
);
assert!(
output_amounts_suggestion
.iter()
.all(|input_amount_sugg| !input_amount_sugg.is_negative()),
"Amount balancer only accepts non-negative output amount suggestsions. Got:\n\n{output_amounts_suggestion:?}"
);
let mut total_output_amount = output_amounts_suggestion
.iter()
.cloned()
Expand Down Expand Up @@ -441,7 +459,7 @@ impl PrimitiveWitness {
// - fee
// - timestamp
(
arb::<NeptuneCoins>(),
NeptuneCoins::arbitrary_non_negative(),
vec(arb::<Digest>(), num_inputs),
vec(arb::<u64>(), num_inputs),
vec(arb::<Digest>(), num_outputs),
Expand Down Expand Up @@ -803,7 +821,10 @@ mod test {
param_sets: [(usize, usize, usize); N],
) -> BoxedStrategy<[PrimitiveWitness; N]> {
(arb::<Option<NeptuneCoins>>(), 0..N)
.prop_flat_map(move |(maybe_coinbase, coinbase_index)| {
.prop_flat_map(move |(mut maybe_coinbase, coinbase_index)| {
// Force coinbase to be non-negative, if set
maybe_coinbase = maybe_coinbase.map(|x| x.abs());

Self::arbitrary_tuple_with_matching_mutator_sets_and_given_coinbase(
param_sets,
maybe_coinbase.map(|coinbase| (coinbase, coinbase_index)),
Expand All @@ -830,8 +851,9 @@ mod test {
|counts: [usize; N]| counts.map(|count| vec(arb::<Digest>(), count));
let nested_vec_strategy_pubann =
|counts: [usize; N]| counts.map(|count| vec(arb::<PublicAnnouncement>(), count));
let nested_vec_strategy_amounts =
|counts: [usize; N]| counts.map(|count| vec(arb::<NeptuneCoins>(), count));
let nested_vec_strategy_amounts = |counts: [usize; N]| {
counts.map(|count| vec(NeptuneCoins::arbitrary_non_negative(), count))
};
let nested_vec_strategy_utxos =
|counts: [usize; N]| counts.map(|count| vec(arb::<Utxo>(), count));
let input_counts: [usize; N] = param_sets.map(|p| p.0);
Expand All @@ -845,7 +867,7 @@ mod test {
nested_vec_strategy_digests(input_counts),
nested_vec_strategy_utxos(output_counts),
nested_vec_strategy_pubann(announcement_counts),
[arb::<NeptuneCoins>(); N],
vec(NeptuneCoins::arbitrary_non_negative(), N),
vec(arb::<Digest>(), total_num_inputs),
vec(arb::<Digest>(), total_num_inputs),
),
Expand Down Expand Up @@ -972,7 +994,7 @@ mod test {
public_announcements_nested.clone(),
output_sender_randomnesses_nested.clone(),
output_receiver_digests_nested.clone(),
fees,
fees.clone(),
inputs_salts,
outputs_salts,
input_utxoss.clone(),
Expand Down Expand Up @@ -1036,7 +1058,7 @@ mod test {
(
(0..total_num_outputs),
(0..total_num_announcements),
arb::<NeptuneCoins>(),
NeptuneCoins::arbitrary_non_negative(),
)
.prop_flat_map(move |(num_outputs, num_announcements, coinbase_amount)| {
let parameter_sets = [
Expand Down Expand Up @@ -1111,8 +1133,8 @@ mod test {
timestamp: Timestamp,
) -> BoxedStrategy<Self> {
(
vec(arb::<NeptuneCoins>(), num_outputs),
arb::<NeptuneCoins>(),
vec(NeptuneCoins::arbitrary_non_negative(), num_outputs),
NeptuneCoins::arbitrary_non_negative(),
vec(arb::<Digest>(), num_outputs),
vec(arb::<Digest>(), num_outputs),
vec(arb::<Digest>(), num_outputs),
Expand Down Expand Up @@ -1320,10 +1342,12 @@ mod test {

#[proptest]
fn amounts_balancer_works_with_coinbase(
#[strategy(arb::<NeptuneCoins>())] total_input_amount: NeptuneCoins,
#[strategy(arb::<NeptuneCoins>())] coinbase: NeptuneCoins,
#[strategy(vec(arb::<NeptuneCoins>(), 1..4))] mut output_amounts: Vec<NeptuneCoins>,
#[strategy(arb::<NeptuneCoins>())] mut fee: NeptuneCoins,
#[strategy(NeptuneCoins::arbitrary_non_negative())] total_input_amount: NeptuneCoins,
#[strategy(NeptuneCoins::arbitrary_non_negative())] coinbase: NeptuneCoins,
#[strategy(vec(NeptuneCoins::arbitrary_non_negative(), 1..4))] mut output_amounts: Vec<
NeptuneCoins,
>,
#[strategy(NeptuneCoins::arbitrary_non_negative())] mut fee: NeptuneCoins,
) {
PrimitiveWitness::find_balanced_output_amounts_and_fee(
total_input_amount,
Expand All @@ -1344,9 +1368,11 @@ mod test {

#[proptest]
fn amounts_balancer_works_without_coinbase(
#[strategy(arb::<NeptuneCoins>())] total_input_amount: NeptuneCoins,
#[strategy(vec(arb::<NeptuneCoins>(), 1..4))] mut output_amounts: Vec<NeptuneCoins>,
#[strategy(arb::<NeptuneCoins>())] mut fee: NeptuneCoins,
#[strategy(NeptuneCoins::arbitrary_non_negative())] total_input_amount: NeptuneCoins,
#[strategy(vec(NeptuneCoins::arbitrary_non_negative(), 1..4))] mut output_amounts: Vec<
NeptuneCoins,
>,
#[strategy(NeptuneCoins::arbitrary_non_negative())] mut fee: NeptuneCoins,
) {
PrimitiveWitness::find_balanced_output_amounts_and_fee(
total_input_amount,
Expand Down
4 changes: 2 additions & 2 deletions src/models/blockchain/transaction/utxo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,11 +258,11 @@ pub fn pseudorandom_utxo(seed: [u8; 32]) -> Utxo {
impl<'a> Arbitrary<'a> for Utxo {
/// Produce a strategy for "arbitrary" UTXOs where "arbitrary" means:
/// - lock script corresponding to an arbitrary generation address
/// - one coin of type NativeCurrency and arbitrary amount.
/// - one coin of type NativeCurrency and arbitrary, non-negative amount.
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let lock_script_hash: Digest = Digest::arbitrary(u)?;
let type_script_hash = NativeCurrency.hash();
let amount = NeptuneCoins::arbitrary(u)?;
let amount = NeptuneCoins::arbitrary(u)?.abs();
let coins = vec![Coin {
type_script_hash,
state: amount.encode(),
Expand Down
6 changes: 3 additions & 3 deletions src/models/blockchain/type_scripts/native_currency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1326,7 +1326,7 @@ pub mod test {
sample(vec(arb::<LockScriptAndWitness>(), 3), &mut tr);
let output_utxos = sample(vec(arb::<Utxo>(), 3), &mut tr);
let public_announcements = sample(vec(arb(), 3), &mut tr);
let fee = sample(arb::<NeptuneCoins>(), &mut tr);
let fee = sample(NeptuneCoins::arbitrary_non_negative(), &mut tr);
let primitive_witness = PrimitiveWitness::arbitrary_primitive_witness_with(
&input_utxos,
&input_lock_scripts_and_witnesses,
Expand Down Expand Up @@ -1444,7 +1444,7 @@ pub mod test {

#[proptest]
fn fee_can_be_positive(
#[strategy(arb::<NeptuneCoins>())] _fee: NeptuneCoins,
#[strategy(NeptuneCoins::arbitrary_non_negative())] _fee: NeptuneCoins,
#[strategy(PrimitiveWitness::arbitrary_with_fee(#_fee))]
primitive_witness: PrimitiveWitness,
) {
Expand All @@ -1453,7 +1453,7 @@ pub mod test {

#[proptest]
fn fee_can_be_negative(
#[strategy(arb::<NeptuneCoins>())] _fee: NeptuneCoins,
#[strategy(NeptuneCoins::arbitrary_non_negative())] _fee: NeptuneCoins,
#[strategy(PrimitiveWitness::arbitrary_with_fee(-#_fee))]
primitive_witness: PrimitiveWitness,
) {
Expand Down
25 changes: 21 additions & 4 deletions src/models/blockchain/type_scripts/neptune_coins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ use num_traits::CheckedSub;
use num_traits::FromPrimitive;
use num_traits::One;
use num_traits::Zero;
use proptest::prelude::BoxedStrategy;
use proptest::prelude::Strategy;
use proptest_arbitrary_interop::arb;
use regex::Regex;
use serde::Deserialize;
use serde::Serialize;
Expand Down Expand Up @@ -204,10 +207,6 @@ impl NeptuneCoins {
Some(Self(number as i128))
}

pub fn is_negative(&self) -> bool {
self.0 < 0
}

pub fn scalar_mul(&self, factor: u32) -> Self {
let factor_as_i128 = factor as i128;
let (res, overflow) = self.0.overflowing_mul(factor_as_i128);
Expand Down Expand Up @@ -279,6 +278,16 @@ impl NeptuneCoins {
}
}

impl NeptuneCoins {
pub(crate) fn abs(&self) -> Self {
Self(self.0.abs())
}

pub fn is_negative(&self) -> bool {
self.0.is_negative()
}
}

impl GetSize for NeptuneCoins {
fn get_stack_size() -> usize {
std::mem::size_of::<Self>()
Expand Down Expand Up @@ -490,6 +499,14 @@ impl<'a> Arbitrary<'a> for NeptuneCoins {
}
}

impl NeptuneCoins {
pub(crate) fn arbitrary_non_negative() -> BoxedStrategy<Self> {
arb::<u128>()
.prop_map(|uint| NeptuneCoins((uint >> 10) as i128))
.boxed()
}
}

#[cfg(test)]
pub(crate) mod test {
use std::ops::ShlAssign;
Expand Down
8 changes: 4 additions & 4 deletions src/models/blockchain/type_scripts/time_lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -776,12 +776,12 @@ impl Arbitrary for TimeLockWitness {
let num_inputs = release_dates.len();
(
vec(arb::<Digest>(), num_inputs),
vec(arb::<NeptuneCoins>(), num_inputs),
vec(NeptuneCoins::arbitrary_non_negative(), num_inputs),
vec(arb::<Digest>(), num_outputs),
vec(arb::<NeptuneCoins>(), num_outputs),
vec(NeptuneCoins::arbitrary_non_negative(), num_outputs),
vec(arb::<PublicAnnouncement>(), num_public_announcements),
arb::<Option<NeptuneCoins>>(),
arb::<NeptuneCoins>(),
NeptuneCoins::arbitrary_non_negative(),
)
.prop_flat_map(
move |(
Expand Down Expand Up @@ -914,7 +914,7 @@ fn arbitrary_primitive_witness_with_timelocks(
release_dates: Vec<Timestamp>,
) -> BoxedStrategy<PrimitiveWitness> {
(
arb::<NeptuneCoins>(),
NeptuneCoins::arbitrary_non_negative(),
vec(arb::<Digest>(), num_inputs),
vec(arb::<u64>(), num_inputs),
vec(arb::<Digest>(), num_outputs),
Expand Down

0 comments on commit d0df57c

Please sign in to comment.