diff --git a/zebra-chain/src/transaction/arbitrary.rs b/zebra-chain/src/transaction/arbitrary.rs index 6046f7dd..ce943cab 100644 --- a/zebra-chain/src/transaction/arbitrary.rs +++ b/zebra-chain/src/transaction/arbitrary.rs @@ -268,32 +268,39 @@ impl Arbitrary for sapling::TransferData { type Parameters = (); fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - // TODO: add an extra spend or output using Either, and stop using filter_map - ( - vec( - any::>(), - 0..MAX_ARBITRARY_ITEMS, - ), - vec(any::(), 0..MAX_ARBITRARY_ITEMS), - ) - .prop_filter_map( - "arbitrary v4 transfers with no spends and no outputs", - |(spends, outputs)| { - if !spends.is_empty() { - Some(sapling::TransferData::SpendsAndMaybeOutputs { - shared_anchor: FieldNotPresent, - spends: spends.try_into().unwrap(), - maybe_outputs: outputs, - }) - } else if !outputs.is_empty() { - Some(sapling::TransferData::JustOutputs { - outputs: outputs.try_into().unwrap(), - }) + vec(any::(), 0..MAX_ARBITRARY_ITEMS) + .prop_flat_map(|outputs| { + ( + if outputs.is_empty() { + // must have at least one spend or output + vec( + any::>(), + 1..MAX_ARBITRARY_ITEMS, + ) } else { - None + vec( + any::>(), + 0..MAX_ARBITRARY_ITEMS, + ) + }, + Just(outputs), + ) + }) + .prop_map(|(spends, outputs)| { + if !spends.is_empty() { + sapling::TransferData::SpendsAndMaybeOutputs { + shared_anchor: FieldNotPresent, + spends: spends.try_into().unwrap(), + maybe_outputs: outputs, } - }, - ) + } else if !outputs.is_empty() { + sapling::TransferData::JustOutputs { + outputs: outputs.try_into().unwrap(), + } + } else { + unreachable!("there must be at least one generated spend or output") + } + }) .boxed() } @@ -304,33 +311,40 @@ impl Arbitrary for sapling::TransferData { type Parameters = (); fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - // TODO: add an extra spend or output using Either, and stop using filter_map - ( - any::(), - vec( - any::>(), - 0..MAX_ARBITRARY_ITEMS, - ), - vec(any::(), 0..MAX_ARBITRARY_ITEMS), - ) - .prop_filter_map( - "arbitrary v5 transfers with no spends and no outputs", - |(shared_anchor, spends, outputs)| { - if !spends.is_empty() { - Some(sapling::TransferData::SpendsAndMaybeOutputs { - shared_anchor, - spends: spends.try_into().unwrap(), - maybe_outputs: outputs, - }) - } else if !outputs.is_empty() { - Some(sapling::TransferData::JustOutputs { - outputs: outputs.try_into().unwrap(), - }) + vec(any::(), 0..MAX_ARBITRARY_ITEMS) + .prop_flat_map(|outputs| { + ( + any::(), + if outputs.is_empty() { + // must have at least one spend or output + vec( + any::>(), + 1..MAX_ARBITRARY_ITEMS, + ) } else { - None + vec( + any::>(), + 0..MAX_ARBITRARY_ITEMS, + ) + }, + Just(outputs), + ) + }) + .prop_map(|(shared_anchor, spends, outputs)| { + if !spends.is_empty() { + sapling::TransferData::SpendsAndMaybeOutputs { + shared_anchor, + spends: spends.try_into().unwrap(), + maybe_outputs: outputs, } - }, - ) + } else if !outputs.is_empty() { + sapling::TransferData::JustOutputs { + outputs: outputs.try_into().unwrap(), + } + } else { + unreachable!("there must be at least one generated spend or output") + } + }) .boxed() }