diff --git a/src/proto_alpha/lib_protocol/test/helpers/context.ml b/src/proto_alpha/lib_protocol/test/helpers/context.ml index 898769a67d8ed684a7981ac88b958f0d89018505..06735bd2e6ef84d56ccdda6da589191136806745 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/context.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/context.ml @@ -829,12 +829,15 @@ let create_bootstrap_accounts ?algo n = let bootstrap_accounts = Account.make_bootstrap_accounts accounts in return (bootstrap_accounts, contracts) -let create_bootstrap_accounts_algo_list algo_list = +let create_bootstrap_accounts_algos_and_balances algos_and_balances = + let algo_list, bootstrap_balances = List.split algos_and_balances in let accounts = Account.generate_accounts_with_algo_list algo_list in let contracts = List.map (fun a -> Alpha_context.Contract.Implicit Account.(a.pkh)) accounts in - let bootstrap_accounts = Account.make_bootstrap_accounts accounts in + let bootstrap_accounts = + Account.make_bootstrap_accounts ~bootstrap_balances accounts + in (bootstrap_accounts, contracts) let init_with_constants_gen ?algo tup constants = @@ -852,12 +855,10 @@ let init_with_constants_gen ?algo tup constants = let init_with_constants_n ?algo constants n = init_with_constants_gen ?algo (TList n) constants -let init_with_constants_algo_list constants algo_list = +let init_with_constants_algos_and_balances constants algos_and_balances = let open Lwt_result_syntax in - let n = List.length algo_list in - let tup = TList n in let bootstrap_accounts, contracts = - create_bootstrap_accounts_algo_list algo_list + create_bootstrap_accounts_algos_and_balances algos_and_balances in let parameters = Tezos_protocol_alpha_parameters.Default_parameters.parameters_of_constants @@ -865,7 +866,7 @@ let init_with_constants_algo_list constants algo_list = constants in let* blk = Block.genesis_with_parameters parameters in - return (blk, tup_get tup contracts) + return (blk, contracts) let init_with_constants1 = init_with_constants_gen T1 diff --git a/src/proto_alpha/lib_protocol/test/helpers/context.mli b/src/proto_alpha/lib_protocol/test/helpers/context.mli index 7d9677cf8d1d6e65d014dc1c41b8cdcb4db8214c..60f55cc72a3c871a886596a5078199eb9d521776 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/context.mli +++ b/src/proto_alpha/lib_protocol/test/helpers/context.mli @@ -464,9 +464,9 @@ val init_with_constants_n : int -> (Block.t * Alpha_context.Contract.t list) tzresult Lwt.t -val init_with_constants_algo_list : +val init_with_constants_algos_and_balances : Constants.Parametric.t -> - Signature.algo option list -> + (Signature.algo option * int64) list -> (Block.t * Alpha_context.Contract.t list) tzresult Lwt.t val init_with_constants1 : diff --git a/src/proto_alpha/lib_protocol/test/helpers/scenario_begin.ml b/src/proto_alpha/lib_protocol/test/helpers/scenario_begin.ml index c48d6a2517fc64136134405a79fb9448ebe1c1f5..b09cb5de2ee0b4dc72a896929b64aa07d4632918 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/scenario_begin.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/scenario_begin.ml @@ -18,91 +18,121 @@ type error += Inconsistent_number_of_bootstrap_accounts type starter_constants = Mainnet | Sandbox | Test -let start ~(constants : starter_constants) : (unit, constants) scenarios = - let constants, name = - match constants with - | Mainnet -> (Default_parameters.constants_mainnet, "mainnet") - | Sandbox -> (Default_parameters.constants_sandbox, "sandbox") - | Test -> (Default_parameters.constants_test, "test") - in - Action - (fun () -> - Log.info ~color:begin_end_color "-- Begin test --" ; - Log.info "Loading constants_%s." name ; - Lwt_result_syntax.return constants) - -let start_with ~(constants : constants) : (unit, constants) scenarios = +let start_with ?(constants_name = "custom") (constants : constants) : + (unit, constants) scenarios = Action (fun () -> Log.info ~color:begin_end_color "-- Begin test --" ; - Log.info "Loading custom constants." ; + Log.info "Loading constants: %s." constants_name ; Lwt_result_syntax.return constants) -let start_with_list ~(constants : (string * constants) list) : +let start_with_list (list : (string * constants) list) : (unit, constants) scenarios = - match constants with + match list with | [] -> Stdlib.failwith (Format.asprintf "%s: Cannot build scenarios from empty list" __LOC__) - | _ -> fold_tag (fun constants -> start_with ~constants) constants + | _ -> + fold + (fun (constants_name, constants) -> + Tag ("constants: " ^ constants_name) + --> start_with ~constants_name constants) + list -(** Initializes the constants for testing, with well chosen default values. - Recommended over [start] or [start_with] *) -let init_constants ?(default = Test) ?(reward_per_block = 0L) +let start ~(constants : starter_constants) : (unit, constants) scenarios = + let constants, constants_name = + match constants with + | Mainnet -> (Default_parameters.constants_mainnet, "mainnet") + | Sandbox -> (Default_parameters.constants_sandbox, "sandbox") + | Test -> (Default_parameters.constants_test, "test") + in + start_with ~constants_name constants + +let default_constants ?(base = Test) ?(reward_per_block = 0L) ?(deactivate_dynamic = false) ?blocks_per_cycle ?delegate_parameters_activation_delay () = + let constants, constants_name = + match base with + | Mainnet -> (Default_parameters.constants_mainnet, "mainnet") + | Sandbox -> (Default_parameters.constants_sandbox, "sandbox") + | Test -> (Default_parameters.constants_test, "test") + in let base_total_issued_per_minute = Tez.of_mutez reward_per_block in - start ~constants:default - --> - (* default for tests: 12 *) - set_opt S.blocks_per_cycle blocks_per_cycle - --> set_opt - S.delegate_parameters_activation_delay - delegate_parameters_activation_delay - --> set - S.issuance_weights - { - base_total_issued_per_minute; - baking_reward_fixed_portion_weight = 1; - baking_reward_bonus_weight = 0; - attesting_reward_weight = 0; - seed_nonce_revelation_tip_weight = 0; - vdf_revelation_tip_weight = 0; - dal_rewards_weight = 0; - } - --> set S.liquidity_baking_subsidy Tez.zero - --> set S.minimal_block_delay Protocol.Alpha_context.Period.one_minute - --> set S.cost_per_byte Tez.zero - --> set S.consensus_threshold_size 0 - (* Make abaab never activate by default *) - --> set - S.all_bakers_attest_activation_threshold - {numerator = 2; denominator = 1} - --> - if deactivate_dynamic then - set + let set_opt f opt c = match opt with None -> c | Some x -> f x c in + let constants = + constants + |> + (* Note: It's 12 in Default_parameters.constants_test *) + set_opt S.blocks_per_cycle blocks_per_cycle + |> set_opt + S.delegate_parameters_activation_delay + delegate_parameters_activation_delay + |> S.issuance_weights + { + base_total_issued_per_minute; + baking_reward_fixed_portion_weight = 1; + baking_reward_bonus_weight = 0; + attesting_reward_weight = 0; + seed_nonce_revelation_tip_weight = 0; + vdf_revelation_tip_weight = 0; + dal_rewards_weight = 0; + } + |> S.liquidity_baking_subsidy Tez.zero + |> S.minimal_block_delay Protocol.Alpha_context.Period.one_minute + |> S.cost_per_byte Tez.zero + |> S.consensus_threshold_size 0 + (* Make abaab never activate by default *) + |> S.all_bakers_attest_activation_threshold {numerator = 2; denominator = 1} + in + let constants = + if deactivate_dynamic then S.Adaptive_issuance.Adaptive_rewards_params.max_bonus - (Protocol.Issuance_bonus_repr.max_bonus_parameter_of_Q_exn Q.zero) - else Empty + (Protocol.Issuance_bonus_repr.max_bonus_parameter_of_Q_exn Q.zero) + constants + else constants + in + (constants, constants_name) + +(** Initializes the constants for testing, with well chosen default values. + Recommended over [start] or [start_with] *) +let init_constants ?base ?reward_per_block ?deactivate_dynamic ?blocks_per_cycle + ?delegate_parameters_activation_delay () = + let constants, constants_name = + default_constants + ?base + ?reward_per_block + ?deactivate_dynamic + ?blocks_per_cycle + ?delegate_parameters_activation_delay + () + in + start_with ~constants_name constants + +type init_delegate_spec = { + name : string; + algo : Signature.algo option; (** None means a random non-BLS algo. *) + full_balance_mutez : int64 option; + (** None means {!Account.default_initial_full_balance}. *) +} (** Initialize the test, given some initial parameters. [algo] defines the algorithm used for the [delegates_name_list]. If not set, a random algorithm is selected for each. - To use a different algorithm for each delegate, use [delegates_with_algo] *) -let begin_test ?(delegates_with_algo = []) ?algo ?(burn_rewards = false) + To use a different algorithm for each delegate or non-default + initial balances, use [delegates_with_spec] *) +let begin_test ?(delegates_with_spec = []) ?algo ?(burn_rewards = false) ?(force_attest_all = false) ?(force_preattest_all = false) ?(check_finalized_every_block = []) ?(disable_default_checks = false) delegates_name_list : (constants, t) scenarios = exec (fun (constants : constants) -> let open Lwt_result_syntax in - let delegates_name_algo = - List.map (fun x -> (x, algo)) delegates_name_list - @ List.map (fun (x, algo) -> (x, Some algo)) delegates_with_algo - in - assert (not @@ List.is_empty delegates_name_algo) ; - let delegates_name_list, delegates_algo_list = - List.split delegates_name_algo + let delegates_specs = + List.map + (fun name -> {name; algo; full_balance_mutez = None}) + delegates_name_list + @ delegates_with_spec in + assert (not @@ List.is_empty delegates_specs) ; (* Do not disable default checks, unless for a good reason *) let check_finalized_every_block = if disable_default_checks then check_finalized_every_block @@ -112,15 +142,30 @@ let begin_test ?(delegates_with_algo = []) ?algo ?(burn_rewards = false) in (* Override threshold value if activate *) let* block, delegates = - Context.init_with_constants_algo_list constants delegates_algo_list + let default = Tez.to_mutez Account.default_initial_full_balance in + Context.init_with_constants_algos_and_balances + constants + (List.map + (fun {algo; full_balance_mutez; _} -> + (algo, Option.value full_balance_mutez ~default)) + delegates_specs) in let*? init_level = Context.get_level (B block) in let*? account_map = List.fold_left2 ~when_different_lengths:[Inconsistent_number_of_bootstrap_accounts] - (fun account_map name contract -> - let liquid = Account.default_initial_spendable_balance in - let init_staked = Account.default_initial_staked_balance in + (fun account_map {name; algo = _; full_balance_mutez} contract -> + let initial_full_balance = + match full_balance_mutez with + | Some v -> Tez.of_mutez v + | None -> Account.default_initial_full_balance + in + let init_staked = + Account.bootstrap_initial_staked_balance + ~constants + ~initial_full_balance + in + let liquid = Tez.(initial_full_balance -! init_staked) in let frozen_deposits = Frozen_tez.init init_staked name name in let frozen_rights = List.fold_left @@ -152,7 +197,7 @@ let begin_test ?(delegates_with_algo = []) ?algo ?(burn_rewards = false) Log.debug "Initial total balance: %a" Tez.pp total_balance ; account_map) String.Map.empty - delegates_name_list + delegates_specs delegates in let* total_supply = Context.get_total_supply (B block) in @@ -192,3 +237,8 @@ let begin_test ?(delegates_with_algo = []) ?algo ?(burn_rewards = false) else return_unit in return (block, state)) + +let bls_delegates names = + List.map + (fun name -> {name; algo = Some Bls; full_balance_mutez = None}) + names diff --git a/src/proto_alpha/lib_protocol/test/helpers/scenario_op.ml b/src/proto_alpha/lib_protocol/test/helpers/scenario_op.ml index bf05adda4e7f229e568f08313f9c3a4060f032f9..45ca31dc70067a55ad78870435db483342260b30 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/scenario_op.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/scenario_op.ml @@ -180,16 +180,18 @@ let set_delegate src_name delegate_name_opt : (t, t) scenarios = let stake src_name stake_value : (t, t) scenarios = exec_op (fun (block, state) -> let open Lwt_result_syntax in + (* Stake applies finalize *before* the stake *) + let state = State.apply_finalize src_name state in let src = State.find_account src_name state in - Log.info + let amount = quantity_to_tez src.liquid stake_value in + Log.report ~color:action_color - "[Stake for \"%s\" (%a)]" + "[Stake %atz for \"%s\" (%a)]" + Tez_helpers.pp + amount src_name tez_quantity_pp stake_value ; - (* Stake applies finalize *before* the stake *) - let state = State.apply_finalize src_name state in - let amount = quantity_to_tez src.liquid stake_value in let current_cycle = current_cycle block in let* operation = stake (B block) src.contract amount in let state = State.apply_stake amount current_cycle src_name state in diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_scenario_attestation.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_scenario_attestation.ml index c23e5bf60ed4f9f0951d5787c39e020e5f21849f..76ebfcf1918e110ccb92f80751ea2d1e2274973f 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_scenario_attestation.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_scenario_attestation.ml @@ -358,7 +358,7 @@ let test_consensus_threshold = --> set S.consensus_committee_size 1000 --> set S.consensus_threshold_size req_attestations --> begin_test - ~delegates_with_algo:[("delegate_1", Bls); ("delegate_2", Bls)] + ~delegates_with_spec:(bls_delegates ["delegate_1"; "delegate_2"]) ["delegate_3"] (* Genesis cannot be attested *) --> next_block @@ -412,7 +412,7 @@ let test_include_valid_dal_content = --> set S.Dal.number_of_slots number_of_slots --> set S.consensus_rights_delay consensus_rights_delay --> begin_test - ~delegates_with_algo:[("delegate_1", Bls); ("delegate_2", Bls)] + ~delegates_with_spec:(bls_delegates ["delegate_1"; "delegate_2"]) ["delegate_3"] --> next_block (* setup companion keys *) 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 9fa7497533704ff350d547e8918d2c9e203f5caa..ca7f5a251c52bbd964c53e7b8897ec8106b12722 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 @@ -56,75 +56,190 @@ let check_cannot_bake_next_block ~loc src_name = let check_can_bake_next_block ~loc src_name = assert_success ~loc (next_block_with_baker src_name) +let with_full_balance_mutez names_and_balances = + List.map + (fun (name, balance) -> + {name; algo = None; full_balance_mutez = Some balance}) + names_and_balances + +type init = { + scenario_init : (unit, Scenario_base.t) scenarios; + constants : constants; + expected_tolerated_inactivity_period : int; + delegate : string * Tez.t; + baker : string * Tez.t; +} + +let init_with_various_delegate_portion = + let constants, constants_name = default_constants () in + let threshold_thousandth = constants.tolerated_inactivity_period_threshold in + [ + ( "high power", + (threshold_thousandth + 1, constants.tolerated_inactivity_period_low) ); + ( "power exactly at tolerated inactivity period threshold", + (threshold_thousandth, constants.tolerated_inactivity_period_high) ); + ( "low power", + (threshold_thousandth - 1, constants.tolerated_inactivity_period_high) ); + ("very high power", (500, constants.tolerated_inactivity_period_low)); + ] + |> List.map + (fun + (tag, (delegate_share_thousandth, expected_tolerated_inactivity_period)) + -> + assert (delegate_share_thousandth > 0) ; + let thousandth_of_total_mutez = + (* Largely enough to exceed [minimal_stake] and [minimal_frozen_stake]. *) + 1_000_000_000_000L + in + let delegate_full_balance_mutez = + Int64.( + mul (of_int delegate_share_thousandth) thousandth_of_total_mutez) + in + let baker_full_balance_mutez = + Int64.( + sub + (mul 1000L thousandth_of_total_mutez) + delegate_full_balance_mutez) + in + let delegates_with_spec = + with_full_balance_mutez + [ + ("delegate", delegate_full_balance_mutez); + ("baker", baker_full_balance_mutez); + ] + in + let delegate_initial_balance = + Tez.of_mutez delegate_full_balance_mutez + in + let baker_initial_balance = Tez.of_mutez baker_full_balance_mutez in + let scenario_init = + Tag tag + --> log + ~level:Report + "Init: delegate with %s\n\ + delegate full balance = %atz (%d/1000 of total)\n\ + baker full balance = %atz (%d/1000 of total)\n\ + Expected tolerated inactivity period = %d cycle(s)" + tag + Tez.pp + delegate_initial_balance + delegate_share_thousandth + Tez.pp + baker_initial_balance + (1000 - delegate_share_thousandth) + expected_tolerated_inactivity_period + --> start_with ~constants_name constants + --> begin_test ~delegates_with_spec [] + in + { + scenario_init; + constants; + expected_tolerated_inactivity_period; + delegate = ("delegate", delegate_initial_balance); + baker = ("baker", baker_initial_balance); + }) + (** Test that a delegate gets deactivated after a set period of time if it is not baking. Test that the frozen funds stay frozen, and the delegate can still issue staking operations without reactivating. *) let test_simple_scenario = - init_constants () - --> begin_test ["delegate"; "baker"] - --> check_balance_field "delegate" `Staked (Tez.of_mutez 200_000_000_000L) - --> set_baker "baker" - --> wait_n_cycles_f (fun (_, state) -> - (* at activation, accounts have a low tolerance grace period *) - state.State.constants.consensus_rights_delay - + state.State.constants.tolerated_inactivity_period_low) - --> check_balance_field "delegate" `Staked (Tez.of_mutez 200_000_000_000L) - --> check_is_active ~loc:__LOC__ "delegate" - --> next_cycle - --> check_is_not_active ~loc:__LOC__ "delegate" - --> check_balance_field "delegate" `Staked (Tez.of_mutez 200_000_000_000L) - --> check_balance_field "delegate" `Unstaked_frozen_total Tez.zero - --> unstake "delegate" All - --> wait_n_cycles_f Test_scenario_stake.unstake_wait - --> finalize_unstake "delegate" - --> check_balance_field "delegate" `Unstaked_finalizable Tez.zero - --> check_balance_field "delegate" `Staked Tez.zero - --> check_balance_field "delegate" `Liquid (Tez.of_mutez 4_000_000_000_000L) - --> check_is_not_active ~loc:__LOC__ "delegate" - --> stake "delegate" Half --> next_cycle - --> check_is_not_active ~loc:__LOC__ "delegate" + init_with_various_delegate_portion + |> fold + @@ + fun { + scenario_init; + constants; + expected_tolerated_inactivity_period; + delegate = delegate, delegate_initial_balance; + baker = baker, baker_initial_balance; + } + -> + let init_staked = + Account.bootstrap_initial_staked_balance + ~constants + ~initial_full_balance:delegate_initial_balance + in + scenario_init + --> check_balance_field delegate `Staked init_staked + --> set_baker baker + --> wait_n_cycles + ((* at activation, accounts have a low tolerance grace period *) + constants.consensus_rights_delay + + expected_tolerated_inactivity_period) + --> check_balance_field delegate `Staked init_staked + --> check_balance_field baker `Total baker_initial_balance + --> check_is_active ~loc:__LOC__ delegate + --> next_cycle + --> check_is_not_active ~loc:__LOC__ delegate + --> check_balance_field delegate `Staked init_staked + --> check_balance_field delegate `Unstaked_frozen_total Tez.zero + --> unstake delegate All + --> wait_n_cycles_f Test_scenario_stake.unstake_wait + --> finalize_unstake delegate + --> check_balance_field delegate `Unstaked_finalizable Tez.zero + --> check_balance_field delegate `Staked Tez.zero + --> check_balance_field delegate `Liquid delegate_initial_balance + --> check_is_not_active ~loc:__LOC__ delegate + --> stake delegate Half --> next_cycle + --> check_is_not_active ~loc:__LOC__ delegate + --> check_balance_field baker `Total baker_initial_balance (** Test that a delegate can be deactivated by setting its frozen funds to 0. Test that a delegate can be activated while having no rights. Test that a delegate can be deactivated while having rights, and test that it can still bake while deactivated, hence reactivating *) let test_baking_deactivation = - init_constants () - --> begin_test ["delegate"; "baker"] - --> unstake "delegate" All - --> wait_n_cycles_f (fun (_, state) -> - (* at activation, accounts have a low tolerance grace period *) - state.State.constants.consensus_rights_delay - + state.State.constants.tolerated_inactivity_period_low) - --> check_is_active ~loc:__LOC__ "delegate" - --> next_cycle - --> check_is_not_active ~loc:__LOC__ "delegate" - (* Reactivate and wait for rights *) - --> stake "delegate" Half - --> (Tag "Reactivate in next block" --> Empty - |+ Tag "Reactivate in next cycle" --> exec bake_until_dawn_of_next_cycle - |+ Tag "Reactivate in last block" - --> exec bake_until_next_cycle_end_but_one) - --> set_delegate "delegate" (Some "delegate") - (* No rights yet, but active *) - --> check_cannot_bake_next_block ~loc:__LOC__ "delegate" - --> check_is_active ~loc:__LOC__ "delegate" - --> wait_n_cycles_f (fun (_, state) -> - state.State.constants.consensus_rights_delay + 1) - --> check_is_active ~loc:__LOC__ "delegate" - --> next_block_with_baker "delegate" - (* Get deactivated by doing nothing *) - --> set_baker "baker" - --> wait_tolerated_inactivity_period "delegate" - --> check_is_active ~loc:__LOC__ "delegate" - --> next_cycle - --> check_is_not_active ~loc:__LOC__ "delegate" - (* The delegate still has enough rights to bake... *) - --> check_has_rights ~loc:__LOC__ "delegate" - --> next_block_with_baker "delegate" - --> check_is_active ~loc:__LOC__ "delegate" + init_with_various_delegate_portion + |> fold + @@ + fun { + scenario_init; + constants; + expected_tolerated_inactivity_period = _; + delegate = delegate, delegate_initial_balance; + baker = baker, _baker_initial_balance; + } + -> + scenario_init --> unstake delegate All + --> wait_n_cycles + ((* at activation, accounts have a low tolerance grace period *) + constants.consensus_rights_delay + + constants.tolerated_inactivity_period_low) + --> check_is_active ~loc:__LOC__ delegate + --> next_cycle + --> check_is_not_active ~loc:__LOC__ delegate + (* Reactivate and wait for rights *) + --> stake + delegate + (Amount + (Account.bootstrap_initial_staked_balance + ~constants + ~initial_full_balance:delegate_initial_balance)) + --> (Tag "Reactivate in next block" --> Empty + |+ Tag "Reactivate in next cycle" + --> exec bake_until_dawn_of_next_cycle + |+ Tag "Reactivate in last block" + --> exec bake_until_next_cycle_end_but_one) + --> set_delegate delegate (Some delegate) + (* No rights yet, but active *) + --> check_cannot_bake_next_block ~loc:__LOC__ delegate + --> check_is_active ~loc:__LOC__ delegate + --> wait_n_cycles_f (fun (_, state) -> + state.State.constants.consensus_rights_delay + 1) + --> check_is_active ~loc:__LOC__ delegate + --> next_block_with_baker delegate + (* Get deactivated by doing nothing *) + --> set_baker baker + --> wait_tolerated_inactivity_period delegate + --> check_is_active ~loc:__LOC__ delegate + --> next_cycle + --> check_is_not_active ~loc:__LOC__ delegate + (* The delegate still has enough rights to bake... *) + --> check_has_rights ~loc:__LOC__ delegate + --> next_block_with_baker delegate + --> check_is_active ~loc:__LOC__ delegate let test_deactivation_timing = let staked_balance_no_change =