diff --git a/src/proto_alpha/lib_parameters/default_parameters.ml b/src/proto_alpha/lib_parameters/default_parameters.ml index fe0288aa9a1d1f5a8e5290fbcf315884e2fd0e9d..abc8d6ba7595b4a17b1d03e591c224c1ccf2c48f 100644 --- a/src/proto_alpha/lib_parameters/default_parameters.ml +++ b/src/proto_alpha/lib_parameters/default_parameters.ml @@ -59,8 +59,9 @@ let constants_mainnet = delay_per_missing_endorsement = Period.of_seconds_exn 4L; (* liquidity_baking_subsidy is 1/16th of total rewards for a block of priority 0 with all endorsements *) liquidity_baking_subsidy = Tez.of_mutez_exn 2_500_000L; - (* level after protocol activation when liquidity baking shuts off *) - liquidity_baking_sunset_level = 525600l; + (* level after protocol activation when liquidity baking shuts off: + about 6 months after first activation on mainnet *) + liquidity_baking_sunset_level = 2_032_928l; (* 1/2 window size of 2000 blocks with precision of 1000 for integer computation *) liquidity_baking_escape_ema_threshold = 1_000_000l; } diff --git a/src/proto_alpha/lib_protocol/raw_context.ml b/src/proto_alpha/lib_protocol/raw_context.ml index a76d77b90f19ddc3ba30253c0b39779c2130c75e..f7694fe57e3e14f45ff4f2d6df7812c654f12bd1 100644 --- a/src/proto_alpha/lib_protocol/raw_context.ml +++ b/src/proto_alpha/lib_protocol/raw_context.ml @@ -879,7 +879,8 @@ let prepare_first_block ~level ~timestamp ~fitness ctxt = ( if mainnet_constants then Period_repr.of_seconds_exn 4L else c.delay_per_missing_endorsement ); liquidity_baking_subsidy = Tez_repr.of_mutez_exn 2_500_000L; - liquidity_baking_sunset_level = 525600l; + (* Approximately 6 month after the first activation of Liquidity Baking on mainnet *) + liquidity_baking_sunset_level = 2_032_928l; liquidity_baking_escape_ema_threshold = 1_000_000l; } in diff --git a/src/proto_alpha/lib_protocol/test/helpers/assert.ml b/src/proto_alpha/lib_protocol/test/helpers/assert.ml index deb9a9919aef835e95b8ca050267c7173980ee51..80f2d77563992be857fd86371d08765e31b57e94 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/assert.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/assert.ml @@ -74,6 +74,18 @@ let equal_int32 ~loc (a : int32) (b : int32) = let not_equal_int ~loc (a : int) (b : int) = not_equal ~loc ( = ) "Integers are equal" Format.pp_print_int a b +let leq_int ~loc (a : int) (b : int) = + if a > b then + failwith + "@[@[[%s]@] - @[Integers aren't less than or equal : %a is greater \ + than %a@]@]" + loc + Format.pp_print_int + a + Format.pp_print_int + b + else return_unit + (* int64 *) let equal_int64 ~loc (a : int64) (b : int64) = equal diff --git a/src/proto_alpha/lib_protocol/test/helpers/block.ml b/src/proto_alpha/lib_protocol/test/helpers/block.ml index 723e22579079f8ca8fcc2579561b31d7bb51c665..5d6bcadd43804b04f378254c94c6c36d1527e463 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/block.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/block.ml @@ -417,7 +417,7 @@ let validate_initial_accounts (initial_accounts : (Account.t * Tez.t) list) let prepare_initial_context_params ?endorsers_per_block ?initial_endorsers ?time_between_blocks ?minimal_block_delay ?delay_per_missing_endorsement - ?min_proposal_quorum initial_accounts = + ?min_proposal_quorum ?level initial_accounts = let open Tezos_protocol_alpha_parameters in let constants = Default_parameters.constants_test in let endorsers_per_block = @@ -473,7 +473,7 @@ let prepare_initial_context_params ?endorsers_per_block ?initial_endorsers in let shell = Forge.make_shell - ~level:0l + ~level:(Option.value ~default:0l level) ~predecessor:hash ~timestamp:Time.Protocol.epoch ~fitness:(Fitness.from_int64 0L) @@ -487,7 +487,7 @@ let prepare_initial_context_params ?endorsers_per_block ?initial_endorsers where the test is run *) let genesis ?with_commitments ?endorsers_per_block ?initial_endorsers ?min_proposal_quorum ?time_between_blocks ?minimal_block_delay - ?delay_per_missing_endorsement ?bootstrap_contracts + ?delay_per_missing_endorsement ?bootstrap_contracts ?level (initial_accounts : (Account.t * Tez.t) list) = prepare_initial_context_params ?endorsers_per_block @@ -496,6 +496,7 @@ let genesis ?with_commitments ?endorsers_per_block ?initial_endorsers ?time_between_blocks ?minimal_block_delay ?delay_per_missing_endorsement + ?level initial_accounts >>=? fun (constants, shell, hash) -> initial_context @@ -625,6 +626,39 @@ let bake_n_with_all_balance_updates ?policy ?liquidity_baking_escape_vote n b = (1 -- n) >|=? fun (b, balance_updates_rev) -> (b, List.rev balance_updates_rev) +let bake_n_with_origination_results ?policy n b = + List.fold_left_es + (fun (b, origination_results_rev) _ -> + bake_with_metadata ?policy b + >>=? fun (b, metadata) -> + let origination_results_rev = + List.fold_left + (fun origination_results_rev -> + let open Apply_results in + function + | Successful_manager_result (Reveal_result _) + | Successful_manager_result (Delegation_result _) + | Successful_manager_result (Transaction_result _) -> + origination_results_rev + | Successful_manager_result (Origination_result x) -> + Origination_result x :: origination_results_rev) + origination_results_rev + metadata.implicit_operations_results + in + return (b, origination_results_rev)) + (b, []) + (1 -- n) + >|=? fun (b, origination_results_rev) -> (b, List.rev origination_results_rev) + +let bake_n_with_liquidity_baking_escape_ema ?policy + ?liquidity_baking_escape_vote n b = + List.fold_left_es + (fun (b, _escape_ema) _ -> + bake_with_metadata ?policy ?liquidity_baking_escape_vote b + >|=? fun (b, metadata) -> (b, metadata.liquidity_baking_escape_ema)) + (b, 0l) + (1 -- n) + let bake_until_cycle_end ?policy b = get_constants b >>=? fun Constants.{parametric = {blocks_per_cycle; _}; _} -> diff --git a/src/proto_alpha/lib_protocol/test/helpers/block.mli b/src/proto_alpha/lib_protocol/test/helpers/block.mli index 0dc051b18c4d38ed785d518e11115397c8296faa..9891042c846bbe8b687460a4562f1bc0ecdf0dba 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/block.mli +++ b/src/proto_alpha/lib_protocol/test/helpers/block.mli @@ -104,6 +104,7 @@ val genesis : ?minimal_block_delay:Period.t -> ?delay_per_missing_endorsement:Period.t -> ?bootstrap_contracts:Parameters.bootstrap_contract list -> + ?level:int32 -> (Account.t * Tez.tez) list -> block tzresult Lwt.t @@ -163,6 +164,27 @@ val bake_n_with_all_balance_updates : t -> (block * Alpha_context.Receipt.balance_updates) tzresult Lwt.t +(** Version of bake_n that returns a list of all origination results + in the metadata of baked blocks. **) +val bake_n_with_origination_results : + ?policy:baker_policy -> + int -> + t -> + ( block + * Alpha_context.Kind.origination + Apply_results.successful_manager_operation_result + list ) + tzresult + Lwt.t + +(** Version of bake_n that returns the liquidity baking escape EMA after [n] blocks. **) +val bake_n_with_liquidity_baking_escape_ema : + ?policy:baker_policy -> + ?liquidity_baking_escape_vote:bool -> + int -> + t -> + (block * Alpha_context.Liquidity_baking.escape_ema) tzresult Lwt.t + val current_cycle : t -> Cycle.t tzresult Lwt.t (** Given a block [b] at level [l] bakes enough blocks to complete a cycle, @@ -184,6 +206,7 @@ val prepare_initial_context_params : ?minimal_block_delay:Period.t -> ?delay_per_missing_endorsement:Period.t -> ?min_proposal_quorum:int32 -> + ?level:int32 -> (Account.t * Tez.t) list -> ( Constants.parametric * Block_header.shell_header * Block_hash.t, tztrace ) diff --git a/src/proto_alpha/lib_protocol/test/helpers/context.ml b/src/proto_alpha/lib_protocol/test/helpers/context.ml index 54bb0f134de61cbf8eb38f1d03c2bc2b081b6e28..08db117fe2b3a10eeceb1c96e25a21908e7ec8e3 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/context.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/context.ml @@ -219,6 +219,8 @@ end module Contract = struct let pp = Alpha_context.Contract.pp + let equal a b = Alpha_context.Contract.compare a b = 0 + let pkh c = Alpha_context.Contract.is_implicit c |> function @@ -322,8 +324,8 @@ end let init ?endorsers_per_block ?with_commitments ?(initial_balances = []) ?initial_endorsers ?min_proposal_quorum ?time_between_blocks - ?minimal_block_delay ?delay_per_missing_endorsement ?bootstrap_contracts n - = + ?minimal_block_delay ?delay_per_missing_endorsement ?bootstrap_contracts + ?level n = let accounts = Account.generate_accounts ~initial_balances n in let contracts = List.map @@ -339,5 +341,6 @@ let init ?endorsers_per_block ?with_commitments ?(initial_balances = []) ?minimal_block_delay ?delay_per_missing_endorsement ?bootstrap_contracts + ?level accounts >|=? fun blk -> (blk, contracts) diff --git a/src/proto_alpha/lib_protocol/test/helpers/context.mli b/src/proto_alpha/lib_protocol/test/helpers/context.mli index ad64c3c3d09836cd3559ffe3fc29e290c141f5f9..e8951f51a2156cabb7d56191ee640469d2d455bb 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/context.mli +++ b/src/proto_alpha/lib_protocol/test/helpers/context.mli @@ -94,6 +94,8 @@ end module Contract : sig val pp : Format.formatter -> Contract.t -> unit + val equal : Contract.t -> Contract.t -> bool + val pkh : Contract.t -> public_key_hash tzresult Lwt.t type balance_kind = Main | Deposit | Fees | Rewards @@ -148,5 +150,6 @@ val init : ?minimal_block_delay:Period.t -> ?delay_per_missing_endorsement:Period.t -> ?bootstrap_contracts:Parameters.bootstrap_contract list -> + ?level:int32 -> int -> (Block.t * Alpha_context.Contract.t list) tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/test/test_liquidity_baking.ml b/src/proto_alpha/lib_protocol/test/test_liquidity_baking.ml index d15b8cce3f2985f70864a61f6a2463cb644307b4..d9225e6ded0572ccdbcd3d019bff5395f20dfce0 100644 --- a/src/proto_alpha/lib_protocol/test/test_liquidity_baking.ml +++ b/src/proto_alpha/lib_protocol/test/test_liquidity_baking.ml @@ -218,6 +218,35 @@ let liquidity_baking_escape_hatch_50 n () = expected_balance >>=? fun () -> return_unit +(* Test that the escape EMA in block metadata is correct. *) +let liquidity_baking_escape_ema n_vote_false n_vote_true escape_level + bake_after_escape expected_escape_ema () = + Context.init 1 + >>=? fun (blk, _contracts) -> + let rec bake_escaping blk i = + if i < escape_level then + Block.bake_n n_vote_false blk + >>=? fun blk -> + Block.bake_n ~liquidity_baking_escape_vote:true n_vote_true blk + >>=? fun blk -> bake_escaping blk (i + n_vote_false + n_vote_true) + else return blk + in + bake_escaping blk 0 + >>=? fun blk -> + (* We only need to return the escape EMA at the end. *) + Block.bake_n_with_liquidity_baking_escape_ema bake_after_escape blk + >>=? fun (_blk, escape_ema) -> + Assert.leq_int ~loc:__LOC__ (Int32.to_int escape_ema) expected_escape_ema + >>=? fun () -> return_unit + +(* With no bakers setting the escape vote, EMA should be zero. *) +let liquidity_baking_escape_ema_zero () = + liquidity_baking_escape_ema 0 0 0 100 0 () + +(* The EMA should be not much over the threshold after the escape hatch has been activated. We add 50_000 to the constant to give room for the last update. *) +let liquidity_baking_escape_ema_threshold () = + liquidity_baking_escape_ema 0 1 1387 1 1_050_000 () + let liquidity_baking_storage n () = Context.init 1 >>=? fun (blk, _contracts) -> @@ -290,6 +319,107 @@ let liquidity_baking_balance_update () = ((Int32.to_int sunset - 1) * Int64.to_int (Tez.to_mutez subsidy)) >>=? fun () -> return_unit +let get_cpmm_result results = + match results with + | cpmm_result :: _results -> + cpmm_result + | _ -> + assert false + +let get_lqt_result results = + match results with + | _cpmm_result :: lqt_result :: _results -> + lqt_result + | _ -> + assert false + +let get_address_in_result result = + match result with + | Apply_results.Origination_result {originated_contracts; _} -> ( + match originated_contracts with [c] -> c | _ -> assert false ) + +let get_balance_update_in_result result = + match result with + | Apply_results.Origination_result {balance_updates; _} -> ( + match balance_updates with + | [(Contract _, Credited balance, Protocol_migration)] -> + balance + | _ -> + assert false ) + +let liquidity_baking_origination_result_cpmm_address () = + Context.init 1 + >>=? fun (blk, _contracts) -> + Context.get_liquidity_baking_cpmm_address (B blk) + >>=? fun cpmm_address_in_storage -> + Block.bake_n_with_origination_results 1 blk + >>=? fun (_blk, origination_results) -> + let result = get_cpmm_result origination_results in + let address = get_address_in_result result in + Assert.equal + ~loc:__LOC__ + Context.Contract.equal + "CPMM address in storage is not the same as in origination result" + Context.Contract.pp + address + cpmm_address_in_storage + >>=? fun () -> return_unit + +let liquidity_baking_origination_result_cpmm_balance () = + Context.init 1 + >>=? fun (blk, _contracts) -> + Block.bake_n_with_origination_results 1 blk + >>=? fun (_blk, origination_results) -> + let result = get_cpmm_result origination_results in + let balance_update = get_balance_update_in_result result in + Assert.equal_tez ~loc:__LOC__ balance_update Tez.(of_mutez_exn 100L) + >>=? fun () -> return_unit + +let liquidity_baking_origination_result_lqt_address () = + Context.init 1 + >>=? fun (blk, _contracts) -> + Block.bake_n_with_origination_results 1 blk + >>=? fun (_blk, origination_results) -> + let result = get_lqt_result origination_results in + let address = get_address_in_result result in + Assert.equal + ~loc:__LOC__ + String.equal + "LQT address in origination result is incorrect" + Format.pp_print_string + (Alpha_context.Contract.to_b58check address) + "KT1AafHA1C1vk959wvHWBispY9Y2f3fxBUUo" + >>=? fun () -> return_unit + +let liquidity_baking_origination_result_lqt_balance () = + Context.init 1 + >>=? fun (blk, _contracts) -> + Block.bake_n_with_origination_results 1 blk + >>=? fun (_blk, origination_results) -> + let result = get_lqt_result origination_results in + let balance_update = get_balance_update_in_result result in + Assert.equal_tez ~loc:__LOC__ balance_update Tez.zero + >>=? fun () -> return_unit + +(* Test that with no contract at the tzBTC address and the level low enough to indicate we're not on mainnet, three contracts are originated in stitching. *) +let liquidity_baking_origination_test_migration () = + Context.init 1 + >>=? fun (blk, _contracts) -> + Block.bake_n_with_origination_results 1 blk + >>=? fun (_blk, origination_results) -> + let num_results = List.length origination_results in + Assert.equal_int ~loc:__LOC__ num_results 3 + +(* Test that with no contract at the tzBTC address and the level high enough to indicate we could be on mainnet, no contracts are originated in stitching. *) +let liquidity_baking_origination_no_tzBTC_mainnet_migration () = + Context.init 1 ~level:1_437_862l + >>=? fun (blk, _contracts) -> + (* By baking a bit we also check that the subsidy application with no CPMM present does nothing rather than stopping the chain.*) + Block.bake_n_with_origination_results 64 blk + >>=? fun (_blk, origination_results) -> + let num_results = List.length origination_results in + Assert.equal_int ~loc:__LOC__ num_results 0 + let tests = [ Test_services.tztest "test liquidity baking script hashes" @@ -378,6 +508,16 @@ let tests = 50% and baking 100 blocks longer" `Quick (liquidity_baking_escape_hatch_50 100); + Test_services.tztest + "test liquidity baking escape ema in block metadata is zero with no \ + bakers flagging." + `Quick + liquidity_baking_escape_ema_zero; + Test_services.tztest + "test liquidity baking escape ema is equal to the threshold after the \ + escape hatch has been activated" + `Quick + liquidity_baking_escape_ema_threshold; Test_services.tztest "test liquidity baking storage is updated" `Quick @@ -385,4 +525,31 @@ let tests = Test_services.tztest "test liquidity baking balance updates" `Quick - liquidity_baking_balance_update ] + liquidity_baking_balance_update; + Test_services.tztest + "test liquidity baking CPMM address in storage matches address in the \ + origination result" + `Quick + liquidity_baking_origination_result_cpmm_address; + Test_services.tztest + "test liquidity baking CPMM balance in origination result is 100 mutez" + `Quick + liquidity_baking_origination_result_cpmm_balance; + Test_services.tztest + "test liquidity baking LQT contract is originated at expected address" + `Quick + liquidity_baking_origination_result_lqt_address; + Test_services.tztest + "test liquidity baking LQT balance in origination result is 0 mutez" + `Quick + liquidity_baking_origination_result_lqt_balance; + Test_services.tztest + "test liquidity baking originates three contracts when tzBTC does not \ + exist and level indicates we are not on mainnet" + `Quick + liquidity_baking_origination_test_migration; + Test_services.tztest + "test liquidity baking originates three contracts when tzBTC does not \ + exist and level indicates we might be on mainnet" + `Quick + liquidity_baking_origination_no_tzBTC_mainnet_migration ]