From 427c6f4e166eb48bea3beac0239d24eaa7b6fb79 Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Wed, 6 Mar 2024 11:19:33 +0100 Subject: [PATCH 1/4] Proto/tests: expose double attestation percentage function --- src/proto_alpha/lib_protocol/slash_percentage.ml | 4 ++++ src/proto_alpha/lib_protocol/slash_percentage.mli | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/src/proto_alpha/lib_protocol/slash_percentage.ml b/src/proto_alpha/lib_protocol/slash_percentage.ml index 68c9f28c94aa..83e31903dab7 100644 --- a/src/proto_alpha/lib_protocol/slash_percentage.ml +++ b/src/proto_alpha/lib_protocol/slash_percentage.ml @@ -44,3 +44,7 @@ let get ctxt ~(kind : Misbehaviour_repr.kind) ~(level : Level_repr.t) | Double_attesting | Double_preattesting -> let* ctxt, rights = Delegate_sampler.attesting_rights_count ctxt level in return (ctxt, for_double_attestation ctxt rights denounced) + +module Internal_for_tests = struct + let for_double_attestation = for_double_attestation +end diff --git a/src/proto_alpha/lib_protocol/slash_percentage.mli b/src/proto_alpha/lib_protocol/slash_percentage.mli index 2f8a71eca851..3a557576f44c 100644 --- a/src/proto_alpha/lib_protocol/slash_percentage.mli +++ b/src/proto_alpha/lib_protocol/slash_percentage.mli @@ -20,3 +20,11 @@ val get : level:Level_repr.t -> Signature.public_key_hash list -> (Raw_context.t * Percentage.t) tzresult Lwt.t + +module Internal_for_tests : sig + val for_double_attestation : + Raw_context.t -> + int Signature.Public_key_hash.Map.t -> + Signature.Public_key_hash.t list -> + Percentage.t +end -- GitLab From 44ea30bdce731b471f1deeb9bddbe629164d1599 Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Wed, 6 Mar 2024 11:20:02 +0100 Subject: [PATCH 2/4] Proto/tests: add raw context maker helper --- src/proto_alpha/lib_protocol/test/helpers/context.ml | 8 ++++++-- src/proto_alpha/lib_protocol/test/helpers/context.mli | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/proto_alpha/lib_protocol/test/helpers/context.ml b/src/proto_alpha/lib_protocol/test/helpers/context.ml index 12d314d5e741..3682337bdb41 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/context.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/context.ml @@ -744,7 +744,7 @@ let init_with_parameters1 = init_with_parameters_gen T1 let init_with_parameters2 = init_with_parameters_gen T2 -let default_raw_context () = +let raw_context_from_constants constants = let open Lwt_result_wrap_syntax in let open Tezos_protocol_alpha_parameters in let initial_account = Account.new_account () in @@ -753,7 +753,6 @@ let default_raw_context () = ~balance:(Tez.of_mutez_exn 100_000_000_000L) initial_account in - let* constants, _, _ = Block.prepare_initial_context_params () in let parameters = Default_parameters.parameters_of_constants ~bootstrap_accounts:[bootstrap_accounts] @@ -786,3 +785,8 @@ let default_raw_context () = ~typecheck_smart_rollup in return e + +let default_raw_context () = + let open Lwt_result_wrap_syntax in + let* constants, _, _ = Block.prepare_initial_context_params () in + raw_context_from_constants constants diff --git a/src/proto_alpha/lib_protocol/test/helpers/context.mli b/src/proto_alpha/lib_protocol/test/helpers/context.mli index 92208504ac30..dff28b54f6a0 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/context.mli +++ b/src/proto_alpha/lib_protocol/test/helpers/context.mli @@ -458,3 +458,8 @@ val init_with_parameters2 : (** [default_raw_context] returns a [Raw_context.t] for use in tests below [Alpha_context] *) val default_raw_context : unit -> Raw_context.t tzresult Lwt.t + +(** [raw_context_from_constants] returns a [Raw_context.t] for use in tests + below [Alpha_context] *) +val raw_context_from_constants : + Constants.Parametric.t -> Raw_context.t tzresult Lwt.t -- GitLab From be4ba58087f311f9051b50a7957559a7390bf1b9 Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Wed, 6 Mar 2024 11:21:10 +0100 Subject: [PATCH 3/4] Proto/tests: add new unit test file --- manifest/product_octez.ml | 1 + src/proto_alpha/lib_protocol/test/unit/dune | 3 ++- .../test/unit/test_slashing_percentage.ml | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 src/proto_alpha/lib_protocol/test/unit/test_slashing_percentage.ml diff --git a/manifest/product_octez.ml b/manifest/product_octez.ml index fc618ceaafe8..01649a24ab1d 100644 --- a/manifest/product_octez.ml +++ b/manifest/product_octez.ml @@ -5113,6 +5113,7 @@ end = struct ("test_adaptive_issuance_ema", N.(number >= 018)); ("test_percentage", N.(number >= 019)); ("test_full_staking_balance_repr", N.(number >= 020)); + ("test_slashing_percentage", N.(number >= 020)); ] |> conditional_list in diff --git a/src/proto_alpha/lib_protocol/test/unit/dune b/src/proto_alpha/lib_protocol/test/unit/dune index d10fc0f9abe5..d1847c6d9dc8 100644 --- a/src/proto_alpha/lib_protocol/test/unit/dune +++ b/src/proto_alpha/lib_protocol/test/unit/dune @@ -75,7 +75,8 @@ test_adaptive_issuance test_adaptive_issuance_ema test_percentage - test_full_staking_balance_repr)) + test_full_staking_balance_repr + test_slashing_percentage)) (executable (name main) diff --git a/src/proto_alpha/lib_protocol/test/unit/test_slashing_percentage.ml b/src/proto_alpha/lib_protocol/test/unit/test_slashing_percentage.ml new file mode 100644 index 000000000000..e2cfe77c7c42 --- /dev/null +++ b/src/proto_alpha/lib_protocol/test/unit/test_slashing_percentage.ml @@ -0,0 +1,14 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs, *) +(* *) +(*****************************************************************************) + +(** Testing + ------- + Component: Protocol (quantities) + Invocation: dune exec src/proto_alpha/lib_protocol/test/unit/main.exe \ + -- --file test_slashing_percentage.ml + Subject: On slashing double attestations. +*) -- GitLab From 794c310fa0f16d1fff0011c0e3fe016c9b9642d1 Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Wed, 6 Mar 2024 14:37:31 +0100 Subject: [PATCH 4/4] Proto/tests: add adaptive slashing unit tests --- .../test/unit/test_slashing_percentage.ml | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) diff --git a/src/proto_alpha/lib_protocol/test/unit/test_slashing_percentage.ml b/src/proto_alpha/lib_protocol/test/unit/test_slashing_percentage.ml index e2cfe77c7c42..0eab94a43fcd 100644 --- a/src/proto_alpha/lib_protocol/test/unit/test_slashing_percentage.ml +++ b/src/proto_alpha/lib_protocol/test/unit/test_slashing_percentage.ml @@ -12,3 +12,192 @@ -- --file test_slashing_percentage.ml Subject: On slashing double attestations. *) + +open Protocol + +let assert_equal ~loc pct1 pct2 = + let open Lwt_result_syntax in + let* pct1 in + let* pct2 in + let pct1_q = Percentage.to_q pct1 in + let pct2_q = Percentage.to_q pct2 in + Assert.equal_q ~loc pct1_q pct2_q + +let assert_equal_int ~loc n (pct : Percentage.t tzresult Lwt.t) = + let open Lwt_result_syntax in + let* pct in + let pct_q = Percentage.to_q pct in + Assert.equal_q ~loc Q.(n // 100) pct_q + +let assert_not_equal_int ~loc n (pct : Percentage.t tzresult Lwt.t) = + let open Lwt_result_syntax in + let* pct in + let pct_q = Percentage.to_q pct in + Assert.not_equal ~loc Q.equal "Values are equal" Q.pp_print Q.(n // 100) pct_q + +let raw_context ~max_slashing_threshold ~max_slashing_per_block ~ns_enable () = + let constants = Default_parameters.constants_test in + let constants = + Constants_helpers.Set.Adaptive_issuance.force_activation constants true + in + let constants = + Constants_helpers.Set.Adaptive_issuance.ns_enable constants ns_enable + in + let constants = + Constants_helpers.Set.max_slashing_threshold + constants + max_slashing_threshold + in + let constants = + Constants_helpers.Set.max_slashing_per_block + constants + max_slashing_per_block + in + Context.raw_context_from_constants constants + +let make_fake_culprits_with_rights_from_int_list il = + let open Result_syntax in + let n = List.length il in + let* accounts_list = Account.generate_accounts n in + let pkh_list = List.map (fun x -> x.Account.pkh) accounts_list in + let pkh_rights_list = Stdlib.List.combine pkh_list il in + let map = + List.fold_left + (fun map (pkh, rights) -> + Environment.Signature.Public_key_hash.Map.add pkh rights map) + Environment.Signature.Public_key_hash.Map.empty + pkh_rights_list + in + return (map, pkh_list) + +let get_pct ~ns_enable ~max_slashing_threshold ~max_slashing_per_block int_list + = + let open Lwt_result_syntax in + let* ctxt = + raw_context ~max_slashing_threshold ~max_slashing_per_block ~ns_enable () + in + let*? map, pkh_list = make_fake_culprits_with_rights_from_int_list int_list in + return + @@ Protocol.Slash_percentage.Internal_for_tests.for_double_attestation + ctxt + map + pkh_list + +(** Test the double attesting slash is always 50% with ns_enable = false *) +let test_ns_enable_disable () = + let open Lwt_result_syntax in + let f x = + get_pct + ~ns_enable:false + ~max_slashing_threshold:100 + ~max_slashing_per_block:Percentage.p100 + [x] + in + let* () = assert_equal_int ~loc:__LOC__ 50 (f 0) in + let* () = assert_equal_int ~loc:__LOC__ 50 (f 1) in + let* () = assert_equal_int ~loc:__LOC__ 50 (f 100) in + let* () = assert_equal_int ~loc:__LOC__ 50 (f 10000) in + return_unit + +(** We set ns_enable = true for the following tests *) +let get_pct = get_pct ~ns_enable:true + +(** Tests that the slashing amount for several delegates is the same as long + as the sum of their rights is the same *) +let test_list_and_sum () = + let open Lwt_result_syntax in + (* A max slashing threshold of 100 ensures that x -> f [x] is injective *) + let f x = + get_pct + ~max_slashing_threshold:100 + ~max_slashing_per_block:Percentage.p100 + x + in + let* () = assert_equal ~loc:__LOC__ (f [0; 0]) (f [0]) in + let* () = assert_equal ~loc:__LOC__ (f [1; 2; 3]) (f [3; 3]) in + let* () = assert_equal ~loc:__LOC__ (f [120]) (f [60; 60; 0]) in + let* () = assert_equal ~loc:__LOC__ (f []) (f [0]) in + return_unit + +(** We test only one slashed delegate from now on *) +let get_pct i = get_pct [i] + +(** Tests the max_slashing_per_block parameter *) +let test_max_slashing_per_block () = + let open Lwt_result_syntax in + let f max_slash x = + let max_slashing_per_block = + Percentage.of_q_bounded ~round:`Up Q.(max_slash // 100) + in + get_pct ~max_slashing_threshold:100 ~max_slashing_per_block x + in + let* () = assert_equal_int ~loc:__LOC__ 100 (f 100 200) in + let* () = assert_equal_int ~loc:__LOC__ 1 (f 1 200) in + let* () = assert_equal_int ~loc:__LOC__ 49 (f 49 200) in + let* () = assert_equal_int ~loc:__LOC__ 100 (f 100 100) in + let* () = assert_not_equal_int ~loc:__LOC__ 100 (f 100 99) in + return_unit + +(** We now test with max slashing to 100% (mainnet value) *) +let get_pct = + get_pct + ~max_slashing_per_block: + Default_parameters.constants_mainnet.max_slashing_per_block + +(** Tests the max_slashing_threshold parameter *) +let test_max_slashing_threshold () = + let open Lwt_result_syntax in + let f max_slashing_threshold x = get_pct ~max_slashing_threshold x in + let* () = assert_equal_int ~loc:__LOC__ 100 (f 100 20000) in + let* () = assert_equal_int ~loc:__LOC__ 100 (f 1000 1001) in + let* () = assert_equal_int ~loc:__LOC__ 100 (f 1000 1000) in + let* () = assert_not_equal_int ~loc:__LOC__ 100 (f 1000 999) in + return_unit + +(** We now test with max slashing threshold to 2334 (mainnet value) *) +let get_pct = + get_pct + ~max_slashing_threshold: + Default_parameters.constants_mainnet.max_slashing_threshold + +(** Test slashing values for mainnet constants *) +let test_mainnet_values () = + let open Lwt_result_syntax in + let f x = get_pct x in + (* percentage with two decimals *) + let assert_equal_precise_int ~loc n (pct : Percentage.t tzresult Lwt.t) = + let open Lwt_result_syntax in + let* pct in + let pct_q = Percentage.to_q pct in + Assert.equal_q ~loc Q.(n // 10000) pct_q + in + let* () = assert_equal_precise_int ~loc:__LOC__ 0 (f 0) in + (* For 1 right, up to 23, slash is 0.01% *) + let* () = assert_equal_precise_int ~loc:__LOC__ 1 (f 1) in + let* () = assert_equal_precise_int ~loc:__LOC__ 1 (f 23) in + let* () = assert_equal_precise_int ~loc:__LOC__ 2 (f 24) in + (* Some random value points *) + let* () = assert_equal_precise_int ~loc:__LOC__ 19 (f 100) in + let* () = assert_equal_precise_int ~loc:__LOC__ 459 (f 500) in + let* () = assert_equal_precise_int ~loc:__LOC__ 1836 (f 1000) in + (* Highest non-saturated slash is 99.92% *) + let* () = assert_equal_precise_int ~loc:__LOC__ 9983 (f 2332) in + let* () = assert_equal_precise_int ~loc:__LOC__ 9992 (f 2333) in + let* () = assert_equal_precise_int ~loc:__LOC__ 10000 (f 2334) in + let* () = assert_equal_precise_int ~loc:__LOC__ 10000 (f 7000) in + let* () = assert_equal_precise_int ~loc:__LOC__ 10000 (f 70000) in + return_unit + +let tests = + Tztest. + [ + tztest "Test ns_enable = false" `Quick test_ns_enable_disable; + tztest "Test only sum of rights counts" `Quick test_list_and_sum; + tztest "Test max_slashing_per_block" `Quick test_max_slashing_per_block; + tztest "Test max_slashing_threshold" `Quick test_max_slashing_threshold; + tztest "Test exact slashing values" `Quick test_mainnet_values; + ] + +let () = + Alcotest_lwt.run ~__FILE__ Protocol.name [("slashing_percentage", tests)] + |> Lwt_main.run -- GitLab