diff --git a/core/src/types/token.rs b/core/src/types/token.rs index 10c690bb065..2bf95735ed1 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -502,4 +502,12 @@ pub mod testing { pub fn arb_amount_ceiled(max: u64) -> impl Strategy { (0..=max).prop_map(Amount::from) } + + /// Generate an arbitrary non-zero token amount up to and including given + /// `max` value + pub fn arb_amount_non_zero_ceiled( + max: u64, + ) -> impl Strategy { + (1..=max).prop_map(Amount::from) + } } diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 68734aa30f8..64c7ffdff86 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -968,8 +968,6 @@ pub enum BondError { InactiveValidator(Address), #[error("Voting power overflow: {0}")] VotingPowerOverflow(TryFromIntError), - #[error("Given zero amount to bond")] - ZeroAmount, } #[allow(missing_docs)] @@ -987,8 +985,6 @@ pub enum UnbondError { ValidatorHasNoVotingPower(Address), #[error("Voting power overflow: {0}")] VotingPowerOverflow(TryFromIntError), - #[error("Given zero amount to unbond")] - ZeroAmount, } #[allow(missing_docs)] @@ -1291,9 +1287,6 @@ fn bond_tokens( validator_set: &mut ValidatorSets, current_epoch: Epoch, ) -> Result { - if amount == token::Amount::default() { - return Err(BondError::ZeroAmount); - } // Check the validator state match validator_state { None => { @@ -1400,9 +1393,6 @@ fn unbond_tokens( validator_set: &mut ValidatorSets, current_epoch: Epoch, ) -> Result { - if amount == token::Amount::default() { - return Err(UnbondError::ZeroAmount); - } // We can unbond tokens that are bonded for a future epoch (not yet // active), hence we check the total at the pipeline offset let unbondable_amount = bond diff --git a/wasm/checksums.json b/wasm/checksums.json index 20b1de2c2af..68225d5684f 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,16 +1,16 @@ { - "tx_bond.wasm": "tx_bond.607ff5fa2f3239c95077b16ceefde7f673eee3d135f00c00bdd650ce28633ac5.wasm", + "tx_bond.wasm": "tx_bond.41d34a5525958df38c2987213c13bf6e16a175cfc6e58d406b618ed3ad2dd239.wasm", "tx_change_validator_commission.wasm": "tx_change_validator_commission.68369752850d31b94d5212a644eedd921201d261aa591f58b8c2162e23116947.wasm", "tx_ibc.wasm": "tx_ibc.4731e4d7b2a0c542da632bce7866639656905c9f7ab22099a2e2d1b12820736e.wasm", "tx_init_account.wasm": "tx_init_account.f978f4eafdb8075e9e1be1160afd0cf2e57f31ef5fbdf8350170a4dbb8d1f1de.wasm", "tx_init_proposal.wasm": "tx_init_proposal.aec4904ba5371bf0927c41a615d48257e1c08e6c80aa7b34b45370b65aeebe0a.wasm", - "tx_init_validator.wasm": "tx_init_validator.badefa4715e4bb265a0376d65e05648225e0780c52fe8c5083cbaf3bfcbd9ff2.wasm", + "tx_init_validator.wasm": "tx_init_validator.74edb623e29562cbf78cdfec7b6ccefdb6932186c641d760f0f195f04fd6eb68.wasm", "tx_reveal_pk.wasm": "tx_reveal_pk.139863565d10266796860a4f977665be8524e14825b7b0eb5aabb13db5aa590e.wasm", "tx_transfer.wasm": "tx_transfer.c8864ad3e620b809d7874f61b14cc8179ee220da855ddf288ebca50428f89bbf.wasm", - "tx_unbond.wasm": "tx_unbond.d3ca1fece60779942adfa3b4de7d65ecc9ba3bb52843b96b8b1dae2674b6fa61.wasm", + "tx_unbond.wasm": "tx_unbond.ab110c867ddd66617899012129e58f356389089ac4f62b024b4b45485b4da8cd.wasm", "tx_update_vp.wasm": "tx_update_vp.ee2e9b882c4accadf4626e87d801c9ac8ea8c61ccea677e0532fc6c1ee7db6a2.wasm", "tx_vote_proposal.wasm": "tx_vote_proposal.b33288153249f20e43942bcb21c2862e68e2edd865638174e3557255bd7f11a5.wasm", - "tx_withdraw.wasm": "tx_withdraw.64bce44372b48eddcc319d1693fa6e821ca96cfc3ce2f2c55f4c0877677311a9.wasm", + "tx_withdraw.wasm": "tx_withdraw.674505b38f6be3188db7e1b223f1229a2c987537d3db620bd04e3ec224fd4ad0.wasm", "vp_implicit.wasm": "vp_implicit.82a65be788b416b3c499c8508c8e06d31ba008bc566b66b7d6cba31ed07df6fd.wasm", "vp_masp.wasm": "vp_masp.d292c4e7521b60aac06bb1e82662367ddf33320f2afc9e76aa061f46b9fbeb9f.wasm", "vp_testnet_faucet.wasm": "vp_testnet_faucet.41932ec7f908cac3068206a23f8aa9ad342874b2740a8401dfb5c113faac21e3.wasm", diff --git a/wasm/wasm_source/proptest-regressions/tx_unbond.txt b/wasm/wasm_source/proptest-regressions/tx_unbond.txt new file mode 100644 index 00000000000..8c589d1abd8 --- /dev/null +++ b/wasm/wasm_source/proptest-regressions/tx_unbond.txt @@ -0,0 +1 @@ +cc f22e874350910b197cb02a4a07ec5bef18e16c0d1a39eaabaee43d1fc05ce11d diff --git a/wasm/wasm_source/src/tx_bond.rs b/wasm/wasm_source/src/tx_bond.rs index 5e5eeb474af..219a6126304 100644 --- a/wasm/wasm_source/src/tx_bond.rs +++ b/wasm/wasm_source/src/tx_bond.rs @@ -291,7 +291,7 @@ mod tests { ( arb_established_address(), prop::option::of(arb_non_internal_address()), - token::testing::arb_amount_ceiled(max_amount), + token::testing::arb_amount_non_zero_ceiled(max_amount), ) .prop_map(|(validator, source, amount)| { transaction::pos::Bond { diff --git a/wasm/wasm_source/src/tx_unbond.rs b/wasm/wasm_source/src/tx_unbond.rs index d7a6431243b..70033286bf1 100644 --- a/wasm/wasm_source/src/tx_unbond.rs +++ b/wasm/wasm_source/src/tx_unbond.rs @@ -258,21 +258,20 @@ mod tests { epoch {epoch}" ); } - // Check that the unbond is as expected - let start_epoch = match &unbond.source { - Some(_) => { - // This bond was a delegation - Epoch::from(pos_params.pipeline_len) - } - None => { - // This bond was a genesis validator self-bond - Epoch::default() - } + let start_epoch = if is_delegation { + // This bond was a delegation + Epoch::from(pos_params.pipeline_len) + } else { + // This bond was a genesis validator self-bond + Epoch::default() }; let end_epoch = Epoch::from(pos_params.unbonding_len - 1); - let expected_unbond = - HashMap::from_iter([((start_epoch, end_epoch), unbond.amount)]); + let expected_unbond = if unbond.amount == token::Amount::default() { + HashMap::new() + } else { + HashMap::from_iter([((start_epoch, end_epoch), unbond.amount)]) + }; let actual_unbond: Unbond = unbonds_post.get(pos_params.unbonding_len).unwrap(); assert_eq!( @@ -342,7 +341,7 @@ mod tests { ( address::testing::arb_established_address(), prop::option::of(address::testing::arb_non_internal_address()), - token::testing::arb_amount_ceiled(max_amount), + token::testing::arb_amount_non_zero_ceiled(max_amount), ) .prop_map(|(validator, source, amount)| { let validator = Address::Established(validator); diff --git a/wasm/wasm_source/src/tx_withdraw.rs b/wasm/wasm_source/src/tx_withdraw.rs index 8819063a0cd..e29415f800d 100644 --- a/wasm/wasm_source/src/tx_withdraw.rs +++ b/wasm/wasm_source/src/tx_withdraw.rs @@ -201,16 +201,17 @@ mod tests { fn arb_initial_stake_and_unbonded_amount() -> impl Strategy { // Generate initial stake - token::testing::arb_amount_ceiled((i64::MAX / 8) as u64).prop_flat_map( - |initial_stake| { + token::testing::arb_amount_non_zero_ceiled((i64::MAX / 8) as u64) + .prop_flat_map(|initial_stake| { // Use the initial stake to limit the unbonded amount from the // stake let unbonded_amount = - token::testing::arb_amount_ceiled(initial_stake.into()); + token::testing::arb_amount_non_zero_ceiled( + initial_stake.into(), + ); // Use the generated initial stake too too (Just(initial_stake), unbonded_amount) - }, - ) + }) } fn arb_withdraw() -> impl Strategy {