diff --git a/src/proto_alpha/lib_protocol/apply.mli b/src/proto_alpha/lib_protocol/apply.mli index 33b3c3920a6451ebf476f6f9521acd71d0cfad29..0cb25868f2fad078bddc66dfa4bfcc5523e96ec4 100644 --- a/src/proto_alpha/lib_protocol/apply.mli +++ b/src/proto_alpha/lib_protocol/apply.mli @@ -39,6 +39,7 @@ type error += | Internal_operation_replay of Apply_internal_results.packed_internal_operation | Empty_transaction of Contract.t + | Staking_to_delegate_that_refuses_external_staking type mode = | Application of { diff --git a/src/proto_alpha/lib_protocol/staking.mli b/src/proto_alpha/lib_protocol/staking.mli index e3b6925fae187bd4149063a363c80fa428af3aec..385fc364374dec2bdc5c37a6b412d3c4ecdc04a8 100644 --- a/src/proto_alpha/lib_protocol/staking.mli +++ b/src/proto_alpha/lib_protocol/staking.mli @@ -23,6 +23,10 @@ (* *) (*****************************************************************************) +type error += + | Cannot_stake_with_unfinalizable_unstake_requests_to_another_delegate + | Manual_staking_forbidden + (** [stake ctxt ~sender ~for_next_cycle_use_only_after_slashing ~delegate amount] add [amount] as [sender]'s stake to [delegate]. diff --git a/src/proto_alpha/lib_protocol/test/helpers/error_helpers.ml b/src/proto_alpha/lib_protocol/test/helpers/error_helpers.ml index 3bec3052011b8090ec1e78f23257bc30ac1e13a8..112cfbaf2cf931eb04c038ca9234a202e0e4675f 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/error_helpers.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/error_helpers.ml @@ -11,6 +11,18 @@ open Protocol open Alpha_context open Validate_errors.Manager +let check_error_constructor_name ~loc ~expected errs = + Assert.expect_error ~loc errs (function + | [err] -> + let err_constructor_name = + Stdlib.Obj.Extension_constructor.(of_val err |> name) + in + let expected_constructor_name = + Stdlib.Obj.Extension_constructor.(of_val expected |> name) + in + Compare.String.(err_constructor_name = expected_constructor_name) + | _ -> false) + (** Identifies the [Inconsistent_sources] error. *) let check_inconsistent_sources ~first_source ~source = function | [Inconsistent_sources {fee_payer; source = s}] -> @@ -81,3 +93,90 @@ let expect_guest_incorrect_reveal_position ~loc ~guest errs = | [Guest_incorrect_reveal_position {guest = g}] -> Signature.Public_key_hash.(g = Account.pkh_of_contract_exn guest) | _ -> false) + +let expect_forbidden_delegate ~loc ~delegate errs = + Assert.expect_error ~loc errs (function + | [Validate_errors.Consensus.Forbidden_delegate d] -> + Signature.Public_key_hash.(d = Account.pkh_of_contract_exn delegate) + | _ -> false) + +let expect_outdated_denunciation ~loc ?kind ?level ?last_cycle errs = + Assert.expect_error ~loc errs (function + | [ + Validate_errors.Anonymous.Outdated_denunciation + {kind = k; level = l; last_cycle = c}; + ] -> ( + (match kind with + | Some kind -> + Protocol.Alpha_context.Misbehaviour.compare_kind kind k = 0 + | None -> true) + && (match level with + | Some level -> Protocol.Alpha_context.Raw_level.equal level l + | None -> true) + && + match last_cycle with + | Some last_cycle -> Cycle.equal last_cycle c + | None -> true) + | _ -> false) + +let expect_outdated_denunciation_state ~loc ~state errs = + let ds = state.State.double_signings in + let ds = match ds with [a] -> a | _ -> assert false in + let level = + Protocol.Alpha_context.Raw_level.Internal_for_tests.from_repr + ds.misbehaviour.level + in + let last_cycle = + Cycle.add + (Block.current_cycle_of_level + ~blocks_per_cycle:state.State.constants.blocks_per_cycle + ~current_level:(Protocol.Raw_level_repr.to_int32 ds.misbehaviour.level)) + (Protocol.Constants_repr.max_slashing_period - 1) + in + let (kind : Protocol.Alpha_context.Misbehaviour.kind) = + (* This conversion would not be needed if + Misbehaviour_repr.kind were moved to a + separate file that doesn't have under/over + Alpha_context versions. *) + match ds.misbehaviour.kind with + | Double_baking -> Double_baking + | Double_attesting -> Double_attesting + | Double_preattesting -> Double_preattesting + in + expect_outdated_denunciation ~loc ~kind ~level ~last_cycle errs + +let expect_no_slots_found_for ~loc ~pkh err = + Assert.error ~loc (Error err) (function + | Block.No_slots_found_for p -> Signature.Public_key_hash.(p = pkh) + | _ -> false) + +let expect_empty_transaction ~loc ~contract errs = + Assert.expect_error ~loc errs (function + | [Apply.Empty_transaction c] -> Contract.(contract = c) + | _ -> false) + +let expect_balance_too_low ~loc errs = + Assert.expect_error ~loc errs (function + | [Contract_storage.Balance_too_low _; Tez_repr.Subtraction_underflow _] + -> + true + | _ -> false) + +(** [expect_failwith str err] checks whether [err] is a failwith error containing + a string that matches the regular expression [str]. + + For example, in case of a failure, [Assert.equal a b] prints a string saying + that [a] is not equal to [b], replacing [a] and [b] with their actual values. + In this case, [Str.regexp ".*\n.*is not equal to.*"] is constructed to check + for such kind of errors. *) +let expect_failwith ~loc ?str err = + Assert.error ~loc (Error err) (function + | Exn (Failure s) -> ( + match str with Some str -> Str.string_match str s 0 | None -> true) + | _ -> false) + +let expect_empty_implicit_delegated_contract ~loc ~contract errs = + Assert.expect_error ~loc errs (function + | [Contract_storage.Empty_implicit_delegated_contract c] -> + Signature.Public_key_hash.(c = Account.pkh_of_contract_exn contract) + | _ -> false) diff --git a/src/proto_alpha/lib_protocol/test/helpers/scenario_base.ml b/src/proto_alpha/lib_protocol/test/helpers/scenario_base.ml index ddcbe5f972829b65d90323b2cf3a9ca6341aa159..9a4bedda4350ad8ff8ead5dffee6f35a81e7ea8f 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/scenario_base.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/scenario_base.ml @@ -14,12 +14,6 @@ open State open Scenario_dsl open Log_helpers -(** For [assert_failure], when expected error does not match the actual error. *) -type error += Unexpected_error - -(** For [assert_failure], when scenario actually succeeds when expected to fail. *) -type error += Unexpected_success - (** Usual threaded state for the tests. Contains the current block, pending operations and the known [State.t] *) type t = Block.t * State.t @@ -150,48 +144,30 @@ let check_rate_evolution (f : Q.t -> Q.t -> bool) : (t, t) scenarios = (* ======== Misc functions ========*) -let check_failure_aux ?(loc = __LOC__) ?expected_error : +let check_failure_aux ?(loc = __LOC__) ~expected_error : ('a -> 'b tzresult Lwt.t) -> 'a -> 'a tzresult Lwt.t = let open Lwt_result_syntax in fun f input -> Log.info ~color:assert_block_color "Entering failing scenario..." ; let*! output = f input in match output with - | Ok _ -> - Log.info "%s: Unexpected success@." loc ; - tzfail Unexpected_success - | Error e -> ( - match expected_error with - | None -> - Log.info ~color:assert_block_color "Rollback" ; - return input - | Some exp_e -> - let exp_e = exp_e input in - if e = exp_e then ( - Log.info ~color:assert_block_color "Rollback" ; - return input) - else ( - Log.info - "%s: Unexpected error:@.%a@.Expected:@.%a@." - loc - (Format.pp_print_list pp) - e - (Format.pp_print_list pp) - exp_e ; - tzfail Unexpected_error)) + | Ok _ -> failwith "%s: Unexpected success@." loc + | Error err -> + let* () = expected_error input err in + return input -let check_fail_and_rollback ?(loc = __LOC__) ?expected_error : +let check_fail_and_rollback ?(loc = __LOC__) ~expected_error : ('a, 'b) single_scenario -> 'a -> 'a tzresult Lwt.t = - fun sc input -> check_failure_aux ~loc ?expected_error (run_scenario sc) input + fun sc input -> check_failure_aux ~loc ~expected_error (run_scenario sc) input (** Useful function to test expected failures: runs the given branch until it fails, then rollbacks to before execution. Fails if the given branch Succeeds *) -let assert_failure ?(loc = __LOC__) ?expected_error : +let assert_failure ?(loc = __LOC__) ~expected_error : ('a, 'b) scenarios -> ('a, 'a) scenarios = fun scenarios -> match unfold_scenarios scenarios with | [] -> Empty - | [(sc, _, _)] -> exec (check_fail_and_rollback ~loc ?expected_error sc) + | [(sc, _, _)] -> exec (check_fail_and_rollback ~loc ~expected_error sc) | _ -> exec (fun _ -> failwith "%s: Error: assert_failure used with branching scenario" loc) @@ -211,6 +187,15 @@ let assert_success ?(loc = __LOC__) : ('a, 'b) scenarios -> ('a, 'a) scenarios = exec (fun _ -> failwith "%s: Error: assert_success used with branching scenario" loc) +let assert_failure_in_check_snapshot_balances ~loc ?f snap_name = + assert_failure + ~expected_error:(fun _ errs -> + Error_helpers.expect_failwith + ~loc + ~str:(Str.regexp ".*\n.*is not equal to.*") + errs) + (check_snapshot_balances ?f snap_name) + (** Loop *) let rec loop n : ('a, 'a) scenarios -> ('a, 'a) scenarios = fun scenario -> @@ -261,6 +246,15 @@ let check_balance_field src_name field amount : (t, t) scenarios = in return_unit) +let assert_failure_in_check_balance_field ~loc src_name field amount = + assert_failure + ~expected_error:(fun _ errs -> + Error_helpers.expect_failwith + ~loc + ~str:(Str.regexp ".*\n.*Tez aren't equal.*") + errs) + (check_balance_field src_name field amount) + let check_balance_fields src_name ~liquid ~staked ?(unstaked_frozen_total = Tez.zero) () = check_balance_field src_name `Staked staked diff --git a/src/proto_alpha/lib_protocol/test/integration/test_scenario_base.ml b/src/proto_alpha/lib_protocol/test/integration/test_scenario_base.ml index 9a14737b314494f5d417f8569f63062b92aa3534..2ecd444b96673c77970f482fd7201c91af1bcc2e 100644 --- a/src/proto_alpha/lib_protocol/test/integration/test_scenario_base.ml +++ b/src/proto_alpha/lib_protocol/test/integration/test_scenario_base.ml @@ -17,13 +17,26 @@ open Scenario let test_expected_error = assert_failure - ~expected_error:(fun _ -> [Exn (Failure "")]) + ~expected_error:(fun _ errs -> + Error_helpers.expect_failwith + ~loc:__LOC__ + ~str:(Str.regexp_string "") + errs) (exec (fun _ -> failwith "")) --> assert_failure - ~expected_error:(fun _ -> [Unexpected_error]) + ~expected_error:(fun _ errs -> + Error_helpers.expect_failwith + ~str:(Str.regexp ".*expected a specific error.*") + ~loc:__LOC__ + errs) (assert_failure - ~expected_error:(fun _ -> - [Inconsistent_number_of_bootstrap_accounts]) + ~expected_error:(fun _ errs -> + Error_helpers.check_error_constructor_name + ~loc:__LOC__ + ~expected: + Protocol.Apply + .Staking_to_delegate_that_refuses_external_staking + errs) (exec (fun _ -> failwith ""))) let tests = diff --git a/src/proto_alpha/lib_protocol/test/integration/test_scenario_deactivation.ml b/src/proto_alpha/lib_protocol/test/integration/test_scenario_deactivation.ml index 8d6a09f3f4c1903e80263615fc0551b4e4d1f431..104f744fab7a29f8b51e79278cf5bc0146faf0fb 100644 --- a/src/proto_alpha/lib_protocol/test/integration/test_scenario_deactivation.ml +++ b/src/proto_alpha/lib_protocol/test/integration/test_scenario_deactivation.ml @@ -119,7 +119,14 @@ let test_baking_deactivation = --> stake "delegate" Half --> set_delegate "delegate" (Some "delegate") (* No rights yet, but active *) - --> assert_failure (next_block_with_baker "delegate") + --> assert_failure + ~expected_error:(fun (_, state) errs -> + let delegate = State.find_account "delegate" state in + Error_helpers.expect_no_slots_found_for + ~loc:__LOC__ + ~pkh:delegate.pkh + errs) + (next_block_with_baker "delegate") --> check_is_active ~loc:__LOC__ "delegate" --> wait_n_cycles_f (fun (_, state) -> state.State.constants.consensus_rights_delay + 1) diff --git a/src/proto_alpha/lib_protocol/test/integration/test_scenario_slashing.ml b/src/proto_alpha/lib_protocol/test/integration/test_scenario_slashing.ml index e4c52c38e57dfa7031004cd9a4946d0449ebed1d..d0ec95ad29f21506bee4cfcd36425da6f44f2bd0 100644 --- a/src/proto_alpha/lib_protocol/test/integration/test_scenario_slashing.ml +++ b/src/proto_alpha/lib_protocol/test/integration/test_scenario_slashing.ml @@ -117,7 +117,12 @@ let test_multiple_misbehaviors = Empty [1; 3] -let check_is_forbidden baker = assert_failure (next_block_with_baker baker) +let check_is_forbidden ~loc baker = + assert_failure + ~expected_error:(fun (_, state) errs -> + let baker = State.find_account baker state in + Error_helpers.expect_forbidden_delegate ~loc ~delegate:baker.contract errs) + (next_block_with_baker baker) let check_is_not_forbidden baker = let open Lwt_result_syntax in @@ -148,7 +153,7 @@ let test_delegate_forbidden = check_is_not_forbidden "delegate" --> make_denunciations () --> (* delegate is forbidden directly after the first denunciation *) - check_is_forbidden "delegate" + check_is_forbidden ~loc:__LOC__ "delegate" |+ Tag "Is forbidden after single misbehavior" --> double_attest "delegate" --> (Tag "very early first denounce" @@ -157,18 +162,24 @@ let test_delegate_forbidden = |+ Tag "in next cycle" --> next_cycle --> exclude_bakers ["delegate"] --> make_denunciations ()) - --> check_is_forbidden "delegate" + --> check_is_forbidden ~loc:__LOC__ "delegate" |+ Tag "Is unforbidden after CONSENSUS_RIGHTS_DELAY after slash cycles" --> double_attest "delegate" --> exclude_bakers ["delegate"] --> make_denunciations () - --> check_is_forbidden "delegate" + --> check_is_forbidden ~loc:__LOC__ "delegate" --> next_cycle (* slash occured *) --> stake "delegate" Half --> wait_n_cycles_f crd --> check_is_not_forbidden "delegate" |+ Tag "Is not forbidden after a denunciation is outdated" --> double_attest "delegate" --> wait_n_cycles 2 - --> assert_failure (make_denunciations ()) + --> assert_failure + ~expected_error:(fun (_block, state) errs -> + Error_helpers.expect_outdated_denunciation_state + ~loc:__LOC__ + ~state + errs) + (make_denunciations ()) --> check_is_not_forbidden "delegate" |+ Tag "Two double attestations, in consecutive cycles, denounce out of \ @@ -183,7 +194,7 @@ let test_delegate_forbidden = (not denounced) && Protocol.Raw_level_repr.to_int32 level <= 10l) () - --> check_is_forbidden "delegate") + --> check_is_forbidden ~loc:__LOC__ "delegate") let test_slash_unstake = init_constants () @@ -299,7 +310,7 @@ let test_no_shortcut_for_cheaters = (Tag "wait 0 cycles" --> Empty) (Stdlib.List.init (consensus_rights_delay - 1) (fun i -> i + 1)) --> stake "delegate" amount - --> assert_failure (check_snapshot_balances "init") + --> assert_failure_in_check_snapshot_balances ~loc:__LOC__ "init" |+ Tag "wait enough cycles (consensus_rights_delay + 1)" --> wait_n_cycles (consensus_rights_delay + 1) --> stake "delegate" amount diff --git a/src/proto_alpha/lib_protocol/test/integration/test_scenario_slashing_stakers.ml b/src/proto_alpha/lib_protocol/test/integration/test_scenario_slashing_stakers.ml index c30ed26eec261c2968c45713f2a1e10cf008f776..bb581450859c2da3abdc81d747255874beca90e8 100644 --- a/src/proto_alpha/lib_protocol/test/integration/test_scenario_slashing_stakers.ml +++ b/src/proto_alpha/lib_protocol/test/integration/test_scenario_slashing_stakers.ml @@ -141,6 +141,13 @@ let test_simple_slash = --> exec_unit (check_pending_slashings ~loc:__LOC__) --> next_cycle --> assert_failure + ~expected_error:(fun (_, state) errs -> + let str = + if State_ai_flags.Delayed_slashing.enabled state then + Str.regexp_string "ns_enable = true: slash not applied yet" + else Str.regexp ".*\n.*is not equal to.*" + in + Error_helpers.expect_failwith ~loc:__LOC__ ~str errs) (exec_unit (fun (_block, state) -> if State_ai_flags.Delayed_slashing.enabled state then failwith "ns_enable = true: slash not applied yet" @@ -150,37 +157,11 @@ let test_simple_slash = --> next_cycle |+ Tag "denounce too late" --> next_cycle --> next_cycle --> assert_failure - ~expected_error:(fun (_block, state) -> - let ds = state.State.double_signings in - let ds = match ds with [a] -> a | _ -> assert false in - let level = - Protocol.Alpha_context.Raw_level.Internal_for_tests.from_repr - ds.misbehaviour.level - in - let last_cycle = - Cycle.add - (Block.current_cycle_of_level - ~blocks_per_cycle:state.State.constants.blocks_per_cycle - ~current_level: - (Protocol.Raw_level_repr.to_int32 - ds.misbehaviour.level)) - (Protocol.Constants_repr.max_slashing_period - 1) - in - let (kind : Protocol.Alpha_context.Misbehaviour.kind) = - (* This conversion would not be needed if - Misbehaviour_repr.kind were moved to a - separate file that doesn't have under/over - Alpha_context versions. *) - match ds.misbehaviour.kind with - | Double_baking -> Double_baking - | Double_attesting -> Double_attesting - | Double_preattesting -> Double_preattesting - in - [ - Environment.Ecoproto_error - (Protocol.Validate_errors.Anonymous.Outdated_denunciation - {kind; level; last_cycle}); - ]) + ~expected_error:(fun (_block, state) errs -> + Error_helpers.expect_outdated_denunciation_state + ~loc:__LOC__ + ~state + errs) (make_denunciations ()) --> check_snapshot_balances "before slash") diff --git a/src/proto_alpha/lib_protocol/test/integration/test_scenario_stake.ml b/src/proto_alpha/lib_protocol/test/integration/test_scenario_stake.ml index f774be782ae795025ed86e69cc04f1247da735ef..4c90967e937a6cef0bcd82fc2addda6595684f2b 100644 --- a/src/proto_alpha/lib_protocol/test/integration/test_scenario_stake.ml +++ b/src/proto_alpha/lib_protocol/test/integration/test_scenario_stake.ml @@ -65,7 +65,7 @@ let wait_for_unfreeze_and_check wait = (* Balance didn't change yet, but will change next cycle *) --> check_snapshot_balances "wait snap" --> next_cycle - --> assert_failure (check_snapshot_balances "wait snap") + --> assert_failure_in_check_snapshot_balances ~loc:__LOC__ "wait snap" let unstake_wait (_, state) = let crd = state.State.constants.consensus_rights_delay in @@ -73,7 +73,11 @@ let unstake_wait (_, state) = crd + msp let finalize staker = - assert_failure (check_balance_field staker `Unstaked_finalizable Tez.zero) + assert_failure_in_check_balance_field + ~loc:__LOC__ + staker + `Unstaked_finalizable + Tez.zero --> finalize_unstake staker --> check_balance_field staker `Unstaked_finalizable Tez.zero @@ -231,8 +235,11 @@ let scenario_finalize = --> wait_n_cycles_f unstake_wait --> (Tag "minimal wait after unstake" --> Empty |+ Tag "wait longer after unstake" --> wait_n_cycles 2) - --> assert_failure - (check_balance_field "staker" `Unstaked_finalizable Tez.zero) + --> assert_failure_in_check_balance_field + ~loc:__LOC__ + "staker" + `Unstaked_finalizable + Tez.zero --> (Tag "finalize with finalize" --> finalize_unstake "staker" |+ Tag "finalize with stake" --> stake "staker" (Amount Tez.one_mutez) |+ Tag "finalize with unstake" --> unstake "staker" (Amount Tez.one_mutez) @@ -247,13 +254,19 @@ let scenario_not_finalize = init_staker_delegate_or_external --> stake "staker" Half --> next_cycle --> unstake "staker" All --> wait_n_cycles_f (unstake_wait ++ 2) - --> assert_failure - (check_balance_field "staker" `Unstaked_finalizable Tez.zero) + --> assert_failure_in_check_balance_field + ~loc:__LOC__ + "staker" + `Unstaked_finalizable + Tez.zero --> snapshot_balances "not finalize" ["staker"] --> (Tag "no finalize with unstake if staked = 0" --> unstake "staker" (Amount Tez.one_mutez)) - --> assert_failure - (check_balance_field "staker" `Unstaked_finalizable Tez.zero) + --> assert_failure_in_check_balance_field + ~loc:__LOC__ + "staker" + `Unstaked_finalizable + Tez.zero --> check_snapshot_balances "not finalize" (* TODO: there's probably more... *) @@ -268,11 +281,33 @@ let scenario_forbidden_operations = init_staker_delegate_or_external --> (* Staking everything works for self delegates, but not for delegated accounts *) assert_failure + ~expected_error:(fun (_, state) errs -> + if State.(is_self_delegate "staker" state) then + Error_helpers.expect_failwith + ~loc:__LOC__ + ~str:(Str.regexp_string "_self_delegate_exit_") + errs + else + let staker = State.find_account "staker" state in + Error_helpers.expect_empty_implicit_delegated_contract + ~loc:__LOC__ + ~contract:staker.contract + errs) (fail_if_staker_is_self_delegate "staker" --> stake "staker" All) (* stake is always forbidden when amount is zero *) - --> assert_failure (stake "staker" Nothing) + --> assert_failure + ~expected_error:(fun (_, state) errs -> + let staker = State.find_account "staker" state in + Error_helpers.expect_empty_transaction + ~loc:__LOC__ + ~contract:staker.contract + errs) + (stake "staker" Nothing) (* One cannot stake more that one has *) - --> assert_failure (stake "staker" Max_tez) + --> assert_failure + ~expected_error:(fun _ errs -> + Error_helpers.expect_balance_too_low ~loc:__LOC__ errs) + (stake "staker" Max_tez) (* unstake is actually authorized for amount 0, but does nothing (doesn't even finalize if possible) *) --> unstake "staker" Nothing @@ -287,6 +322,8 @@ let full_balance_in_finalizable = (* At this point, almost all the balance (but one mutez) of the stake is in finalizable *) (* Staking is possible, but not transfer *) --> assert_failure + ~expected_error:(fun _ errs -> + Error_helpers.expect_balance_too_low ~loc:__LOC__ errs) (transfer "staker" "dummy" (Amount (Tez.of_mutez 10_000_000L))) --> stake "staker" (Amount (Tez.of_mutez 10_000_000L)) (* After the stake, transfer is possible again because the funds were finalized *) @@ -321,15 +358,34 @@ let change_delegate_to_self = having unstake requests to a previous delegate that cannot be finalized yet. Try again in a later cycle (no more than consensus_rights_delay + max_slashing_period)." *) - --> assert_failure (stake "staker" Half) + --> assert_failure + ~expected_error:(fun _ errs -> + Error_helpers.check_error_constructor_name + ~loc:__LOC__ + ~expected: + Protocol.Staking + .Cannot_stake_with_unfinalizable_unstake_requests_to_another_delegate + errs) + (stake "staker" Half) --> unstake "staker" Max_tez --> wait_n_cycles_f (unstake_wait -- 1) (* Still can't stake. *) --> check_balance_field "staker" `Unstaked_finalizable Tez.zero - --> assert_failure (stake "staker" Half) + --> assert_failure + ~expected_error:(fun _ errs -> + Error_helpers.check_error_constructor_name + ~loc:__LOC__ + ~expected: + Protocol.Staking + .Cannot_stake_with_unfinalizable_unstake_requests_to_another_delegate + errs) + (stake "staker" Half) --> next_cycle (* The unstake request from changing delegates is now finalizable. *) - --> assert_failure - (check_balance_field "staker" `Unstaked_finalizable Tez.zero) + --> assert_failure_in_check_balance_field + ~loc:__LOC__ + "staker" + `Unstaked_finalizable + Tez.zero --> assert_success (* Can directly stake again, which automatically finalizes, even though the finalizable unstaked request is about a @@ -371,15 +427,34 @@ let change_delegate = having unstake requests to a previous delegate that cannot be finalized yet. Try again in a later cycle (no more than consensus_rights_delay + max_slashing_period)." *) - --> assert_failure (stake "staker" Half) + --> assert_failure + ~expected_error:(fun _ errs -> + Error_helpers.check_error_constructor_name + ~loc:__LOC__ + ~expected: + Protocol.Staking + .Cannot_stake_with_unfinalizable_unstake_requests_to_another_delegate + errs) + (stake "staker" Half) --> unstake "staker" Max_tez --> wait_n_cycles_f (unstake_wait -- 1) (* Still can't stake. *) --> check_balance_field "staker" `Unstaked_finalizable Tez.zero - --> assert_failure (stake "staker" Half) + --> assert_failure + ~expected_error:(fun _ errs -> + Error_helpers.check_error_constructor_name + ~loc:__LOC__ + ~expected: + Protocol.Staking + .Cannot_stake_with_unfinalizable_unstake_requests_to_another_delegate + errs) + (stake "staker" Half) --> next_cycle (* The unstake request from changing delegates is now finalizable. *) - --> assert_failure - (check_balance_field "staker" `Unstaked_finalizable Tez.zero) + --> assert_failure_in_check_balance_field + ~loc:__LOC__ + "staker" + `Unstaked_finalizable + Tez.zero --> assert_success (* Can directly stake again, which automatically finalizes, even though the finalizable unstaked request is about a @@ -472,7 +547,14 @@ let forbid_costaking = --> stake "staker" amount --> next_cycle (* External staking is now forbidden *) - --> assert_failure (stake "staker" amount) + --> assert_failure + ~expected_error:(fun _ errs -> + Error_helpers.check_error_constructor_name + ~loc:__LOC__ + ~expected: + Protocol.Apply.Staking_to_delegate_that_refuses_external_staking + errs) + (stake "staker" amount) (* Can still self-stake *) --> stake "delegate" amount (* Can still unstake *) @@ -483,7 +565,14 @@ let forbid_costaking = --> set_delegate_params "delegate" init_params --> wait_cycle_until `right_before_delegate_parameters_activation (* Not yet... *) - --> assert_failure (stake "staker" amount) + --> assert_failure + ~expected_error:(fun _ errs -> + Error_helpers.check_error_constructor_name + ~loc:__LOC__ + ~expected: + Protocol.Apply.Staking_to_delegate_that_refuses_external_staking + errs) + (stake "staker" amount) --> next_cycle (* Now possible *) --> stake "staker" amount @@ -494,13 +583,15 @@ let test_deactivation = let init_params = {limit_of_staking_over_baking = Q.one; edge_of_baking_over_staking = Q.one} in - let fail_if_deactivated delegate = + let check_deactivated_status ~loc ~expected delegate = let open Lwt_result_syntax in exec_unit (fun (block, state) -> let dlgt = State.find_account delegate state in let* deactivated = Context.Delegate.deactivated (B block) dlgt.pkh in - Assert.is_true ~loc:__LOC__ (not deactivated)) + Assert.equal_bool ~loc deactivated expected) in + let check_is_deactivated = check_deactivated_status ~expected:true in + let check_is_not_deactivated = check_deactivated_status ~expected:false in init_constants () --> set S.Adaptive_issuance.autostaking_enable false --> activate_ai `Force @@ -529,7 +620,14 @@ let test_deactivation = --> assert_success ~loc:__LOC__ (next_block_with_baker "delegate") --> next_cycle (* ...But not in the following cycle *) - --> assert_failure ~loc:__LOC__ (next_block_with_baker "delegate") + --> assert_failure + ~expected_error:(fun (_, state) errs -> + let delegate = State.find_account "delegate" state in + Error_helpers.expect_no_slots_found_for + ~loc:__LOC__ + ~pkh:delegate.pkh + errs) + (next_block_with_baker "delegate") (* The stakers still have stake, and can still stake/unstake *) --> check_balance_field "staker1" `Staked (Tez.of_mutez 100_000_000_000L) --> check_balance_field "staker2" `Staked (Tez.of_mutez 100_000_000_000L) @@ -539,14 +637,14 @@ let test_deactivation = (unstake "staker2" Half --> wait_n_cycles 5 --> finalize_unstake "staker2") (* We wait until the delegate is completely deactivated *) - --> assert_success ~loc:__LOC__ (fail_if_deactivated "delegate") + --> check_is_not_deactivated ~loc:__LOC__ "delegate" (* We already waited for [consensus_rights_delay] + 1 cycles since 0 stake, we must wait for [consensus_rights_delay] more. *) --> wait_n_cycles_f (fun (_, state) -> state.State.constants.consensus_rights_delay) - --> assert_success ~loc:__LOC__ (fail_if_deactivated "delegate") + --> check_is_not_deactivated ~loc:__LOC__ "delegate" --> next_cycle - --> assert_failure ~loc:__LOC__ (fail_if_deactivated "delegate") + --> check_is_deactivated ~loc:__LOC__ "delegate" --> next_cycle (* The stakers still have stake, and can still stake/unstake *) --> check_balance_field "staker1" `Staked (Tez.of_mutez 100_000_000_000L) @@ -561,11 +659,25 @@ let test_deactivation = --> set_delegate "delegate" (Some "delegate") --> stake "delegate" (Amount (Tez.of_mutez 2_000_000_000_000L)) (* It cannot bake right away *) - --> assert_failure ~loc:__LOC__ (next_block_with_baker "delegate") + --> assert_failure + ~expected_error:(fun (_, state) errs -> + let delegate = State.find_account "delegate" state in + Error_helpers.expect_no_slots_found_for + ~loc:__LOC__ + ~pkh:delegate.pkh + errs) + (next_block_with_baker "delegate") --> wait_n_cycles_f (fun (_, state) -> state.State.constants.consensus_rights_delay) (* After consensus_rights_delay, the delegate still has no rights... *) - --> assert_failure ~loc:__LOC__ (next_block_with_baker "delegate") + --> assert_failure + ~expected_error:(fun (_, state) errs -> + let delegate = State.find_account "delegate" state in + Error_helpers.expect_no_slots_found_for + ~loc:__LOC__ + ~pkh:delegate.pkh + errs) + (next_block_with_baker "delegate") --> next_cycle (* ...But has enough to bake in the following cycle *) --> assert_success ~loc:__LOC__ (next_block_with_baker "delegate")