diff --git a/scripts/yes-wallet/yes_wallet_lib.ml b/scripts/yes-wallet/yes_wallet_lib.ml index b49196122b25912fbf2cfbcf7a04abc701c46468..fc2e610599809b7037b0eea31e750deb100082c2 100644 --- a/scripts/yes-wallet/yes_wallet_lib.ml +++ b/scripts/yes-wallet/yes_wallet_lib.ml @@ -449,7 +449,12 @@ let get_delegates (proto : protocol) context ~init:(Ok ([], Alpha_context.Tez.zero)) ~f:(fun pkh acc -> let* pk = - let*! r = Alpha_context.Delegate.pubkey ctxt pkh in + let*! r = + let* pk = + Alpha_context.Delegate.Consensus_key.active_pubkey ctxt pkh + in + return pk.consensus_pk + in Lwt.return @@ Environment.wrap_tzresult r in let*? key_list_acc, staking_balance_acc = acc in diff --git a/src/lib_store/test/alpha_utils.ml b/src/lib_store/test/alpha_utils.ml index 6369a58503c47fcefd89166aa203b2f64466de91..31a14e77165b58f7ce654e579dad82d3df2f5e5f 100644 --- a/src/lib_store/test/alpha_utils.ml +++ b/src/lib_store/test/alpha_utils.ml @@ -110,7 +110,13 @@ module Account = struct let account_to_bootstrap ({pkh; pk; _}, amount, delegate_to) = let open Parameters in - ({public_key_hash = pkh; public_key = Some pk; amount; delegate_to} + ({ + public_key_hash = pkh; + public_key = Some pk; + amount; + delegate_to; + consensus_key = None; + } : bootstrap_account) let commitment_secret = diff --git a/src/proto_alpha/lib_client/client_proto_context.ml b/src/proto_alpha/lib_client/client_proto_context.ml index 49111fcc74e3e32340a18899ae45bf4cbca744a4..0ad62e66205c30e3b0bc60bd29612f733099e8e2 100644 --- a/src/proto_alpha/lib_client/client_proto_context.ml +++ b/src/proto_alpha/lib_client/client_proto_context.ml @@ -299,22 +299,128 @@ let set_delegate cctxt ~chain ~block ?confirmations ?dry_run ?verbose_signing ~fee_parameter opt_delegate +let build_update_consensus_key ?fee ?gas_limit ?storage_limit cycle consensus_pk + = + let operation = Update_consensus_key (cycle, consensus_pk) in + Injection.prepare_manager_operation + ~fee:(Limit.of_option fee) + ~gas_limit:(Limit.of_option gas_limit) + ~storage_limit:(Limit.of_option storage_limit) + operation + let register_as_delegate cctxt ~chain ~block ?confirmations ?dry_run - ?verbose_signing ?fee ~manager_sk ~fee_parameter src_pk = + ?verbose_signing ?fee ~manager_sk ~fee_parameter ?consensus_pk src_pk = let source = Signature.Public_key.hash src_pk in - delegate_contract + let delegate_op = build_delegate_operation ?fee (Some source) in + match consensus_pk with + | None -> ( + let operation = Annotated_manager_operation.Single_manager delegate_op in + Injection.inject_manager_operation + cctxt + ~chain + ~block + ?confirmations + ?dry_run + ?verbose_signing + ~source + ~fee:(Limit.of_option fee) + ~gas_limit:Limit.unknown + ~storage_limit:Limit.unknown + ~src_pk + ~src_sk:manager_sk + ~fee_parameter + operation + >>=? fun (oph, op, result) -> + match Apply_results.pack_contents_list op result with + | Apply_results.Single_and_result ((Manager_operation _ as op), result) -> + return ((oph, op, result), None)) + | Some consensus_pk -> ( + Alpha_services.Constants.all cctxt (chain, block) + >>=? fun {parametric = {preserved_cycles; _}; _} -> + Plugin.RPC.current_level cctxt ~offset:1l (chain, block) >>=? fun l -> + let cycle = Cycle.(succ (add l.cycle preserved_cycles)) in + let operation = + Annotated_manager_operation.Cons_manager + ( delegate_op, + Annotated_manager_operation.Single_manager + (build_update_consensus_key ?fee cycle consensus_pk) ) + in + Injection.inject_manager_operation + cctxt + ~chain + ~block + ?confirmations + ?dry_run + ?verbose_signing + ~successor_level:true + ~source + ~fee:(Limit.of_option fee) + ~gas_limit:Limit.unknown + ~storage_limit:Limit.unknown + ~src_pk + ~src_sk:manager_sk + ~fee_parameter + operation + >>=? fun (oph, op, result) -> + match Apply_results.pack_contents_list op result with + | Apply_results.Single_and_result + (Manager_operation _, Manager_operation_result _) -> + . + | Apply_results.Cons_and_result + ( (Manager_operation _ as op1), + res1, + Single_and_result ((Manager_operation _ as op2), res2) ) -> + return ((oph, op1, res1), Some (op2, res2))) + +let update_consensus_key cctxt ~chain ~block ?confirmations ?dry_run + ?verbose_signing ?simulation ?fee ~consensus_pk ~manager_sk ~fee_parameter + cycle src_pk = + let source = Signature.Public_key.hash src_pk in + let operation = build_update_consensus_key ?fee cycle consensus_pk in + let operation = Annotated_manager_operation.Single_manager operation in + Injection.inject_manager_operation cctxt ~chain ~block ?confirmations ?dry_run ?verbose_signing + ?simulation + ~successor_level:true ~source + ~fee:(Limit.of_option fee) + ~gas_limit:Limit.unknown + ~storage_limit:Limit.unknown ~src_pk ~src_sk:manager_sk - ?fee ~fee_parameter - (Some source) + operation + >>=? fun (oph, op, result) -> + match Apply_results.pack_contents_list op result with + | Apply_results.Single_and_result ((Manager_operation _ as op), result) -> + return (oph, op, result) + +let drain_delegate cctxt ~chain ~block ?confirmations ?dry_run ?verbose_signing + ?simulation ~consensus_sk ~consensus_pkh ?(destination = consensus_pkh) + ~delegate () = + let operation = + Single + (Drain_delegate {consensus_key = consensus_pkh; delegate; destination}) + in + Injection.inject_operation + cctxt + ~chain + ~block + ?confirmations + ?dry_run + ?verbose_signing + ?simulation + ~src_sk:consensus_sk + operation + >>=? fun (oph, op, result) -> + match Apply_results.pack_contents_list op result with + | Apply_results.Single_and_result ((Drain_delegate _ as op), result) -> + return (oph, op, result) let set_deposits_limit cctxt ~chain ~block ?confirmations ?dry_run ?verbose_signing ?simulation ?fee contract ~src_pk ~manager_sk diff --git a/src/proto_alpha/lib_client/client_proto_context.mli b/src/proto_alpha/lib_client/client_proto_context.mli index 97951466bf00e31650fa65b8a93be0070843951b..4c13216cd8944d6cdc59119590254f4e21403621 100644 --- a/src/proto_alpha/lib_client/client_proto_context.mli +++ b/src/proto_alpha/lib_client/client_proto_context.mli @@ -131,6 +131,37 @@ val set_delegate : public_key_hash option -> Kind.delegation Kind.manager Injection.result tzresult Lwt.t +val update_consensus_key : + #Protocol_client_context.full -> + chain:Shell_services.chain -> + block:Shell_services.block -> + ?confirmations:int -> + ?dry_run:bool -> + ?verbose_signing:bool -> + ?simulation:bool -> + ?fee:Tez.tez -> + consensus_pk:Signature.public_key -> + manager_sk:Client_keys.sk_uri -> + fee_parameter:Injection.fee_parameter -> + Cycle.t -> + Signature.public_key -> + Kind.update_consensus_key Kind.manager Injection.result tzresult Lwt.t + +val drain_delegate : + #Protocol_client_context.full -> + chain:Shell_services.chain -> + block:Shell_services.block -> + ?confirmations:int -> + ?dry_run:bool -> + ?verbose_signing:bool -> + ?simulation:bool -> + consensus_sk:Client_keys.sk_uri -> + consensus_pkh:Signature.public_key_hash -> + ?destination:Signature.public_key_hash -> + delegate:Signature.public_key_hash -> + unit -> + Kind.drain_delegate Injection.result tzresult Lwt.t + val set_deposits_limit : #Protocol_client_context.full -> chain:Shell_services.chain -> @@ -157,8 +188,14 @@ val register_as_delegate : ?fee:Tez.tez -> manager_sk:Client_keys.sk_uri -> fee_parameter:Injection.fee_parameter -> + ?consensus_pk:public_key -> public_key -> - Kind.delegation Kind.manager Injection.result tzresult Lwt.t + (Kind.delegation Kind.manager Injection.result + * (Kind.update_consensus_key Kind.manager contents + * Kind.update_consensus_key Kind.manager Apply_results.contents_result) + option) + tzresult + Lwt.t val save_contract : force:bool -> diff --git a/src/proto_alpha/lib_client/injection.ml b/src/proto_alpha/lib_client/injection.ml index 11e8eb18027b96411f14563731369c7362f2e363..81b87bc7d9830a29160d3be7cc6729ebd757bc86 100644 --- a/src/proto_alpha/lib_client/injection.ml +++ b/src/proto_alpha/lib_client/injection.ml @@ -315,6 +315,7 @@ let estimated_gas_single (type kind) | Applied (Register_global_constant_result {consumed_gas; _}) -> Ok consumed_gas | Applied (Set_deposits_limit_result {consumed_gas}) -> Ok consumed_gas + | Applied (Update_consensus_key_result {consumed_gas; _}) -> Ok consumed_gas | Applied (Tx_rollup_origination_result {consumed_gas; _}) -> Ok consumed_gas | Applied (Tx_rollup_submit_batch_result {consumed_gas; _}) -> @@ -379,6 +380,7 @@ let estimated_storage_single (type kind) ~tx_rollup_origination_size | Applied (Register_global_constant_result {size_of_constant; _}) -> Ok size_of_constant | Applied (Set_deposits_limit_result _) -> Ok Z.zero + | Applied (Update_consensus_key_result _) -> Ok Z.zero | Applied (Tx_rollup_origination_result _) -> Ok tx_rollup_origination_size | Applied (Tx_rollup_submit_batch_result {paid_storage_size_diff; _}) | Applied (Sc_rollup_atomic_batch_result {paid_storage_size_diff; _}) -> @@ -454,6 +456,7 @@ let originated_contracts_single (type kind) | Applied (Reveal_result _) -> Ok [] | Applied (Delegation_result _) -> Ok [] | Applied (Set_deposits_limit_result _) -> Ok [] + | Applied (Update_consensus_key_result _) -> Ok [] | Applied (Tx_rollup_origination_result _) -> Ok [] | Applied (Tx_rollup_submit_batch_result _) -> Ok [] | Applied (Tx_rollup_commit_result _) -> Ok [] diff --git a/src/proto_alpha/lib_client/mockup.ml b/src/proto_alpha/lib_client/mockup.ml index 646a1db0cb794a93c67265f8b6a2c70e067ad72f..24ff91f2262e6948bd1f8434e736954360b61106 100644 --- a/src/proto_alpha/lib_client/mockup.ml +++ b/src/proto_alpha/lib_client/mockup.ml @@ -1107,6 +1107,7 @@ module Parsed_account = struct public_key = Some public_key; amount = repr.amount; delegate_to = None; + consensus_key = None; } let default_to_json (cctxt : Tezos_client_base.Client_context.full) : @@ -1158,37 +1159,17 @@ module Bootstrap_account = struct let encoding : Parameters.bootstrap_account Data_encoding.t = let open Data_encoding in let open Parameters in - union - [ - case - ~title:"No delegate" - (Tag 0) - (obj3 - (req "public_key_hash" Signature.Public_key_hash.encoding) - (opt "public_key" Signature.Public_key.encoding) - (req "amount" Tez.encoding)) - (function - | {public_key_hash; public_key; amount; delegate_to = None} -> - Some (public_key_hash, public_key, amount) - | _ -> None) - (fun (public_key_hash, public_key, amount) -> - {public_key_hash; public_key; amount; delegate_to = None}); - case - ~title:"With delegate" - (Tag 1) - (obj4 - (req "public_key_hash" Signature.Public_key_hash.encoding) - (opt "public_key" Signature.Public_key.encoding) - (req "amount" Tez.encoding) - (req "delegate_to" Signature.Public_key_hash.encoding)) - (function - | {public_key_hash; public_key; amount; delegate_to = Some delegate} - -> - Some (public_key_hash, public_key, amount, delegate) - | _ -> None) - (fun (public_key_hash, public_key, amount, delegate) -> - {public_key_hash; public_key; amount; delegate_to = Some delegate}); - ] + conv + (fun {public_key_hash; public_key; amount; delegate_to; consensus_key} -> + (public_key_hash, public_key, amount, delegate_to, consensus_key)) + (fun (public_key_hash, public_key, amount, delegate_to, consensus_key) -> + {public_key_hash; public_key; amount; delegate_to; consensus_key}) + (obj5 + (req "public_key_hash" Signature.Public_key_hash.encoding) + (opt "public_key" Signature.Public_key.encoding) + (req "amount" Tez.encoding) + (opt "delegate_to" Signature.Public_key_hash.encoding) + (opt "consensus_key" Signature.Public_key.encoding)) end module Bootstrap_contract = struct @@ -1245,31 +1226,12 @@ module Protocol_parameters = struct end (* This encoding extends [Protocol_constants_overrides.encoding] to allow - reading json files as produced by lib_parameters. Sadly, this require - copying partially [bootstrap_account_encoding], which is not exposed - in parameters_repr.ml. *) + reading json files as produced by lib_parameters. *) let lib_parameters_json_encoding = - let bootstrap_account_encoding = - let open Data_encoding in - conv - (function - | {Parameters.public_key; amount; _} -> ( - match public_key with - | None -> assert false - | Some pk -> (pk, amount))) - (fun (pk, amount) -> - { - Parameters.public_key = Some pk; - public_key_hash = Signature.Public_key.hash pk; - amount; - delegate_to = None; - }) - (tup2 Signature.Public_key.encoding Tez.encoding) - in Data_encoding.( merge_objs (obj2 - (opt "bootstrap_accounts" (list bootstrap_account_encoding)) + (opt "bootstrap_accounts" (list Parameters.bootstrap_account_encoding)) (opt "commitments" (list Commitment.encoding))) Protocol_constants_overrides.encoding) diff --git a/src/proto_alpha/lib_client/operation_result.ml b/src/proto_alpha/lib_client/operation_result.ml index ad2d57872ff3a293d8a582afd121c93e6d954ab1..dee2e1b59e9bc2341e1583c580d48bcaf5d0432d 100644 --- a/src/proto_alpha/lib_client/operation_result.ml +++ b/src/proto_alpha/lib_client/operation_result.ml @@ -205,6 +205,14 @@ let pp_manager_operation_content (type kind) source pp_result ppf match limit_opt with | None -> Format.pp_print_string ppf "Unlimited deposits" | Some limit -> Format.fprintf ppf "Limit: %a" Tez.pp limit) + | Update_consensus_key (cycle, pk) -> + Format.fprintf + ppf + "Update_consensus_key:@,Cycle: %a@,Public key hash: %a" + Cycle.pp + cycle + Signature.Public_key_hash.pp + (Signature.Public_key.hash pk) | Tx_rollup_origination -> Format.fprintf ppf "Tx rollup origination:@,From: %a" Contract.pp source | Tx_rollup_submit_batch {tx_rollup; content; burn_limit = _} -> @@ -744,6 +752,9 @@ let pp_manager_operation_contents_and_result ppf Format.fprintf ppf "@,Consumed gas: %a" Gas.Arith.pp consumed_gas ; pp_balance_updates_opt ppf balance_updates in + let pp_update_consensus_key_op (Update_consensus_key_result {consumed_gas}) = + Format.fprintf ppf "@,Consumed gas: %a" Gas.Arith.pp consumed_gas + in let pp_result (type kind) ppf (result : kind manager_operation_result) = Format.fprintf ppf "@," ; match result with @@ -773,6 +784,15 @@ let pp_manager_operation_contents_and_result ppf ppf "@[This deposits limit modification was BACKTRACKED, its \ expected effects were NOT applied.@]" + | Applied (Update_consensus_key_result _ as op) -> + Format.fprintf ppf "The consensus key was successfully udpated" ; + pp_update_consensus_key_op op + | Backtracked ((Update_consensus_key_result _ as op), _) -> + Format.fprintf + ppf + "@[This consensus key update was BACKTRACKED, its expected \ + effects were NOT applied.@]" ; + pp_update_consensus_key_op op | Applied (Transaction_result tx) -> Format.fprintf ppf "This transaction was successfully applied" ; pp_transaction_result tx @@ -1066,8 +1086,8 @@ let rec pp_contents_and_result_list : bus | Single_and_result ( Preendorsement {level; _}, - Preendorsement_result {balance_updates; delegate; preendorsement_power} - ) -> + Preendorsement_result + {balance_updates; delegate; consensus_key; preendorsement_power} ) -> Format.fprintf ppf "@[Preendorsement:@,\ @@ -1079,12 +1099,13 @@ let rec pp_contents_and_result_list : level pp_balance_updates balance_updates - Signature.Public_key_hash.pp - delegate + Consensus_key.pp + {delegate; consensus_pkh = consensus_key} preendorsement_power | Single_and_result ( Endorsement {level; _}, - Endorsement_result {balance_updates; delegate; endorsement_power} ) -> + Endorsement_result + {balance_updates; delegate; consensus_key; endorsement_power} ) -> Format.fprintf ppf "@[Endorsement:@,\ @@ -1096,8 +1117,8 @@ let rec pp_contents_and_result_list : level pp_balance_updates balance_updates - Signature.Public_key_hash.pp - delegate + Consensus_key.pp + {delegate; consensus_pkh = consensus_key} endorsement_power | Single_and_result ( Double_endorsement_evidence {op1; op2}, @@ -1164,6 +1185,25 @@ let rec pp_contents_and_result_list : proposal Data_encoding.Json.pp (Data_encoding.Json.construct Vote.ballot_encoding ballot) + | Single_and_result + ( Drain_delegate {consensus_key; delegate; destination}, + Drain_delegate_result {balance_updates; allocated_destination_contract} + ) -> + Format.fprintf + ppf + "@[Drain delegate:@,\ + Consensus key hash: %a@,\ + Delegate: %a@,\ + Destination: %a%s%a@]" + Signature.Public_key_hash.pp + consensus_key + Signature.Public_key_hash.pp + delegate + Signature.Public_key_hash.pp + destination + (if allocated_destination_contract then " (allocated)" else "") + pp_balance_updates_opt + balance_updates | Single_and_result (Failing_noop _arbitrary, _) -> (* the Failing_noop operation always fails and can't have result *) . diff --git a/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml b/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml index 7ace7e77ed5cb60e64dfa926f000450333063fe8..14fdbc765d8729ad0ab2802675d8d8e8f6273ef6 100644 --- a/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml +++ b/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml @@ -1021,7 +1021,7 @@ let commands_rw () = ?fee ~src_pk ~manager_sk - >>= fun _ -> return_unit); + >>=? fun _ -> return_unit); command ~group ~desc:"Launch a smart contract on the blockchain." @@ -1482,10 +1482,153 @@ let commands_rw () = src_pk >>= function | Ok _ -> return_unit - | Error [Environment.Ecoproto_error Delegate_storage.Active_delegate] -> + | Error + [ + Environment.Ecoproto_error + Delegate_storage.Contract.Active_delegate; + ] -> cctxt#message "Delegate already activated." >>= fun () -> return_unit | Error el -> Lwt.return_error el); + command + ~group + ~desc:"Register the public key hash as a delegate." + (args4 fee_arg dry_run_switch verbose_signing_switch fee_parameter_args) + (prefixes ["register"; "key"] + @@ Public_key_hash.source_param ~name:"mgr" ~desc:"the delegate key" + @@ prefixes ["as"; "delegate"; "with"; "consensus"; "key"] + @@ Public_key.source_param ~name:"key" ~desc:"the consensus key" + @@ stop) + (fun (fee, dry_run, verbose_signing, fee_parameter) + src_pkh + (name_pk, consensus_pk) + cctxt -> + Client_keys.get_key cctxt src_pkh >>=? fun (_, src_pk, src_sk) -> + (match consensus_pk with + | Some pk -> return pk + | None -> Client_keys.public_key name_pk) + >>=? fun consensus_pk -> + register_as_delegate + cctxt + ~chain:cctxt#chain + ~block:cctxt#block + ?confirmations:cctxt#confirmations + ~dry_run + ~fee_parameter + ~verbose_signing + ?fee + ~manager_sk:src_sk + ~consensus_pk + src_pk + >>= function + | Ok _ -> return_unit + | Error + [ + Environment.Ecoproto_error + Delegate_storage.Contract.Active_delegate; + ] -> + cctxt#message "Delegate already activated." >>= fun () -> + return_unit + | Error el -> Lwt.return_error el); + command + ~group + ~desc:"Update the consensus key of a delegate." + (args4 fee_arg dry_run_switch verbose_signing_switch fee_parameter_args) + (prefixes ["set"; "consensus"; "key"; "for"] + @@ Public_key_hash.source_param ~name:"mgr" ~desc:"the delegate key" + @@ prefixes ["to"] + @@ Public_key.source_param ~name:"key" ~desc:"the consensus key" + @@ stop) + (fun (fee, dry_run, verbose_signing, fee_parameter) + delegate_pkh + (name_pk, consensus_pk) + cctxt -> + (match consensus_pk with + | Some pk -> return pk + | None -> Client_keys.public_key name_pk) + >>=? fun consensus_pk -> + Client_keys.get_key cctxt delegate_pkh + >>=? fun (_, delegate_pk, delegate_sk) -> + Alpha_services.Constants.all cctxt (cctxt#chain, cctxt#block) + >>=? fun {parametric = {preserved_cycles; _}; _} -> + Plugin.RPC.current_level cctxt ~offset:1l (cctxt#chain, cctxt#block) + >>=? fun l -> + let cycle = Cycle.(succ (add l.cycle preserved_cycles)) in + update_consensus_key + cctxt + ~chain:cctxt#chain + ~block:cctxt#block + ?confirmations:cctxt#confirmations + ~dry_run + ~fee_parameter + ~verbose_signing + ?fee + ~consensus_pk + ~manager_sk:delegate_sk + cycle + delegate_pk + >>= function + | Ok _ -> return_unit + | Error el -> Lwt.return_error el); + command + ~group + ~desc:"Drain all funds from a delegate." + (args2 dry_run_switch verbose_signing_switch) + (prefixes ["drain"; "delegate"] + @@ Public_key_hash.source_param ~name:"mgr" ~desc:"the delegate key" + @@ prefixes ["to"] + @@ Public_key_hash.source_param ~name:"key" ~desc:"the consensus key" + @@ stop) + (fun (dry_run, verbose_signing) delegate_pkh consensus_pkh cctxt -> + Client_keys.get_key cctxt consensus_pkh + >>=? fun (_, _consensus_pk, consensus_sk) -> + drain_delegate + cctxt + ~chain:cctxt#chain + ~block:cctxt#block + ?confirmations:cctxt#confirmations + ~dry_run + ~verbose_signing + ~consensus_pkh + ~consensus_sk + ~delegate:delegate_pkh + () + >>= function + | Ok _ -> return_unit + | Error el -> Lwt.return_error el); + command + ~group + ~desc:"Drain all funds from a delegate." + (args2 dry_run_switch verbose_signing_switch) + (prefixes ["drain"; "delegate"] + @@ Public_key_hash.source_param ~name:"mgr" ~desc:"the delegate key" + @@ prefixes ["to"] + @@ Public_key_hash.source_param ~name:"key" ~desc:"the destination key" + @@ prefixes ["with"] + @@ Public_key_hash.source_param ~name:"key" ~desc:"the consensus key" + @@ stop) + (fun (dry_run, verbose_signing) + delegate_pkh + destination_pkh + consensus_pkh + cctxt -> + Client_keys.get_key cctxt consensus_pkh + >>=? fun (_, _consensus_pk, consensus_sk) -> + drain_delegate + cctxt + ~chain:cctxt#chain + ~block:cctxt#block + ?confirmations:cctxt#confirmations + ~dry_run + ~verbose_signing + ~consensus_pkh + ~consensus_sk + ~destination:destination_pkh + ~delegate:delegate_pkh + () + >>= function + | Ok _ -> return_unit + | Error el -> Lwt.return_error el); command ~desc:"Wait until an operation is included in a block" (args3 diff --git a/src/proto_alpha/lib_delegate/baking_actions.ml b/src/proto_alpha/lib_delegate/baking_actions.ml index 81dfefc9f74b02bff03fdde1ef009eb296c2e954..5ac94662164773b18b7fc6a05851af6136f5b41c 100644 --- a/src/proto_alpha/lib_delegate/baking_actions.ml +++ b/src/proto_alpha/lib_delegate/baking_actions.ml @@ -117,7 +117,7 @@ type block_kind = type block_to_bake = { predecessor : block_info; round : Round.t; - delegate : Baking_state.delegate; + delegate : Baking_state.consensus_key_and_delegate; kind : block_kind; } @@ -125,11 +125,11 @@ type action = | Do_nothing | Inject_block of {block_to_bake : block_to_bake; updated_state : state} | Inject_preendorsements of { - preendorsements : (delegate * consensus_content) list; + preendorsements : (consensus_key_and_delegate * consensus_content) list; updated_state : state; } | Inject_endorsements of { - endorsements : (delegate * consensus_content) list; + endorsements : (consensus_key_and_delegate * consensus_content) list; updated_state : state; } | Update_to_level of level_update @@ -214,7 +214,9 @@ let sign_block_header state proposer unsigned_block_header = return {Block_header.shell; protocol_data = {contents; signature}} let inject_block ~state_recorder state block_to_bake ~updated_state = - let {predecessor; round; delegate; kind} = block_to_bake in + let {predecessor; round; delegate = (consensus_key, _) as delegate; kind} = + block_to_bake + in let cctxt = state.global_state.cctxt in let chain_id = state.global_state.chain_id in let simulation_mode = state.global_state.validation_mode in @@ -260,7 +262,7 @@ let inject_block ~state_recorder state block_to_bake ~updated_state = >>=? fun injection_level -> generate_seed_nonce_hash state.global_state.config.Baking_configuration.nonce - delegate + consensus_key injection_level >>=? fun seed_nonce_opt -> let seed_nonce_hash = Option.map fst seed_nonce_opt in @@ -292,7 +294,7 @@ let inject_block ~state_recorder state block_to_bake ~updated_state = simulation_kind state.global_state.constants.parametric >>=? fun {unsigned_block_header; operations} -> - sign_block_header state delegate unsigned_block_header + sign_block_header state consensus_key unsigned_block_header >>=? fun signed_block_header -> (match seed_nonce_opt with | None -> @@ -325,7 +327,7 @@ let inject_preendorsements ~state_recorder state ~preendorsements ~updated_state (* Don't parallelize signatures: the signer might not be able to handle concurrent requests *) List.filter_map_es - (fun (delegate, consensus_content) -> + (fun (((consensus_key, _) as delegate), consensus_content) -> Events.(emit signing_preendorsement delegate) >>= fun () -> let shell = { @@ -340,11 +342,10 @@ let inject_preendorsements ~state_recorder state ~preendorsements ~updated_state let block_location = Baking_files.resolve_location ~chain_id `Highwatermarks in - let delegate = delegate.public_key_hash in Baking_highwatermarks.may_sign_preendorsement cctxt block_location - ~delegate + ~delegate:consensus_key.public_key_hash ~level ~round >>=? function @@ -352,7 +353,7 @@ let inject_preendorsements ~state_recorder state ~preendorsements ~updated_state Baking_highwatermarks.record_preendorsement cctxt block_location - ~delegate + ~delegate:consensus_key.public_key_hash ~level ~round >>=? fun () -> return_true @@ -367,7 +368,8 @@ let inject_preendorsements ~state_recorder state ~preendorsements ~updated_state unsigned_operation in (* TODO: do we want to reload the sk uri or not ? *) - Client_keys.get_key cctxt delegate.public_key_hash >>=? fun (_, _, sk) -> + Client_keys.get_key cctxt consensus_key.public_key_hash + >>=? fun (_, _, sk) -> Client_keys.sign cctxt ~watermark sk unsigned_operation_bytes else fail (Baking_highwatermarks.Block_previously_preendorsed {round; level})) @@ -412,7 +414,7 @@ let sign_endorsements state endorsements = (* Don't parallelize signatures: the signer might not be able to handle concurrent requests *) List.filter_map_es - (fun (delegate, consensus_content) -> + (fun (((consensus_key, _) as delegate), consensus_content) -> Events.(emit signing_endorsement delegate) >>= fun () -> let shell = { @@ -433,7 +435,7 @@ let sign_endorsements state endorsements = Baking_highwatermarks.may_sign_endorsement cctxt block_location - ~delegate:delegate.public_key_hash + ~delegate:consensus_key.public_key_hash ~level ~round >>=? function @@ -441,7 +443,7 @@ let sign_endorsements state endorsements = Baking_highwatermarks.record_endorsement cctxt block_location - ~delegate:delegate.public_key_hash + ~delegate:consensus_key.public_key_hash ~level ~round >>=? fun () -> return_true @@ -456,7 +458,8 @@ let sign_endorsements state endorsements = unsigned_operation in (* TODO: do we want to reload the sk uri or not ? *) - Client_keys.get_key cctxt delegate.public_key_hash >>=? fun (_, _, sk) -> + Client_keys.get_key cctxt consensus_key.public_key_hash + >>=? fun (_, _, sk) -> Client_keys.sign cctxt ~watermark sk unsigned_operation_bytes else fail (Baking_highwatermarks.Block_previously_preendorsed {round; level})) diff --git a/src/proto_alpha/lib_delegate/baking_actions.mli b/src/proto_alpha/lib_delegate/baking_actions.mli index 83789e84892f8ec3b83f6af4b7c45d9c02c77b65..da181cda8c3202b695a1e671d5239b9d262c7ee6 100644 --- a/src/proto_alpha/lib_delegate/baking_actions.mli +++ b/src/proto_alpha/lib_delegate/baking_actions.mli @@ -39,7 +39,7 @@ type block_kind = type block_to_bake = { predecessor : block_info; round : Round.t; - delegate : delegate; + delegate : consensus_key_and_delegate; kind : block_kind; } @@ -47,11 +47,11 @@ type action = | Do_nothing | Inject_block of {block_to_bake : block_to_bake; updated_state : state} | Inject_preendorsements of { - preendorsements : (delegate * consensus_content) list; + preendorsements : (consensus_key_and_delegate * consensus_content) list; updated_state : state; } | Inject_endorsements of { - endorsements : (delegate * consensus_content) list; + endorsements : (consensus_key_and_delegate * consensus_content) list; updated_state : state; } | Update_to_level of level_update @@ -75,7 +75,7 @@ type t = action val generate_seed_nonce_hash : Baking_configuration.nonce_config -> - delegate -> + consensus_key -> Level.t -> (Nonce_hash.t * Nonce.t) option tzresult Lwt.t @@ -89,19 +89,19 @@ val inject_block : val inject_preendorsements : state_recorder:(new_state:state -> unit tzresult Lwt.t) -> state -> - preendorsements:(delegate * consensus_content) list -> + preendorsements:(consensus_key_and_delegate * consensus_content) list -> updated_state:state -> state tzresult Lwt.t val sign_endorsements : state -> - (delegate * consensus_content) list -> - (delegate * packed_operation) list tzresult Lwt.t + (consensus_key_and_delegate * consensus_content) list -> + (consensus_key_and_delegate * packed_operation) list tzresult Lwt.t val inject_endorsements : state_recorder:(new_state:state -> unit tzresult Lwt.t) -> state -> - endorsements:(delegate * consensus_content) list -> + endorsements:(consensus_key_and_delegate * consensus_content) list -> updated_state:state -> state tzresult Lwt.t diff --git a/src/proto_alpha/lib_delegate/baking_commands.ml b/src/proto_alpha/lib_delegate/baking_commands.ml index 78d6ec9e61a1bfcea816b01223c67e5e329f29b6..eae0f2d4bedacaff6b65b449b497e2f32f206e5c 100644 --- a/src/proto_alpha/lib_delegate/baking_commands.ml +++ b/src/proto_alpha/lib_delegate/baking_commands.ml @@ -202,7 +202,9 @@ let sources_param = Clic.seq_of_param (Client_keys.Public_key_hash.source_param ~name:"baker" - ~desc:"name of the delegate owning the endorsement right") + ~desc: + "name of the delegate owning the endorsement/baking right or name of \ + the consensus key signing on the delegate's behalf") let delegate_commands () : Protocol_client_context.full Clic.command list = let open Clic in diff --git a/src/proto_alpha/lib_delegate/baking_events.ml b/src/proto_alpha/lib_delegate/baking_events.ml index d0e2f9a35f2eb7e10fe323969f710a69fea90a74..dd8506d8c7fc9126359f106820014613a4425d47 100644 --- a/src/proto_alpha/lib_delegate/baking_events.ml +++ b/src/proto_alpha/lib_delegate/baking_events.ml @@ -87,8 +87,8 @@ module State_transitions = struct ("level", Data_encoding.int32) ~pp3:Round.pp ("next_round", Round.encoding) - ~pp4:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp4:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let new_head_while_waiting_for_qc = declare_0 @@ -241,8 +241,8 @@ module State_transitions = struct ~name:"proposing_fresh_block" ~level:Info ~msg:"proposing fresh block for {delegate} at round {round}" - ~pp1:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp1:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) ~pp2:Round.pp ("round", Round.encoding) @@ -384,8 +384,8 @@ module Scheduling = struct ("round", Round.encoding) ~pp3:Timestamp.pp ("timestamp", Timestamp.encoding) - ~pp4:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp4:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let waiting_end_of_round = declare_3 @@ -499,8 +499,8 @@ module Actions = struct ~name:"skipping_preendorsement" ~level:Error ~msg:"skipping preendorsement for {delegate} -- {trace}" - ~pp1:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp1:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) ~pp2:Error_monad.pp_print_trace ("trace", Error_monad.trace_encoding) @@ -510,8 +510,8 @@ module Actions = struct ~name:"skipping_endorsement" ~level:Error ~msg:"skipping endorsement for {delegate} -- {trace}" - ~pp1:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp1:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) ~pp2:Error_monad.pp_print_trace ("trace", Error_monad.trace_encoding) @@ -521,8 +521,8 @@ module Actions = struct ~name:"failed_to_inject_preendorsement" ~level:Error ~msg:"failed to inject preendorsement for {delegate} -- {trace}" - ~pp1:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp1:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) ~pp2:Error_monad.pp_print_trace ("trace", Error_monad.trace_encoding) @@ -545,8 +545,8 @@ module Actions = struct ~msg:"injected preendorsement {ophash} for {delegate}" ~pp1:Operation_hash.pp ("ophash", Operation_hash.encoding) - ~pp2:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp2:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let endorsement_injected = declare_2 @@ -556,8 +556,8 @@ module Actions = struct ~msg:"injected endorsement {ophash} for {delegate}" ~pp1:Operation_hash.pp ("ophash", Operation_hash.encoding) - ~pp2:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp2:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let synchronizing_round = declare_1 @@ -573,29 +573,26 @@ module Actions = struct ~section ~name:"forging_block" ~level:Info - ~msg: - "forging block at level {level}, round {round} for delegate {delegate}" + ~msg:"forging block at level {level}, round {round} for {delegate}" ~pp1:pp_int32 ~pp2:Round.pp - ~pp3:Baking_state.pp_delegate + ~pp3:Baking_state.pp_consensus_key_and_delegate ("level", Data_encoding.int32) ("round", Round.encoding) - ("delegate", Baking_state.delegate_encoding) + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let injecting_block = declare_3 ~section ~name:"injecting_block" ~level:Debug - ~msg: - "injecting block at level {level}, round {round} for delegate \ - {delegate}" + ~msg:"injecting block at level {level}, round {round} for {delegate}" ~pp1:pp_int32 ~pp2:Round.pp - ~pp3:Baking_state.pp_delegate + ~pp3:Baking_state.pp_consensus_key_and_delegate ("level", Data_encoding.int32) ("round", Round.encoding) - ("delegate", Baking_state.delegate_encoding) + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let block_injected = declare_4 @@ -603,16 +600,15 @@ module Actions = struct ~name:"block_injected" ~level:Notice ~msg: - "block {block} at level {level}, round {round} injected for delegate \ - {delegate}" + "block {block} at level {level}, round {round} injected for {delegate}" ~pp1:Block_hash.pp ~pp2:pp_int32 ~pp3:Round.pp - ~pp4:Baking_state.pp_delegate + ~pp4:Baking_state.pp_consensus_key_and_delegate ("block", Block_hash.encoding) ("level", Data_encoding.int32) ("round", Round.encoding) - ("delegate", Baking_state.delegate_encoding) + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let signing_preendorsement = declare_1 @@ -620,8 +616,8 @@ module Actions = struct ~name:"signing_preendorsement" ~level:Info ~msg:"signing preendorsement for {delegate}" - ~pp1:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp1:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let signing_endorsement = declare_1 @@ -629,8 +625,8 @@ module Actions = struct ~name:"signing_endorsement" ~level:Info ~msg:"signing endorsement for {delegate}" - ~pp1:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp1:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let invalid_json_file = declare_1 diff --git a/src/proto_alpha/lib_delegate/baking_lib.ml b/src/proto_alpha/lib_delegate/baking_lib.ml index 7ef044310a5fcdecef62b38675193170a21d0c21..7ec3ae000cd5f97e8fc8281126379232137dee2a 100644 --- a/src/proto_alpha/lib_delegate/baking_lib.ml +++ b/src/proto_alpha/lib_delegate/baking_lib.ml @@ -80,7 +80,10 @@ let preendorse (cctxt : Protocol_client_context.full) ?(force = false) delegates let*! () = cctxt#message "@[Preendorsing for:@ %a@]" - Format.(pp_print_list ~pp_sep:pp_print_space Baking_state.pp_delegate) + Format.( + pp_print_list + ~pp_sep:pp_print_space + Baking_state.pp_consensus_key_and_delegate) (List.map fst consensus_list) in let state_recorder ~new_state = @@ -117,7 +120,10 @@ let endorse (cctxt : Protocol_client_context.full) ?(force = false) delegates = let*! () = cctxt#message "@[Endorsing for:@ %a@]" - Format.(pp_print_list ~pp_sep:pp_print_space Baking_state.pp_delegate) + Format.( + pp_print_list + ~pp_sep:pp_print_space + Baking_state.pp_consensus_key_and_delegate) (List.map fst consensus_list) in let state_recorder ~new_state = diff --git a/src/proto_alpha/lib_delegate/baking_lib.mli b/src/proto_alpha/lib_delegate/baking_lib.mli index 547822f25d1a878ea38f1216f02d18923b8e31f7..e37f01ac7bb52d7b8d899274fcdb629b5f886674 100644 --- a/src/proto_alpha/lib_delegate/baking_lib.mli +++ b/src/proto_alpha/lib_delegate/baking_lib.mli @@ -37,19 +37,19 @@ val bake : ?extra_operations:Baking_configuration.Operations_source.t -> ?monitor_node_mempool:bool -> ?context_path:string -> - Baking_state.delegate list -> + Baking_state.consensus_key list -> unit tzresult Lwt.t val preendorse : Protocol_client_context.full -> ?force:bool -> - Baking_state.delegate list -> + Baking_state.consensus_key list -> unit tzresult Lwt.t val endorse : Protocol_client_context.full -> ?force:bool -> - Baking_state.delegate list -> + Baking_state.consensus_key list -> unit tzresult Lwt.t val propose : @@ -61,5 +61,5 @@ val propose : ?minimal_timestamp:bool -> ?extra_operations:Baking_configuration.Operations_source.t -> ?context_path:string -> - Baking_state.delegate list -> + Baking_state.consensus_key list -> unit tzresult Lwt.t diff --git a/src/proto_alpha/lib_delegate/baking_nonces.ml b/src/proto_alpha/lib_delegate/baking_nonces.ml index 170bedfdb697121c9f51bc91a3c7e25a173cccce..ce893c0a8b1cb9cd4b0074f724d89a6a11c7ca2b 100644 --- a/src/proto_alpha/lib_delegate/baking_nonces.ml +++ b/src/proto_alpha/lib_delegate/baking_nonces.ml @@ -199,7 +199,7 @@ let get_unrevealed_nonces ({cctxt; chain; _} as state) nonces = (* Nonce creation *) let generate_seed_nonce (nonce_config : Baking_configuration.nonce_config) - (delegate : Baking_state.delegate) level = + (delegate : Baking_state.consensus_key) level = (match nonce_config with | Deterministic -> let data = Data_encoding.Binary.to_bytes_exn Raw_level.encoding level in diff --git a/src/proto_alpha/lib_delegate/baking_nonces.mli b/src/proto_alpha/lib_delegate/baking_nonces.mli index baa6ebf23f188e5ac2e7d04f132af1628a85b626..5151c8909f2479774bbcfa6bce93411fbf8465aa 100644 --- a/src/proto_alpha/lib_delegate/baking_nonces.mli +++ b/src/proto_alpha/lib_delegate/baking_nonces.mli @@ -84,7 +84,7 @@ val get_unrevealed_nonces : val generate_seed_nonce : Baking_configuration.nonce_config -> - Baking_state.delegate -> + Baking_state.consensus_key -> Raw_level.t -> (Nonce_hash.t * Nonce.t) tzresult Lwt.t diff --git a/src/proto_alpha/lib_delegate/baking_scheduling.mli b/src/proto_alpha/lib_delegate/baking_scheduling.mli index a641b7f892fcfbb6af00d462831cc0d65b8d1f88..83e167cfcf3482d062279f06e399b48024204194 100644 --- a/src/proto_alpha/lib_delegate/baking_scheduling.mli +++ b/src/proto_alpha/lib_delegate/baking_scheduling.mli @@ -44,7 +44,9 @@ val wait_next_event : val compute_next_round_time : state -> (Time.Protocol.t * Round.t) option val first_potential_round_at_next_level : - state -> earliest_round:Round.t -> (Round.t * delegate) option + state -> + earliest_round:Round.t -> + (Round.t * consensus_key_and_delegate) option val compute_next_potential_baking_time_at_next_level : state -> (Time.Protocol.t * Round.t) option Lwt.t @@ -58,7 +60,7 @@ val create_initial_state : Baking_configuration.t -> Operation_worker.t -> current_proposal:proposal -> - delegate trace -> + consensus_key list -> state tzresult Lwt.t val compute_bootstrap_event : state -> event tzresult @@ -79,5 +81,5 @@ val run : ?on_error:(tztrace -> unit tzresult Lwt.t) -> chain:Chain_services.chain -> Baking_configuration.t -> - delegate trace -> + consensus_key list -> unit tzresult Lwt.t diff --git a/src/proto_alpha/lib_delegate/baking_state.ml b/src/proto_alpha/lib_delegate/baking_state.ml index c7370341ff795ea2bc5370cd49ae98f08fa712cf..0d07cba1b012f2ed5d7f11415dc0d191a86567b2 100644 --- a/src/proto_alpha/lib_delegate/baking_state.ml +++ b/src/proto_alpha/lib_delegate/baking_state.ml @@ -26,16 +26,16 @@ open Protocol open Alpha_context -(** A delegate (aka, a validator) is identified by its alias name, its +(** A consenssus key (aka, a validator) is identified by its alias name, its public key, its public key hash, and its secret key. *) -type delegate = { +type consensus_key = { alias : string option; public_key : Signature.Public_key.t; public_key_hash : Signature.Public_key_hash.t; secret_key_uri : Client_keys.sk_uri; } -let delegate_encoding = +let consensus_key_encoding = let open Data_encoding in conv (fun {alias; public_key; public_key_hash; secret_key_uri} -> @@ -59,7 +59,7 @@ let delegate_encoding = (req "public_key_hash" Signature.Public_key_hash.encoding) (req "secret_key_uri" string)) -let pp_delegate fmt {alias; public_key_hash; _} = +let pp_consensus_key fmt {alias; public_key_hash; _} = match alias with | None -> Format.fprintf fmt "%a" Signature.Public_key_hash.pp public_key_hash | Some alias -> @@ -70,6 +70,26 @@ let pp_delegate fmt {alias; public_key_hash; _} = Signature.Public_key_hash.pp public_key_hash +type consensus_key_and_delegate = consensus_key * Signature.Public_key_hash.t + +let consensus_key_and_delegate_encoding = + let open Data_encoding in + merge_objs + consensus_key_encoding + (obj1 (req "delegate" Signature.Public_key_hash.encoding)) + +let pp_consensus_key_and_delegate fmt (consensus_key, delegate) = + if Signature.Public_key_hash.equal consensus_key.public_key_hash delegate then + pp_consensus_key fmt consensus_key + else + Format.fprintf + fmt + "%a@,on behalf of %a" + pp_consensus_key + consensus_key + Signature.Public_key_hash.pp + delegate + type validation_mode = Node | Local of Abstract_context_index.t type prequorum = { @@ -96,7 +116,7 @@ type block_info = { type cache = { known_timestamps : Timestamp.time Baking_cache.Timestamp_of_round_cache.t; round_timestamps : - (Timestamp.time * Round.t * delegate) + (Timestamp.time * Round.t * consensus_key_and_delegate) Baking_cache.Round_timestamp_interval_cache.t; } @@ -116,7 +136,7 @@ type global_state = { (* the validation mode used by the baker*) validation_mode : validation_mode; (* the delegates on behalf of which the baker is running *) - delegates : delegate list; + delegates : consensus_key list; cache : cache; } @@ -215,11 +235,7 @@ module SlotMap : Map.S with type key = Slot.t = Map.Make (Slot) list of slots (i.e., a list of position indexes in the slot map, in other words the list of rounds when it will be the proposer), and its endorsing power. *) -type endorsing_slot = { - delegate : Signature.Public_key_hash.t; - slots : Slot.t list; - endorsing_power : int; -} +type endorsing_slot = {slots : Slot.t list; endorsing_power : int} (* FIXME: determine if the slot map should contain all slots or just the first one *) @@ -228,7 +244,7 @@ type endorsing_slot = { type delegate_slots = { (* be careful not to duplicate endorsing slots with different slots keys: always use the first slot in the slots list *) - own_delegate_slots : (delegate * endorsing_slot) SlotMap.t; + own_delegate_slots : (consensus_key_and_delegate * endorsing_slot) SlotMap.t; all_delegate_slots : endorsing_slot SlotMap.t; all_slots_by_round : Slot.t array; } @@ -575,7 +591,7 @@ let may_load_endorsable_data state = module DelegateSet = struct include Set.Make (struct - type t = delegate + type t = consensus_key let compare {public_key_hash = pkh; _} {public_key_hash = pkh'; _} = Signature.Public_key_hash.compare pkh pkh' @@ -605,10 +621,8 @@ let compute_delegate_slots (cctxt : Protocol_client_context.full) let own_delegate_slots, all_delegate_slots = List.fold_left (fun (own_map, all_map) slot -> - let {Plugin.RPC.Validators.delegate; slots; _} = slot in - let endorsing_slot = - {endorsing_power = List.length slots; delegate; slots} - in + let {Plugin.RPC.Validators.consensus_key; delegate; slots; _} = slot in + let endorsing_slot = {endorsing_power = List.length slots; slots} in let all_map = List.fold_left (fun all_map slot -> SlotMap.add slot endorsing_slot all_map) @@ -616,11 +630,14 @@ let compute_delegate_slots (cctxt : Protocol_client_context.full) slots in let own_map = - match DelegateSet.find_pkh delegate own_delegates with - | Some delegate -> + match DelegateSet.find_pkh consensus_key own_delegates with + | Some consensus_key -> List.fold_left (fun own_map slot -> - SlotMap.add slot (delegate, endorsing_slot) own_map) + SlotMap.add + slot + ((consensus_key, delegate), endorsing_slot) + own_map) own_map slots | None -> own_map @@ -658,7 +675,7 @@ let pp_global_state fmt {chain_id; config; validation_mode; delegates; _} = config pp_validation_mode validation_mode - Format.(pp_print_list pp_delegate) + Format.(pp_print_list pp_consensus_key) delegates let pp_option pp fmt = function @@ -739,14 +756,15 @@ let pp_elected_block fmt {proposal; endorsement_qc} = proposal.block (List.length endorsement_qc) -let pp_endorsing_slot fmt (delegate, {delegate = _; slots; endorsing_power}) = +let pp_endorsing_slot fmt (consensus_key_and_delegate, {slots; endorsing_power}) + = Format.fprintf fmt "slots: @[[%a]@],@ delegate: %a,@ endorsing_power: %d" Format.(pp_print_list ~pp_sep:pp_print_space Slot.pp) slots - pp_delegate - delegate + pp_consensus_key_and_delegate + consensus_key_and_delegate endorsing_power let pp_delegate_slots fmt {own_delegate_slots; _} = diff --git a/src/proto_alpha/lib_delegate/baking_state.mli b/src/proto_alpha/lib_delegate/baking_state.mli index 45f325e509d1f639f2deba659e1cca266cefe0fe..3650fc322708918338caddcaf197b0a4c7eaf048 100644 --- a/src/proto_alpha/lib_delegate/baking_state.mli +++ b/src/proto_alpha/lib_delegate/baking_state.mli @@ -26,16 +26,24 @@ open Protocol open Alpha_context -type delegate = { +type consensus_key = { alias : string option; public_key : Signature.public_key; public_key_hash : Signature.public_key_hash; secret_key_uri : Client_keys.sk_uri; } -val delegate_encoding : delegate Data_encoding.t +val consensus_key_encoding : consensus_key Data_encoding.t -val pp_delegate : Format.formatter -> delegate -> unit +val pp_consensus_key : Format.formatter -> consensus_key -> unit + +type consensus_key_and_delegate = consensus_key * Signature.Public_key_hash.t + +val consensus_key_and_delegate_encoding : + consensus_key_and_delegate Data_encoding.t + +val pp_consensus_key_and_delegate : + Format.formatter -> consensus_key_and_delegate -> unit type validation_mode = Node | Local of Abstract_context_index.t @@ -65,7 +73,7 @@ type block_info = { type cache = { known_timestamps : Timestamp.time Baking_cache.Timestamp_of_round_cache.t; round_timestamps : - (Timestamp.time * Round.t * delegate) + (Timestamp.time * Round.t * consensus_key_and_delegate) Baking_cache.Round_timestamp_interval_cache.t; } @@ -77,7 +85,7 @@ type global_state = { round_durations : Round.round_durations; operation_worker : Operation_worker.t; validation_mode : validation_mode; - delegates : delegate list; + delegates : consensus_key list; cache : cache; } @@ -87,14 +95,10 @@ val round_of_shell_header : Block_header.shell_header -> Round.t tzresult module SlotMap : Map.S with type key = Slot.t -type endorsing_slot = { - delegate : Signature.public_key_hash; - slots : Slot.t trace; - endorsing_power : int; -} +type endorsing_slot = {slots : Slot.t list; endorsing_power : int} type delegate_slots = { - own_delegate_slots : (delegate * endorsing_slot) SlotMap.t; + own_delegate_slots : (consensus_key_and_delegate * endorsing_slot) SlotMap.t; all_delegate_slots : endorsing_slot SlotMap.t; all_slots_by_round : Slot.t array; } @@ -189,7 +193,7 @@ val compute_delegate_slots : ?block:Block_services.block -> level:int32 -> chain:Shell_services.chain -> - delegate list -> + consensus_key list -> delegate_slots tzresult Lwt.t val create_cache : unit -> cache @@ -211,7 +215,8 @@ val pp_endorsable_payload : Format.formatter -> endorsable_payload -> unit val pp_elected_block : Format.formatter -> elected_block -> unit -val pp_endorsing_slot : Format.formatter -> delegate * endorsing_slot -> unit +val pp_endorsing_slot : + Format.formatter -> consensus_key_and_delegate * endorsing_slot -> unit val pp_delegate_slots : Format.formatter -> delegate_slots -> unit diff --git a/src/proto_alpha/lib_delegate/client_baking_denunciation.ml b/src/proto_alpha/lib_delegate/client_baking_denunciation.ml index 4f36381313075b1a4ffa8331938dccb2d3cecc10..2ebc4af5961ebbaaaca22a8a95d8ebd310d291f0 100644 --- a/src/proto_alpha/lib_delegate/client_baking_denunciation.ml +++ b/src/proto_alpha/lib_delegate/client_baking_denunciation.ml @@ -264,13 +264,13 @@ let process_block (cctxt : #Protocol_client_context.full) state Option.value ~default:Delegate_Map.empty @@ HLevel.find state.blocks_table (chain_id, level, round) in - match Delegate_Map.find baker map with + match Delegate_Map.find baker.delegate map with | None -> return @@ HLevel.add state.blocks_table (chain_id, level, round) - (Delegate_Map.add baker new_hash map) + (Delegate_Map.add baker.delegate new_hash map) | Some existing_hash when Block_hash.(existing_hash = new_hash) -> (* This case should never happen *) Events.(emit double_baking_but_not) () >>= fun () -> @@ -278,7 +278,7 @@ let process_block (cctxt : #Protocol_client_context.full) state @@ HLevel.replace state.blocks_table (chain_id, level, round) - (Delegate_Map.add baker new_hash map) + (Delegate_Map.add baker.delegate new_hash map) | Some existing_hash -> (* If a previous block made by this pkh is found for the same (level, round) we inject a double_baking_evidence *) @@ -310,7 +310,7 @@ let process_block (cctxt : #Protocol_client_context.full) state @@ HLevel.replace state.blocks_table (chain_id, level, round) - (Delegate_Map.add baker new_hash map)) + (Delegate_Map.add baker.delegate new_hash map)) (* Remove levels that are lower than the [highest_level_encountered] minus [preserved_levels] *) diff --git a/src/proto_alpha/lib_delegate/client_daemon.mli b/src/proto_alpha/lib_delegate/client_daemon.mli index 0e6f799861058fffabcd8e5f5cdca36a0ad0e9e9..790d03cee1c20bbfcc00c70ae646090fc143547d 100644 --- a/src/proto_alpha/lib_delegate/client_daemon.mli +++ b/src/proto_alpha/lib_delegate/client_daemon.mli @@ -39,7 +39,7 @@ module Baker : sig chain:Shell_services.chain -> context_path:string -> keep_alive:bool -> - Baking_state.delegate list -> + Baking_state.consensus_key list -> unit tzresult Lwt.t end diff --git a/src/proto_alpha/lib_delegate/state_transitions.ml b/src/proto_alpha/lib_delegate/state_transitions.ml index dd978072e535c5c9e5f04c1944b05bda45151b7d..6e8dd27e628468da9c64a5493c1d137d5a965e28 100644 --- a/src/proto_alpha/lib_delegate/state_transitions.ml +++ b/src/proto_alpha/lib_delegate/state_transitions.ml @@ -79,8 +79,8 @@ let make_consensus_list state proposal = let round = proposal.block.round in let block_payload_hash = proposal.block.payload_hash in SlotMap.fold - (fun _slot (delegate, slots) acc -> - ( delegate, + (fun _slot (consensus_key_and_delegate, slots) acc -> + ( consensus_key_and_delegate, {slot = Stdlib.List.hd slots.slots; level; round; block_payload_hash} ) :: acc) state.level_state.delegate_slots.own_delegate_slots @@ -96,7 +96,7 @@ let make_preendorse_action state proposal = in {state with round_state} in - let preendorsements : (delegate * consensus_content) list = + let preendorsements : (consensus_key_and_delegate * consensus_content) list = make_consensus_list state proposal in Inject_preendorsements {preendorsements; updated_state} @@ -598,7 +598,7 @@ let make_endorse_action state proposal = proposal.block.round proposal.block.payload_hash in - let endorsements : (delegate * consensus_content) list = + let endorsements : (consensus_key_and_delegate * consensus_content) list = make_consensus_list state proposal in Inject_endorsements {endorsements; updated_state} diff --git a/src/proto_alpha/lib_delegate/state_transitions.mli b/src/proto_alpha/lib_delegate/state_transitions.mli index 4b14864cb1de36a6cc8f3d828ace4cbc8fb735cd..9dde0efaf02bba9e9c9ca327a709020e226757e3 100644 --- a/src/proto_alpha/lib_delegate/state_transitions.mli +++ b/src/proto_alpha/lib_delegate/state_transitions.mli @@ -36,7 +36,7 @@ val is_acceptable_proposal_for_current_level : state -> proposal -> proposal_acceptance Lwt.t val make_consensus_list : - state -> proposal -> (delegate * consensus_content) list + state -> proposal -> (consensus_key_and_delegate * consensus_content) list val make_preendorse_action : state -> proposal -> action @@ -51,21 +51,21 @@ val handle_new_proposal : state -> proposal -> (state * action) Lwt.t val round_proposer : state -> - (delegate * endorsing_slot) SlotMap.t -> + (consensus_key_and_delegate * endorsing_slot) SlotMap.t -> Round.t -> - (delegate * endorsing_slot) option + (consensus_key_and_delegate * endorsing_slot) option val propose_fresh_block_action : endorsements:Kind.endorsement Operation.t list -> ?last_proposal:block_info -> predecessor:block_info -> state -> - delegate -> + consensus_key_and_delegate -> Round.t -> action Lwt.t val propose_block_action : - state -> delegate -> Round.t -> proposal -> action Lwt.t + state -> consensus_key_and_delegate -> Round.t -> proposal -> action Lwt.t (** Increase the current round and propose at the new round (same level), if the baker has a proposer slot. *) diff --git a/src/proto_alpha/lib_delegate/test/mockup_simulator/mockup_simulator.ml b/src/proto_alpha/lib_delegate/test/mockup_simulator/mockup_simulator.ml index 5194f4dd2cea398ab182799710190069b2c3b4a4..001336b30a94cada1073ee43d5fc16295818ff9f 100644 --- a/src/proto_alpha/lib_delegate/test/mockup_simulator/mockup_simulator.ml +++ b/src/proto_alpha/lib_delegate/test/mockup_simulator/mockup_simulator.ml @@ -135,7 +135,7 @@ module type Hooks = sig val on_start_baker : baker_position:int -> - delegates:Baking_state.delegate list -> + delegates:Baking_state.consensus_key list -> cctxt:Protocol_client_context.full -> unit Lwt.t @@ -738,7 +738,7 @@ let create_fake_node_state ~i ~live_depth } (** Start baker process. *) -let baker_process ~(delegates : Baking_state.delegate list) ~base_dir +let baker_process ~(delegates : Baking_state.consensus_key list) ~base_dir ~(genesis_block : Block_header.t * Environment_context.rpc_context) ~i ~global_chain_table ~broadcast_pipes ~(user_hooks : (module Hooks)) = let broadcast_pipe = @@ -766,7 +766,7 @@ let baker_process ~(delegates : Baking_state.delegate list) ~base_dir User_hooks.on_start_baker ~baker_position:i ~delegates ~cctxt >>= fun () -> List.iter_es (fun ({alias; public_key; public_key_hash; secret_key_uri} : - Baking_state.delegate) -> + Baking_state.consensus_key) -> let open Tezos_client_base in let name = alias |> WithExceptions.Option.get ~loc:__LOC__ in Client_keys.neuterize secret_key_uri >>=? fun public_key_uri -> @@ -1064,7 +1064,7 @@ let default_config = let make_baking_delegate ( (account : Alpha_context.Parameters.bootstrap_account), (secret : Tezos_mockup_commands.Mockup_wallet.bootstrap_secret) ) : - Baking_state.delegate = + Baking_state.consensus_key = Baking_state. { alias = Some secret.name; diff --git a/src/proto_alpha/lib_delegate/test/mockup_simulator/mockup_simulator.mli b/src/proto_alpha/lib_delegate/test/mockup_simulator/mockup_simulator.mli index 25b73e0c61827aeb7d108b82d0579e3901a189ad..9704b16d57be5550619944c77845c31530dea552 100644 --- a/src/proto_alpha/lib_delegate/test/mockup_simulator/mockup_simulator.mli +++ b/src/proto_alpha/lib_delegate/test/mockup_simulator/mockup_simulator.mli @@ -116,7 +116,7 @@ module type Hooks = sig bakers that were started for this run. *) val on_start_baker : baker_position:int -> - delegates:Baking_state.delegate list -> + delegates:Baking_state.consensus_key list -> cctxt:Protocol_client_context.full -> unit Lwt.t diff --git a/src/proto_alpha/lib_delegate/test/tenderbrute/lib/tenderbrute.ml b/src/proto_alpha/lib_delegate/test/tenderbrute/lib/tenderbrute.ml index a27b9381426c08b3f8d273bb3db89bef462f9f16..ce3f8bba638032e0e0d5ca935d40cf774956ea05 100644 --- a/src/proto_alpha/lib_delegate/test/tenderbrute/lib/tenderbrute.ml +++ b/src/proto_alpha/lib_delegate/test/tenderbrute/lib/tenderbrute.ml @@ -100,10 +100,11 @@ let check ctxt ~selection = (fun () -> LevelRoundMap.fold_es (fun (level, round) delegate ctxt -> - Delegate_storage.baking_rights_owner ctxt level ~round + Delegate_sampler.baking_rights_owner ctxt level ~round >|= Environment.wrap_tzresult - >>=? fun (ctxt, _, (_, pkh)) -> - if not (Signature.Public_key_hash.equal delegate pkh) then raise Exit + >>=? fun (ctxt, _, pk) -> + if not (Signature.Public_key_hash.equal delegate pk.delegate) then + raise Exit else return ctxt) selection ctxt diff --git a/src/proto_alpha/lib_injector/l1_operation.ml b/src/proto_alpha/lib_injector/l1_operation.ml index b0aef6cc21422cdabccee672c4a12261db8e348c..c4af4a525f213ba653d48eba4e31406dc5a72a33 100644 --- a/src/proto_alpha/lib_injector/l1_operation.ml +++ b/src/proto_alpha/lib_injector/l1_operation.ml @@ -80,6 +80,7 @@ module Manager_operation = struct | Delegation _ -> delegation_case | Register_global_constant _ -> register_global_constant_case | Set_deposits_limit _ -> set_deposits_limit_case + | Update_consensus_key _ -> update_consensus_key_case | Tx_rollup_origination -> tx_rollup_origination_case | Tx_rollup_submit_batch _ -> tx_rollup_submit_batch_case | Tx_rollup_commit _ -> tx_rollup_commit_case diff --git a/src/proto_alpha/lib_parameters/default_parameters.ml b/src/proto_alpha/lib_parameters/default_parameters.ml index 9ae091ee04f76f3ba89e16b770775e04002d740c..334302db0e33611234fe34ca1ec8e3dabfeb6736 100644 --- a/src/proto_alpha/lib_parameters/default_parameters.ml +++ b/src/proto_alpha/lib_parameters/default_parameters.ml @@ -296,12 +296,20 @@ let compute_accounts = public_key = Some public_key; amount = bootstrap_balance; delegate_to = None; + consensus_key = None; }) let bootstrap_accounts = compute_accounts bootstrap_accounts_strings -let make_bootstrap_account (pkh, pk, amount, delegate_to) = - Parameters.{public_key_hash = pkh; public_key = Some pk; amount; delegate_to} +let make_bootstrap_account (pkh, pk, amount, delegate_to, consensus_key) = + Parameters. + { + public_key_hash = pkh; + public_key = Some pk; + amount; + delegate_to; + consensus_key; + } let parameters_of_constants ?(bootstrap_accounts = bootstrap_accounts) ?(bootstrap_contracts = []) ?(commitments = []) constants = diff --git a/src/proto_alpha/lib_parameters/default_parameters.mli b/src/proto_alpha/lib_parameters/default_parameters.mli index 8aeb40866526a57d5e4230bd9879609a7c8f61d4..eff7da67497223993fdcca88bfbf31ac7066a7a4 100644 --- a/src/proto_alpha/lib_parameters/default_parameters.mli +++ b/src/proto_alpha/lib_parameters/default_parameters.mli @@ -38,7 +38,8 @@ val make_bootstrap_account : Signature.public_key_hash * Signature.public_key * Tez.t - * Signature.public_key_hash option -> + * Signature.public_key_hash option + * Signature.public_key option -> Parameters.bootstrap_account val parameters_of_constants : diff --git a/src/proto_alpha/lib_plugin/plugin.ml b/src/proto_alpha/lib_plugin/plugin.ml index a4e87f1dd557e66bbae04eb48f81556ee6eb9301..67140917931604cbb7ba78e4b5f35fcd97de2169 100644 --- a/src/proto_alpha/lib_plugin/plugin.ml +++ b/src/proto_alpha/lib_plugin/plugin.ml @@ -104,6 +104,7 @@ module Mempool = struct minimal_fees : Tez.t; minimal_nanotez_per_gas_unit : nanotez; minimal_nanotez_per_byte : nanotez; + minimal_drain_fees : Tez.t; allow_script_failure : bool; (** If [true], this makes [post_filter_manager] unconditionally return [`Passed_postfilter filter_state], no matter the operation's @@ -125,6 +126,9 @@ module Mempool = struct let default_minimal_fees = match Tez.of_mutez 100L with None -> assert false | Some t -> t + let default_minimal_drain_fees = + match Tez.of_mutez 100L with None -> assert false | Some t -> t + let default_minimal_nanotez_per_gas_unit = Q.of_int 100 let default_minimal_nanotez_per_byte = Q.of_int 1000 @@ -146,6 +150,7 @@ module Mempool = struct minimal_fees = default_minimal_fees; minimal_nanotez_per_gas_unit = default_minimal_nanotez_per_gas_unit; minimal_nanotez_per_byte = default_minimal_nanotez_per_byte; + minimal_drain_fees = default_minimal_drain_fees; allow_script_failure = true; clock_drift = None; replace_by_fee_factor = @@ -159,6 +164,7 @@ module Mempool = struct conv (fun { minimal_fees; + minimal_drain_fees; minimal_nanotez_per_gas_unit; minimal_nanotez_per_byte; allow_script_failure; @@ -167,6 +173,7 @@ module Mempool = struct max_prechecked_manager_operations; } -> ( minimal_fees, + minimal_drain_fees, minimal_nanotez_per_gas_unit, minimal_nanotez_per_byte, allow_script_failure, @@ -174,6 +181,7 @@ module Mempool = struct replace_by_fee_factor, max_prechecked_manager_operations )) (fun ( minimal_fees, + minimal_drain_fees, minimal_nanotez_per_gas_unit, minimal_nanotez_per_byte, allow_script_failure, @@ -182,6 +190,7 @@ module Mempool = struct max_prechecked_manager_operations ) -> { minimal_fees; + minimal_drain_fees; minimal_nanotez_per_gas_unit; minimal_nanotez_per_byte; allow_script_failure; @@ -189,8 +198,12 @@ module Mempool = struct replace_by_fee_factor; max_prechecked_manager_operations; }) - (obj7 + (obj8 (dft "minimal_fees" Tez.encoding default_config.minimal_fees) + (dft + "minimal_drain_fees" + Tez.encoding + default_config.minimal_drain_fees) (dft "minimal_nanotez_per_gas_unit" nanotez_enc @@ -225,6 +238,8 @@ module Mempool = struct weight : Q.t; } + type op_info = Manager_op of manager_op_info | Drained of Operation_hash.t + type manager_op_weight = {operation_hash : Operation_hash.t; weight : Q.t} let op_weight_of_info (info : manager_op_info) : manager_op_weight = @@ -243,7 +258,7 @@ module Mempool = struct type state = { grandparent_level_start : Timestamp.t option; round_zero_duration : Period.t option; - op_prechecked_managers : manager_op_info Signature.Public_key_hash.Map.t; + op_prechecked_managers : op_info Signature.Public_key_hash.Map.t; (** All managers that are the source of manager operations prechecked in the mempool. Each manager in the map is associated to a record of type [manager_op_info] (See for record details above). @@ -363,7 +378,8 @@ module Mempool = struct let ops_prechecked = match !removed_op with | None -> filter_state.ops_prechecked - | Some op -> + | Some (Drained _) -> filter_state.ops_prechecked + | Some (Manager_op op) -> ManagerOpWeightSet.remove (op_weight_of_info op) filter_state.ops_prechecked @@ -417,6 +433,7 @@ module Mempool = struct type Environment.Error_monad.error += | Manager_restriction of {oph : Operation_hash.t; fee : Tez.t} + | Manager_drained of {oph : Operation_hash.t} let () = Environment.Error_monad.register_error_kind @@ -439,7 +456,24 @@ module Mempool = struct (req "operation_hash" Operation_hash.encoding) (req "operation_fee" Tez.encoding)) (function Manager_restriction {oph; fee} -> Some (oph, fee) | _ -> None) - (fun (oph, fee) -> Manager_restriction {oph; fee}) + (fun (oph, fee) -> Manager_restriction {oph; fee}) ; + Environment.Error_monad.register_error_kind + `Temporary + ~id:"prefilter.manager_drained" + ~title: + "Only one manager operation per manager per block allowed (drained)" + ~description: + "Only one manager operation per manager per block allowed (drained)" + ~pp:(fun ppf oph -> + Format.fprintf + ppf + "Only one manager operation per manager per block allowed (found \ + Drain_delegate operation %a which takes precedence)" + Operation_hash.pp + oph) + Data_encoding.(obj1 (req "operation_hash" Operation_hash.encoding)) + (function Manager_drained {oph} -> Some oph | _ -> None) + (fun oph -> Manager_drained {oph}) type Environment.Error_monad.error += | Manager_operation_replaced of { @@ -543,13 +577,18 @@ module Mempool = struct filter_state.op_prechecked_managers with | None -> `Fresh + | Some (Drained old_hash) -> + `Fail + (`Branch_delayed + [Environment.wrap_tzerror (Manager_drained {oph = old_hash})]) | Some - { - operation_hash = old_hash; - gas_limit = old_gas; - fee = old_fee; - weight = _; - } -> + (Manager_op + { + operation_hash = old_hash; + gas_limit = old_gas; + fee = old_fee; + weight = _; + }) -> (* Manager already seen: one manager per block limitation triggered. Can replace old operation if new operation's fees are better *) if @@ -1009,7 +1048,8 @@ module Mempool = struct | Single (Double_baking_evidence _) | Single (Activate_account _) | Single (Proposals _) - | Single (Ballot _) -> + | Single (Ballot _) + | Single (Drain_delegate _) -> Lwt.return @@ `Passed_prefilter other_prio | Single (Manager_operation {source; _}) as op -> prefilter_manager_op source op @@ -1132,7 +1172,7 @@ module Mempool = struct (* Manager not seen yet, record it for next ops *) Signature.Public_key_hash.Map.add source - info + (Manager_op info) filter_state.op_prechecked_managers; operation_hash_to_manager = Operation_hash.Map.add oph source filter_state.operation_hash_to_manager @@ -1143,6 +1183,82 @@ module Mempool = struct min_prechecked_op_weight; } + let precheck_drain config filter_state validation_state oph shell + ({contents; _} as protocol_data : Kind.drain_delegate protocol_data) + delegate ~nb_successful_prechecks = + let precheck_drain_and_check_signature ~on_success = + ( Main.precheck_drain validation_state contents >>=? fun fees -> + Environment.Error_monad.fail_when + Tez.(fees < config.minimal_drain_fees) + Fees_too_low + >>=? fun () -> + let (raw_operation : Kind.drain_delegate operation) = + Alpha_context.{shell; protocol_data} + in + if Compare.Int.(nb_successful_prechecks > 0) then + (* Signature succesfully checked at least once. *) + return_unit + else + (* Signature probably never checked. *) + Main.check_drain_signature validation_state contents raw_operation ) + >|= function + | Ok () -> on_success + | Error err -> ( + let err = Environment.wrap_tztrace err in + match classify_trace err with + | Branch -> `Branch_refused err + | Permanent -> `Refused err + | Temporary -> `Branch_delayed err + | Outdated -> `Outdated err) + in + match + Signature.Public_key_hash.Map.find + delegate + filter_state.op_prechecked_managers + with + | None -> + let on_success = `Prechecked_drain `No_replace in + precheck_drain_and_check_signature ~on_success + | Some (Drained old_hash) -> + Lwt.return + (`Branch_delayed + [Environment.wrap_tzerror (Manager_drained {oph = old_hash})]) + | Some (Manager_op {operation_hash = old_oph; _}) -> + let err = + Environment.wrap_tzerror + @@ Manager_operation_replaced {old_hash = old_oph; new_hash = oph} + in + precheck_drain_and_check_signature + ~on_success:(`Prechecked_drain (`Replace (old_oph, `Outdated [err]))) + + let add_drain_restriction filter_state oph delegate replacement = + let filter_state = + match replacement with + | `No_replace -> filter_state + | `Replace (oph, _class) -> remove ~filter_state oph + in + let prechecked_operations_count = + if Operation_hash.Map.mem oph filter_state.operation_hash_to_manager then + filter_state.prechecked_operations_count + else filter_state.prechecked_operations_count + 1 + in + { + filter_state with + op_prechecked_managers = + (* Manager not seen yet, record it for next ops *) + Signature.Public_key_hash.Map.add + delegate + (Drained oph) + filter_state.op_prechecked_managers; + operation_hash_to_manager = + Operation_hash.Map.add + oph + delegate + filter_state.operation_hash_to_manager + (* Record which manager is used for the operation hash. *); + prechecked_operations_count; + } + let precheck : config -> filter_state:state -> @@ -1162,6 +1278,26 @@ module Mempool = struct oph {shell = shell_header; protocol_data = Operation_data protocol_data} ~nb_successful_prechecks -> + let precheck_drain protocol_data delegate = + precheck_drain + config + filter_state + validation_state + oph + shell_header + protocol_data + delegate + ~nb_successful_prechecks + >|= function + | `Prechecked_drain replacement -> + let filter_state = + add_drain_restriction filter_state oph delegate replacement + in + `Passed_precheck (filter_state, replacement) + | (`Refused _ | `Branch_delayed _ | `Branch_refused _ | `Outdated _) as + errs -> + errs + in let precheck_manager protocol_data source op = match get_manager_operation_gas_and_fee op with | Error err -> Lwt.return (`Refused (Environment.wrap_tztrace err)) @@ -1197,6 +1333,8 @@ module Mempool = struct errs) in match protocol_data.contents with + | Single (Drain_delegate {delegate; _}) -> + precheck_drain protocol_data delegate | Single (Manager_operation {source; _}) as op -> precheck_manager protocol_data source op | Cons (Manager_operation {source; _}, _) as op -> @@ -1288,7 +1426,8 @@ module Mempool = struct | Single_result (Double_baking_evidence_result _) | Single_result (Activate_account_result _) | Single_result Proposals_result - | Single_result Ballot_result -> + | Single_result Ballot_result + | Single_result (Drain_delegate_result _) -> Lwt.return (`Passed_postfilter filter_state) | Single_result (Manager_operation_result _) as result -> Lwt.return (post_filter_manager ctxt filter_state result config) @@ -2360,7 +2499,7 @@ module RPC = struct let operation : _ operation = {shell; protocol_data} in let hash = Operation.hash {shell; protocol_data} in let ctxt = Origination_nonce.init ctxt hash in - let payload_producer = Signature.Public_key_hash.zero in + let payload_producer = Consensus_key.zero in match protocol_data.contents with | Single (Manager_operation _) as op -> Apply.precheck_manager_contents_list ctxt op ~mempool_mode:true @@ -3983,7 +4122,8 @@ module RPC = struct module Baking_rights = struct type t = { level : Raw_level.t; - delegate : Signature.Public_key_hash.t; + delegate : public_key_hash; + consensus_key : public_key_hash; round : int; timestamp : Timestamp.t option; } @@ -3991,15 +4131,16 @@ module RPC = struct let encoding = let open Data_encoding in conv - (fun {level; delegate; round; timestamp} -> - (level, delegate, round, timestamp)) - (fun (level, delegate, round, timestamp) -> - {level; delegate; round; timestamp}) - (obj4 + (fun {level; delegate; consensus_key; round; timestamp} -> + (level, delegate, round, timestamp, consensus_key)) + (fun (level, delegate, round, timestamp, consensus_key) -> + {level; delegate; consensus_key; round; timestamp}) + (obj5 (req "level" Raw_level.encoding) (req "delegate" Signature.Public_key_hash.encoding) (req "round" uint16) - (opt "estimated_time" Timestamp.encoding)) + (opt "estimated_time" Timestamp.encoding) + (req "consensus_key" Signature.Public_key_hash.encoding)) let default_max_round = 64 @@ -4012,18 +4153,23 @@ module RPC = struct levels : Raw_level.t list; cycle : Cycle.t option; delegates : Signature.Public_key_hash.t list; + consensus_keys : Signature.Public_key_hash.t list; max_round : int option; all : bool; } let baking_rights_query = let open RPC_query in - query (fun levels cycle delegates max_round all -> - {levels; cycle; delegates; max_round; all}) + query (fun levels cycle delegates consensus_keys max_round all -> + {levels; cycle; delegates; consensus_keys; max_round; all}) |+ multi_field "level" Raw_level.rpc_arg (fun t -> t.levels) |+ opt_field "cycle" Cycle.rpc_arg (fun t -> t.cycle) |+ multi_field "delegate" Signature.Public_key_hash.rpc_arg (fun t -> t.delegates) + |+ multi_field + "consensus_key" + Signature.Public_key_hash.rpc_arg + (fun t -> t.consensus_keys) |+ opt_field "max_round" RPC_arg.uint (fun t -> t.max_round) |+ flag "all" (fun t -> t.all) |> seal @@ -4040,9 +4186,10 @@ module RPC = struct (valid) level(s) in the past or future at which the baking \ rights have to be returned.\n\ Parameter `delegate` can be used to restrict the results to \ - the given delegates. If parameter `all` is set, all the baking \ - opportunities for each baker at each level are returned, \ - instead of just the first one.\n\ + the given delegates. Parameter `consensus_key` can be used to \ + restrict the results to the given consensus_keys. If parameter \ + `all` is set, all the baking opportunities for each baker at \ + each level are returned, instead of just the first one.\n\ Returns the list of baking opportunities up to round %d. Also \ returns the minimal timestamps that correspond to these \ opportunities. The timestamps are omitted for levels in the \ @@ -4065,8 +4212,9 @@ module RPC = struct let rec loop l acc round = if Compare.Int.(round > max_round) then return (List.rev acc) else - let (Misc.LCons (pk, next)) = l in - let delegate = Signature.Public_key.hash pk in + let (Misc.LCons ({Consensus_key.consensus_pkh; delegate}, next)) = + l + in estimated_time round_durations ~current_level @@ -4075,7 +4223,16 @@ module RPC = struct ~level ~round >>?= fun timestamp -> - let acc = {level = level.level; delegate; round; timestamp} :: acc in + let acc = + { + level = level.level; + delegate; + consensus_key = consensus_pkh; + round; + timestamp; + } + :: acc + in next () >>=? fun l -> loop l acc (round + 1) in loop delegates [] 0 @@ -4120,29 +4277,44 @@ module RPC = struct if q.all then List.concat rights else List.concat_map remove_duplicated_delegates rights in - match q.delegates with - | [] -> rights - | _ :: _ as delegates -> - let is_requested p = - List.exists - (Signature.Public_key_hash.equal p.delegate) - delegates - in - List.filter is_requested rights) + let rights = + match q.delegates with + | [] -> rights + | _ :: _ as delegates -> + let is_requested p = + List.exists + (Signature.Public_key_hash.equal p.delegate) + delegates + in + List.filter is_requested rights + in + let rights = + match q.consensus_keys with + | [] -> rights + | _ :: _ as delegates -> + let is_requested p = + List.exists + (Signature.Public_key_hash.equal p.consensus_key) + delegates + in + List.filter is_requested rights + in + rights) - let get ctxt ?(levels = []) ?cycle ?(delegates = []) ?(all = false) - ?max_round block = + let get ctxt ?(levels = []) ?cycle ?(delegates = []) ?(consensus_keys = []) + ?(all = false) ?max_round block = RPC_context.make_call0 S.baking_rights ctxt block - {levels; cycle; delegates; max_round; all} + {levels; cycle; delegates; consensus_keys; max_round; all} () end module Endorsing_rights = struct type delegate_rights = { delegate : Signature.Public_key_hash.t; + consensus_key : Signature.Public_key_hash.t; first_slot : Slot.t; endorsing_power : int; } @@ -4156,14 +4328,15 @@ module RPC = struct let delegate_rights_encoding = let open Data_encoding in conv - (fun {delegate; first_slot; endorsing_power} -> - (delegate, first_slot, endorsing_power)) - (fun (delegate, first_slot, endorsing_power) -> - {delegate; first_slot; endorsing_power}) - (obj3 + (fun {delegate; consensus_key; first_slot; endorsing_power} -> + (delegate, first_slot, endorsing_power, consensus_key)) + (fun (delegate, first_slot, endorsing_power, consensus_key) -> + {delegate; first_slot; endorsing_power; consensus_key}) + (obj4 (req "delegate" Signature.Public_key_hash.encoding) (req "first_slot" Slot.encoding) - (req "endorsing_power" uint16)) + (req "endorsing_power" uint16) + (req "consensus_key" Signature.Public_key_hash.encoding)) let encoding = let open Data_encoding in @@ -4186,15 +4359,21 @@ module RPC = struct levels : Raw_level.t list; cycle : Cycle.t option; delegates : Signature.Public_key_hash.t list; + consensus_keys : Signature.Public_key_hash.t list; } let endorsing_rights_query = let open RPC_query in - query (fun levels cycle delegates -> {levels; cycle; delegates}) + query (fun levels cycle delegates consensus_keys -> + {levels; cycle; delegates; consensus_keys}) |+ multi_field "level" Raw_level.rpc_arg (fun t -> t.levels) |+ opt_field "cycle" Cycle.rpc_arg (fun t -> t.cycle) |+ multi_field "delegate" Signature.Public_key_hash.rpc_arg (fun t -> t.delegates) + |+ multi_field + "consensus_key" + Signature.Public_key_hash.rpc_arg + (fun t -> t.consensus_keys) |> seal let endorsing_rights = @@ -4210,6 +4389,8 @@ module RPC = struct deprecated because in this case the RPC takes a long time and \ returns an unreasonable amount of data. Parameter `delegate` can \ be used to restrict the results to the given delegates.\n\ + Parameter `consensus_key` can be used to restrict the results to \ + the given consensus_keys. \n\ Returns the smallest endorsing slots and the endorsing power. \ Also returns the minimal timestamp that corresponds to endorsing \ at the given level. The timestamps are omitted for levels in the \ @@ -4238,8 +4419,15 @@ module RPC = struct >>?= fun estimated_time -> let rights = Slot.Map.fold - (fun first_slot (_pk, delegate, endorsing_power) acc -> - {delegate; first_slot; endorsing_power} :: acc) + (fun first_slot + ( { + Consensus_key.delegate; + consensus_pk = _; + consensus_pkh = consensus_key; + }, + endorsing_power ) + acc -> + {delegate; consensus_key; first_slot; endorsing_power} :: acc) rights [] in @@ -4259,49 +4447,76 @@ module RPC = struct in List.map_es (endorsing_rights_at_level ctxt) levels >|=? fun rights_per_level -> - match q.delegates with - | [] -> rights_per_level - | _ :: _ as delegates -> - List.filter_map - (fun rights_at_level -> - let is_requested p = - List.exists - (Signature.Public_key_hash.equal p.delegate) - delegates - in - match - List.filter is_requested rights_at_level.delegates_rights - with - | [] -> None - | delegates_rights -> - Some {rights_at_level with delegates_rights}) - rights_per_level) - - let get ctxt ?(levels = []) ?cycle ?(delegates = []) block = + let rights_per_level = + match q.delegates with + | [] -> rights_per_level + | _ :: _ as delegates -> + List.filter_map + (fun rights_at_level -> + let is_requested p = + List.exists + (Signature.Public_key_hash.equal p.delegate) + delegates + in + match + List.filter is_requested rights_at_level.delegates_rights + with + | [] -> None + | delegates_rights -> + Some {rights_at_level with delegates_rights}) + rights_per_level + in + let rights_per_level = + match q.consensus_keys with + | [] -> rights_per_level + | _ :: _ as consensus_keys -> + List.filter_map + (fun rights_at_level -> + let is_requested p = + List.exists + (Signature.Public_key_hash.equal p.consensus_key) + consensus_keys + in + match + List.filter is_requested rights_at_level.delegates_rights + with + | [] -> None + | delegates_rights -> + Some {rights_at_level with delegates_rights}) + rights_per_level + in + rights_per_level) + + let get ctxt ?(levels = []) ?cycle ?(delegates = []) ?(consensus_keys = []) + block = RPC_context.make_call0 S.endorsing_rights ctxt block - {levels; cycle; delegates} + {levels; cycle; delegates; consensus_keys} () end module Validators = struct type t = { level : Raw_level.t; - delegate : Signature.Public_key_hash.t; + delegate : Signature.public_key_hash; + consensus_key : Signature.public_key_hash; slots : Slot.t list; } let encoding = let open Data_encoding in conv - (fun {level; delegate; slots} -> (level, delegate, slots)) - (fun (level, delegate, slots) -> {level; delegate; slots}) - (obj3 + (fun {level; delegate; consensus_key; slots} -> + (level, delegate, slots, consensus_key)) + (fun (level, delegate, slots, consensus_key) -> + {level; delegate; consensus_key; slots}) + (obj4 (req "level" Raw_level.encoding) (req "delegate" Signature.Public_key_hash.encoding) - (req "slots" (list Slot.encoding))) + (req "slots" (list Slot.encoding)) + (req "consensus_key" Signature.Public_key_hash.encoding)) module S = struct open Data_encoding @@ -4311,14 +4526,20 @@ module RPC = struct type validators_query = { levels : Raw_level.t list; delegates : Signature.Public_key_hash.t list; + consensus_keys : Signature.Public_key_hash.t list; } let validators_query = let open RPC_query in - query (fun levels delegates -> {levels; delegates}) + query (fun levels delegates consensus_keys -> + {levels; delegates; consensus_keys}) |+ multi_field "level" Raw_level.rpc_arg (fun t -> t.levels) |+ multi_field "delegate" Signature.Public_key_hash.rpc_arg (fun t -> t.delegates) + |+ multi_field + "consensus_key" + Signature.Public_key_hash.rpc_arg + (fun t -> t.consensus_keys) |> seal let validators = @@ -4330,7 +4551,8 @@ module RPC = struct Parameter `level` can be used to specify the (valid) level(s) in \ the past or future at which the endorsement rights have to be \ returned. Parameter `delegate` can be used to restrict the \ - results to the given delegates.\n" + results to the given delegates. Parameter `consensus_key` can be \ + used to restrict the results to the given consensus_keys.\n" ~query:validators_query ~output:(list encoding) path @@ -4339,9 +4561,9 @@ module RPC = struct let endorsing_slots_at_level ctxt level = Baking.endorsing_rights ctxt level >|=? fun (_, rights) -> Signature.Public_key_hash.Map.fold - (fun delegate slots acc -> - {level = level.level; delegate; slots} :: acc) - (rights :> Slot.t list Signature.Public_key_hash.Map.t) + (fun _pkh {Baking.delegate; consensus_key; slots} acc -> + {level = level.level; delegate; consensus_key; slots} :: acc) + rights [] let register () = @@ -4355,18 +4577,37 @@ module RPC = struct in List.concat_map_es (endorsing_slots_at_level ctxt) levels >|=? fun rights -> - match q.delegates with - | [] -> rights - | _ :: _ as delegates -> - let is_requested p = - List.exists - (Signature.Public_key_hash.equal p.delegate) - delegates - in - List.filter is_requested rights) + let rights = + match q.delegates with + | [] -> rights + | _ :: _ as delegates -> + let is_requested p = + List.exists + (Signature.Public_key_hash.equal p.delegate) + delegates + in + List.filter is_requested rights + in + let rights = + match q.consensus_keys with + | [] -> rights + | _ :: _ as delegates -> + let is_requested p = + List.exists + (Signature.Public_key_hash.equal p.consensus_key) + delegates + in + List.filter is_requested rights + in + rights) - let get ctxt ?(levels = []) ?(delegates = []) block = - RPC_context.make_call0 S.validators ctxt block {levels; delegates} () + let get ctxt ?(levels = []) ?(delegates = []) ?(consensus_keys = []) block = + RPC_context.make_call0 + S.validators + ctxt + block + {levels; delegates; consensus_keys} + () end module S = struct diff --git a/src/proto_alpha/lib_plugin/test/generators.ml b/src/proto_alpha/lib_plugin/test/generators.ml index 38d6e4e135091e678f30ac175bea785fa76f1283..36782258262164d90b8a693c5ce1c096f8cd8d84 100644 --- a/src/proto_alpha/lib_plugin/test/generators.ml +++ b/src/proto_alpha/lib_plugin/test/generators.ml @@ -80,7 +80,7 @@ let filter_state_gen : Plugin.Mempool.state QCheck2.Gen.t = op_prechecked_managers = Signature.Public_key_hash.Map.add pkh - info + (Manager_op info) state.op_prechecked_managers; operation_hash_to_manager = Operation_hash.Map.add oph pkh state.operation_hash_to_manager; diff --git a/src/proto_alpha/lib_plugin/test/test_filter_state.ml b/src/proto_alpha/lib_plugin/test/test_filter_state.ml index 1a06a6762e87a7eb6cb1614061a75bd86b1124ce..f28f180505eed2b04e8f368a09fe1977b66b1923 100644 --- a/src/proto_alpha/lib_plugin/test/test_filter_state.ml +++ b/src/proto_alpha/lib_plugin/test/test_filter_state.ml @@ -95,7 +95,7 @@ let test_check_manager_restriction_fail = op_prechecked_managers = Signature.Public_key_hash.Map.add pkh - {op_info with fee = Alpha_context.Tez.one} + (Manager_op {op_info with fee = Alpha_context.Tez.one}) (* We force higher fee than below: [one > zero]. *) filter_state.op_prechecked_managers; } @@ -150,7 +150,7 @@ let test_check_manager_restriction_replace = op_prechecked_managers = Signature.Public_key_hash.Map.add pkh - op_info + (Manager_op op_info) filter_state.op_prechecked_managers; } in diff --git a/src/proto_alpha/lib_plugin/test/test_utils.ml b/src/proto_alpha/lib_plugin/test/test_utils.ml index cf25d367381ee55265bbbbee3b643ca120b032b5..d26e76f16cc698f361a0fafb8b2eda44f2b5a3b6 100644 --- a/src/proto_alpha/lib_plugin/test/test_utils.ml +++ b/src/proto_alpha/lib_plugin/test/test_utils.ml @@ -40,18 +40,28 @@ let pp_prechecked_managers fmt set = Format.fprintf fmt "[%a]" - (Format.pp_print_list (fun ppf (pkh, (op_info : manager_op_info)) -> - Format.fprintf - ppf - "(%a -> (hash:%a,gas:%a,fee:%a))" - Signature.Public_key_hash.pp - pkh - Operation_hash.pp - op_info.operation_hash - Alpha_context.Gas.Arith.pp - op_info.gas_limit - Alpha_context.Tez.pp - op_info.fee)) + (Format.pp_print_list (fun ppf (pkh, info) -> + match info with + | Drained oph -> + Format.fprintf + ppf + "(%a -> (hash:%a,drained))" + Signature.Public_key_hash.pp + pkh + Operation_hash.pp + oph + | Manager_op op_info -> + Format.fprintf + ppf + "(%a -> (hash:%a,gas:%a,fee:%a))" + Signature.Public_key_hash.pp + pkh + Operation_hash.pp + op_info.operation_hash + Alpha_context.Gas.Arith.pp + op_info.gas_limit + Alpha_context.Tez.pp + op_info.fee)) (Signature.Public_key_hash.Map.bindings set) let pp_operation_hash_manager fmt map = @@ -113,13 +123,22 @@ let pp_state fmt state = state.min_prechecked_op_weight let eq_prechecked_managers = - Signature.Public_key_hash.Map.equal - (fun - ({operation_hash = oph1; gas_limit = _; fee = _; weight = _} : - manager_op_info) - ({operation_hash = oph2; gas_limit = _; fee = _; weight = _} : - manager_op_info) - -> Operation_hash.equal oph1 oph2) + Signature.Public_key_hash.Map.equal (fun op1 op2 -> + let oph1 = + match op1 with + | Drained oph1 + | Manager_op {operation_hash = oph1; gas_limit = _; fee = _; weight = _} + -> + oph1 + in + let oph2 = + match op2 with + | Drained oph2 + | Manager_op {operation_hash = oph2; gas_limit = _; fee = _; weight = _} + -> + oph2 + in + Operation_hash.equal oph1 oph2) (* This function needs to be updated if the filter state is extended *) let eq_state s1 s2 = diff --git a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL index 0a8af4e50aaf17a70963d9f433ba365a5aa5e041..104ed3a178a07a146ea768d800b180c5f19354bc 100644 --- a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL +++ b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL @@ -111,11 +111,16 @@ "Stake_storage", "Contract_storage", "Token", + "Fees_storage", + "Delegate_consensus_key", "Delegate_storage", + "Delegate_missed_endorsements_storage", + "Delegate_slashed_deposits_storage", + "Delegate_sampler", + "Delegate_cycles", "Bootstrap_storage", "Vote_storage", - "Fees_storage", "Ticket_storage", "Liquidity_baking_storage", "Liquidity_baking_cpmm", diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index a8171c397beb50802e8474285286da5a928c09cf..2292460c7489b1e348f74f2a9a37bc4e4f0fc4fd 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -238,6 +238,12 @@ module Contract = struct let get_manager_key = Contract_manager_storage.get_manager_key + module Delegate = struct + let find = Contract_delegate_storage.find + + include Delegate_storage.Contract + end + module Internal_for_tests = struct include Contract_repr include Contract_storage @@ -395,9 +401,15 @@ module Bond_id = struct end module Receipt = Receipt_repr +module Consensus_key = Delegate_consensus_key module Delegate = struct include Delegate_storage + include Delegate_missed_endorsements_storage + include Delegate_slashed_deposits_storage + include Delegate_cycles + + let deactivated = Delegate_activation_storage.is_inactive type deposits = Storage.deposits = { initial_amount : Tez.t; @@ -409,25 +421,21 @@ module Delegate = struct let prepare_stake_distribution = Stake_storage.prepare_stake_distribution - let registered = Contract_delegate_storage.registered - - let find = Contract_delegate_storage.find - let delegated_contracts = Contract_delegate_storage.delegated_contracts + + module Consensus_key = Delegate_consensus_key end module Stake_distribution = struct let snapshot = Stake_storage.snapshot - let compute_snapshot_index = Delegate_storage.compute_snapshot_index - - let baking_rights_owner = Delegate.baking_rights_owner + let compute_snapshot_index = Delegate_sampler.compute_snapshot_index - let slot_owner = Delegate.slot_owner + let baking_rights_owner = Delegate_sampler.baking_rights_owner - let delegate_pubkey = Delegate.pubkey + let slot_owner = Delegate_sampler.slot_owner - let get_staking_balance = Delegate.staking_balance + let get_staking_balance = Delegate_storage.staking_balance end module Nonce = Nonce_storage diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index d32e6023936badc16d1e031ec8dc636c8cffc9f8..a419c7d4a53c7f9cef202f3df739af0556dc1dbd 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -1202,9 +1202,6 @@ module Seed : sig val for_cycle : context -> Cycle.t -> seed tzresult Lwt.t - val cycle_end : - context -> Cycle.t -> (context * Nonce.unrevealed list) tzresult Lwt.t - val seed_encoding : seed Data_encoding.t end @@ -1586,6 +1583,18 @@ module Contract : sig script:Script.t * Lazy_storage.diffs option -> context tzresult Lwt.t + (** Functions for handling the delegate of a contract.*) + module Delegate : sig + (** see {!Contract_delegate_storage.find} for documentation *) + val init : context -> t -> public_key_hash -> context tzresult Lwt.t + + (** see {!Delegate_table_storage.set} for documentation *) + val set : context -> t -> public_key_hash option -> context tzresult Lwt.t + + (** see {!Delegate_table_storage.find} for documentation *) + val find : context -> t -> public_key_hash option tzresult Lwt.t + end + module Internal_for_tests : sig (** see [Contract_repr.originated_contract] for documentation *) val originated_contract : Origination_nonce.Internal_for_tests.t -> t @@ -1645,7 +1654,7 @@ end module Tx_rollup_withdraw : sig type order = { - claimer : Signature.Public_key_hash.t; + claimer : public_key_hash; ticket_hash : Ticket_hash.t; amount : Tx_rollup_l2_qty.t; } @@ -1768,7 +1777,7 @@ module Tx_rollup_reveal : sig ty : Script.lazy_expr; ticketer : Contract.t; amount : Tx_rollup_l2_qty.t; - claimer : Signature.Public_key_hash.t; + claimer : public_key_hash; } val encoding : t Data_encoding.t @@ -1922,7 +1931,7 @@ module Tx_rollup_commitment : sig type nonrec t = { commitment : Compact.t; commitment_hash : Tx_rollup_commitment_hash.t; - committer : Signature.Public_key_hash.t; + committer : public_key_hash; submitted_at : Raw_level.t; finalized_at : Raw_level.t option; } @@ -1953,10 +1962,9 @@ module Tx_rollup_commitment : sig context -> Tx_rollup.t -> Tx_rollup_state.t -> - Signature.public_key_hash -> + public_key_hash -> Full.t -> - (context * Tx_rollup_state.t * Signature.public_key_hash option) tzresult - Lwt.t + (context * Tx_rollup_state.t * public_key_hash option) tzresult Lwt.t val find : context -> @@ -1992,16 +2000,10 @@ module Tx_rollup_commitment : sig (context * Submitted_commitment.t) tzresult Lwt.t val pending_bonded_commitments : - context -> - Tx_rollup.t -> - Signature.public_key_hash -> - (context * int) tzresult Lwt.t + context -> Tx_rollup.t -> public_key_hash -> (context * int) tzresult Lwt.t val has_bond : - context -> - Tx_rollup.t -> - Signature.public_key_hash -> - (context * bool) tzresult Lwt.t + context -> Tx_rollup.t -> public_key_hash -> (context * bool) tzresult Lwt.t val finalize_commitment : context -> @@ -2016,16 +2018,10 @@ module Tx_rollup_commitment : sig (context * Tx_rollup_state.t * Tx_rollup_level.t) tzresult Lwt.t val remove_bond : - context -> - Tx_rollup.t -> - Signature.public_key_hash -> - context tzresult Lwt.t + context -> Tx_rollup.t -> public_key_hash -> context tzresult Lwt.t val slash_bond : - context -> - Tx_rollup.t -> - Signature.public_key_hash -> - (context * bool) tzresult Lwt.t + context -> Tx_rollup.t -> public_key_hash -> (context * bool) tzresult Lwt.t val reject_commitment : context -> @@ -2071,8 +2067,8 @@ module Tx_rollup_errors : sig } | Level_already_has_commitment of Tx_rollup_level.t | Wrong_inbox_hash - | Bond_does_not_exist of Signature.public_key_hash - | Bond_in_use of Signature.public_key_hash + | Bond_does_not_exist of public_key_hash + | Bond_in_use of public_key_hash | No_uncommitted_inbox | No_commitment_to_finalize | No_commitment_to_remove @@ -2156,7 +2152,7 @@ module Receipt : sig | Baking_bonuses | Storage_fees | Double_signing_punishments - | Lost_endorsing_rewards of Signature.Public_key_hash.t * bool * bool + | Lost_endorsing_rewards of public_key_hash * bool * bool | Liquidity_baking_subsidies | Burned | Commitments of Blinded_public_key_hash.t @@ -2187,23 +2183,31 @@ module Receipt : sig val group_balance_updates : balance_updates -> balance_updates tzresult end -module Delegate : sig - val init : - context -> - Contract.t -> - Signature.Public_key_hash.t -> - context tzresult Lwt.t +module Consensus_key : sig + type pk = { + delegate : Signature.Public_key_hash.t; + consensus_pk : Signature.Public_key.t; + consensus_pkh : Signature.Public_key_hash.t; + } + + type t = { + delegate : Signature.Public_key_hash.t; + consensus_pkh : Signature.Public_key_hash.t; + } - val find : context -> Contract.t -> public_key_hash option tzresult Lwt.t + val zero : t + + val pp : Format.formatter -> t -> unit - val set : - context -> Contract.t -> public_key_hash option -> context tzresult Lwt.t + val pkh : pk -> t +end +module Delegate : sig val frozen_deposits_limit : - context -> Signature.Public_key_hash.t -> Tez.t option tzresult Lwt.t + context -> public_key_hash -> Tez.t option tzresult Lwt.t val set_frozen_deposits_limit : - context -> Signature.Public_key_hash.t -> Tez.t option -> context Lwt.t + context -> public_key_hash -> Tez.t option -> context Lwt.t val fold : context -> @@ -2214,7 +2218,15 @@ module Delegate : sig val list : context -> public_key_hash list Lwt.t - val check_delegate : context -> public_key_hash -> unit tzresult Lwt.t + val check_registered : context -> public_key_hash -> unit tzresult Lwt.t + + val drain : + context -> + delegate:public_key_hash -> + destination:public_key_hash -> + (context * bool * Tez.t * Receipt.balance_updates) tzresult Lwt.t + + val drain_fees : Tez.t -> Tez.t tzresult type participation_info = { expected_cycle_activity : int; @@ -2225,22 +2237,13 @@ module Delegate : sig expected_endorsing_rewards : Tez.t; } - val delegate_participation_info : + val participation_info : context -> public_key_hash -> participation_info tzresult Lwt.t val cycle_end : context -> Cycle.t -> - Nonce.unrevealed list -> - (context * Receipt.balance_updates * Signature.Public_key_hash.t list) - tzresult - Lwt.t - - val already_slashed_for_double_endorsing : - context -> public_key_hash -> Level.t -> bool tzresult Lwt.t - - val already_slashed_for_double_baking : - context -> public_key_hash -> Level.t -> bool tzresult Lwt.t + (context * Receipt.balance_updates * public_key_hash list) tzresult Lwt.t val punish_double_endorsing : context -> @@ -2260,15 +2263,15 @@ module Delegate : sig val record_baking_activity_and_pay_rewards_and_fees : context -> - payload_producer:Signature.Public_key_hash.t -> - block_producer:Signature.Public_key_hash.t -> + payload_producer:public_key_hash -> + block_producer:public_key_hash -> baking_reward:Tez.t -> reward_bonus:Tez.t option -> (context * Receipt.balance_updates) tzresult Lwt.t val record_endorsing_participation : context -> - delegate:Signature.Public_key_hash.t -> + delegate:public_key_hash -> participation:level_participation -> endorsing_power:int -> context tzresult Lwt.t @@ -2277,24 +2280,40 @@ module Delegate : sig val frozen_deposits : context -> public_key_hash -> deposits tzresult Lwt.t - val staking_balance : - context -> Signature.Public_key_hash.t -> Tez.t tzresult Lwt.t + val staking_balance : context -> public_key_hash -> Tez.t tzresult Lwt.t - val delegated_contracts : - context -> Signature.Public_key_hash.t -> Contract.t list Lwt.t + val delegated_contracts : context -> public_key_hash -> Contract.t list Lwt.t - val delegated_balance : - context -> Signature.Public_key_hash.t -> Tez.t tzresult Lwt.t + val delegated_balance : context -> public_key_hash -> Tez.t tzresult Lwt.t - val registered : context -> Signature.Public_key_hash.t -> bool tzresult Lwt.t + val registered : context -> public_key_hash -> bool Lwt.t - val deactivated : - context -> Signature.Public_key_hash.t -> bool tzresult Lwt.t + val deactivated : context -> public_key_hash -> bool tzresult Lwt.t val last_cycle_before_deactivation : - context -> Signature.Public_key_hash.t -> Cycle.t tzresult Lwt.t + context -> public_key_hash -> Cycle.t tzresult Lwt.t + + val manager_pubkey : + context -> + Signature.public_key_hash -> + Signature.Public_key.t tzresult Lwt.t + + module Consensus_key : sig + val active_pubkey : + context -> public_key_hash -> Consensus_key.pk tzresult Lwt.t + + val pending_updates : + context -> + public_key_hash -> + (Cycle.t * public_key_hash) list tzresult Lwt.t - val pubkey : context -> public_key_hash -> public_key tzresult Lwt.t + val register_update : + context -> + public_key_hash -> + Cycle.t -> + public_key -> + context tzresult Lwt.t + end val prepare_stake_distribution : context -> context tzresult Lwt.t end @@ -2355,8 +2374,7 @@ module Vote : sig val recorded_proposal_count_for_delegate : context -> public_key_hash -> int tzresult Lwt.t - val listings_encoding : - (Signature.Public_key_hash.t * int64) list Data_encoding.t + val listings_encoding : (public_key_hash * int64) list Data_encoding.t val update_listings : context -> context tzresult Lwt.t @@ -2380,13 +2398,12 @@ module Vote : sig val delegate_info_encoding : delegate_info Data_encoding.t val get_delegate_info : - context -> Signature.Public_key_hash.t -> delegate_info tzresult Lwt.t + context -> public_key_hash -> delegate_info tzresult Lwt.t - val get_voting_power_free : - context -> Signature.Public_key_hash.t -> int64 tzresult Lwt.t + val get_voting_power_free : context -> public_key_hash -> int64 tzresult Lwt.t val get_voting_power : - context -> Signature.Public_key_hash.t -> (context * int64) tzresult Lwt.t + context -> public_key_hash -> (context * int64) tzresult Lwt.t val get_total_voting_power_free : context -> int64 tzresult Lwt.t @@ -2461,8 +2478,7 @@ module Sc_rollup : sig val encoding : t Data_encoding.t end - module Staker : - S.SIGNATURE_PUBLIC_KEY_HASH with type t = Signature.Public_key_hash.t + module Staker : S.SIGNATURE_PUBLIC_KEY_HASH with type t = public_key_hash module State_hash : S.HASH @@ -2752,7 +2768,7 @@ module Block_header : sig Liquidity_baking_repr.liquidity_baking_toggle_vote; } - type protocol_data = {contents : contents; signature : Signature.t} + type protocol_data = {contents : contents; signature : signature} type t = {shell : Block_header.shell_header; protocol_data : protocol_data} @@ -2809,8 +2825,7 @@ module Block_header : sig predecessor_round:Round.t -> unit tzresult - val check_signature : - t -> Chain_id.t -> Signature.Public_key.t -> unit tzresult + val check_signature : t -> Chain_id.t -> public_key -> unit tzresult val begin_validate_block_header : block_header:t -> @@ -2819,7 +2834,7 @@ module Block_header : sig predecessor_round:Round.t -> fitness:Fitness.t -> timestamp:Time.t -> - delegate_pk:Signature.public_key -> + delegate_pk:public_key -> round_durations:Round.round_durations -> proof_of_work_threshold:int64 -> expected_commitment:bool -> @@ -2886,6 +2901,10 @@ module Kind : sig type set_deposits_limit = Set_deposits_limit_kind + type update_consensus_key = Update_consensus_key_kind + + type drain_delegate = Drain_delegate_kind + type failing_noop = Failing_noop_kind type register_global_constant = Register_global_constant_kind @@ -2929,6 +2948,7 @@ module Kind : sig | Delegation_manager_kind : delegation manager | Register_global_constant_manager_kind : register_global_constant manager | Set_deposits_limit_manager_kind : set_deposits_limit manager + | Update_consensus_key_manager_kind : update_consensus_key manager | Tx_rollup_origination_manager_kind : tx_rollup_origination manager | Tx_rollup_submit_batch_manager_kind : tx_rollup_submit_batch manager | Tx_rollup_commit_manager_kind : tx_rollup_commit manager @@ -2972,7 +2992,7 @@ val consensus_content_encoding : consensus_content Data_encoding.t val pp_consensus_content : Format.formatter -> consensus_content -> unit type origination = { - delegate : Signature.Public_key_hash.t option; + delegate : public_key_hash option; script : Script.t; credit : Tez.tez; } @@ -2984,7 +3004,7 @@ type 'kind operation = { and 'kind protocol_data = { contents : 'kind contents_list; - signature : Signature.t option; + signature : signature option; } and _ contents_list = @@ -3022,21 +3042,27 @@ and _ contents = } -> Kind.activate_account contents | Proposals : { - source : Signature.Public_key_hash.t; + source : public_key_hash; period : int32; proposals : Protocol_hash.t list; } -> Kind.proposals contents | Ballot : { - source : Signature.Public_key_hash.t; + source : public_key_hash; period : int32; proposal : Protocol_hash.t; ballot : Vote.ballot; } -> Kind.ballot contents + | Drain_delegate : { + consensus_key : Signature.Public_key_hash.t; + delegate : Signature.Public_key_hash.t; + destination : Signature.Public_key_hash.t; + } + -> Kind.drain_delegate contents | Failing_noop : string -> Kind.failing_noop contents | Manager_operation : { - source : Signature.Public_key_hash.t; + source : public_key_hash; fee : Tez.tez; counter : counter; operation : 'kind manager_operation; @@ -3046,7 +3072,7 @@ and _ contents = -> 'kind Kind.manager contents and _ manager_operation = - | Reveal : Signature.Public_key.t -> Kind.reveal manager_operation + | Reveal : public_key -> Kind.reveal manager_operation | Transaction : { amount : Tez.tez; parameters : Script.lazy_expr; @@ -3055,9 +3081,7 @@ and _ manager_operation = } -> Kind.transaction manager_operation | Origination : origination -> Kind.origination manager_operation - | Delegation : - Signature.Public_key_hash.t option - -> Kind.delegation manager_operation + | Delegation : public_key_hash option -> Kind.delegation manager_operation | Register_global_constant : { value : Script.lazy_expr; } @@ -3065,6 +3089,9 @@ and _ manager_operation = | Set_deposits_limit : Tez.t option -> Kind.set_deposits_limit manager_operation + | Update_consensus_key : + Cycle.t * Signature.Public_key.t + -> Kind.update_consensus_key manager_operation | Tx_rollup_origination : Kind.tx_rollup_origination manager_operation | Tx_rollup_submit_batch : { tx_rollup : Tx_rollup.t; @@ -3274,6 +3301,8 @@ module Operation : sig val ballot_case : Kind.ballot case + val drain_delegate_case : Kind.drain_delegate case + val failing_noop_case : Kind.failing_noop case val reveal_case : Kind.reveal Kind.manager case @@ -3284,6 +3313,8 @@ module Operation : sig val delegation_case : Kind.delegation Kind.manager case + val update_consensus_key_case : Kind.update_consensus_key Kind.manager case + val tx_rollup_origination_case : Kind.tx_rollup_origination Kind.manager case @@ -3349,6 +3380,10 @@ module Operation : sig val delegation_case : Kind.delegation case + val update_consensus_key_tag : int + + val update_consensus_key_case : Kind.update_consensus_key case + val register_global_constant_case : Kind.register_global_constant case val set_deposits_limit_case : Kind.set_deposits_limit case @@ -3404,18 +3439,12 @@ module Stake_distribution : sig context -> Level.t -> round:Round.t -> - (context * Slot.t * (public_key * public_key_hash)) tzresult Lwt.t + (context * Slot.t * Consensus_key.pk) tzresult Lwt.t val slot_owner : - context -> - Level.t -> - Slot.t -> - (context * (public_key * public_key_hash)) tzresult Lwt.t - - val delegate_pubkey : context -> public_key_hash -> public_key tzresult Lwt.t + context -> Level.t -> Slot.t -> (context * Consensus_key.pk) tzresult Lwt.t - val get_staking_balance : - context -> Signature.Public_key_hash.t -> Tez.t tzresult Lwt.t + val get_staking_balance : context -> public_key_hash -> Tez.t tzresult Lwt.t end module Commitment : sig @@ -3492,6 +3521,7 @@ module Parameters : sig public_key : public_key option; amount : Tez.t; delegate_to : public_key_hash option; + consensus_key : public_key option; } type bootstrap_contract = { @@ -3509,6 +3539,8 @@ module Parameters : sig no_reward_cycles : int option; } + val bootstrap_account_encoding : bootstrap_account Data_encoding.t + val encoding : t Data_encoding.t end @@ -3577,6 +3609,7 @@ module Consensus : sig and type 'a slot_map := 'a Slot.Map.t and type slot_set := Slot.Set.t and type round := Round.t + and type consensus_pk := Consensus_key.pk val store_endorsement_branch : context -> Block_hash.t * Block_payload_hash.t -> context Lwt.t @@ -3590,8 +3623,8 @@ module Token : sig type container = [ `Contract of Contract.t | `Collected_commitments of Blinded_public_key_hash.t - | `Delegate_balance of Signature.Public_key_hash.t - | `Frozen_deposits of Signature.Public_key_hash.t + | `Delegate_balance of public_key_hash + | `Frozen_deposits of public_key_hash | `Block_fees | `Frozen_bonds of Contract.t * Bond_id.t ] @@ -3612,7 +3645,7 @@ module Token : sig type sink = [ `Storage_fees | `Double_signing_punishments - | `Lost_endorsing_rewards of Signature.Public_key_hash.t * bool * bool + | `Lost_endorsing_rewards of public_key_hash * bool * bool | `Burned | `Tx_rollup_rejection_punishments | container ] diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 183dfb0524c7758d709017d450d7476d0aaf973d..2fd5a2d8eaf8556b660fcae5605237b1bb2f2a9a 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -93,6 +93,20 @@ type error += } | Set_deposits_limit_on_unregistered_delegate of Signature.Public_key_hash.t | Set_deposits_limit_too_high of {limit : Tez.t; max_limit : Tez.t} + | Update_consensus_key_on_unregistered_delegate of Signature.Public_key_hash.t + | Drain_delegate_on_unregistered_delegate of Signature.Public_key_hash.t + | Invalid_drain_delegate_inactive_key of { + delegate : Signature.Public_key_hash.t; + consensus_key : Signature.Public_key_hash.t; + active_consensus_key : Signature.Public_key_hash.t; + } + | Invalid_drain_delegate_no_consensus_key of Signature.Public_key_hash.t + | Invalid_drain_delegate_noop of Signature.Public_key_hash.t + | Invalid_drain_delegate_no_allocation_burn of { + delegate : Signature.Public_key_hash.t; + destination : Signature.Public_key_hash.t; + } + | Unauthorized_key_for_drain of Signature.Public_key_hash.t | Empty_transaction of Contract.t | Tx_rollup_feature_disabled | Tx_rollup_invalid_transaction_ticket_amount @@ -107,7 +121,6 @@ type error += delegate1 : Signature.Public_key_hash.t; delegate2 : Signature.Public_key_hash.t; } - | Unrequired_denunciation | Too_early_denunciation of { kind : denunciation_kind; level : Raw_level.t; @@ -469,6 +482,133 @@ let () = | Set_deposits_limit_too_high {limit; max_limit} -> Some (limit, max_limit) | _ -> None) (fun (limit, max_limit) -> Set_deposits_limit_too_high {limit; max_limit}) ; + register_error_kind + `Temporary + ~id:"operation.update_consensus_key_on_unregistered_delegate" + ~title:"Update consensus key on an unregistered delegate" + ~description:"Cannot update consensus key an unregistered delegate." + ~pp:(fun ppf c -> + Format.fprintf + ppf + "Cannot update the consensus key on the unregistered delegate %a." + Signature.Public_key_hash.pp + c) + Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) + (function + | Update_consensus_key_on_unregistered_delegate c -> Some c | _ -> None) + (fun c -> Update_consensus_key_on_unregistered_delegate c) ; + register_error_kind + `Temporary + ~id:"operation.drain_delegate_key_on_unregistered_delegate" + ~title:"Drain delegate key on an unregistered delegate" + ~description:"Cannot drain an unregistered delegate." + ~pp:(fun ppf c -> + Format.fprintf + ppf + "Cannot drain an unregistered delegate %a." + Signature.Public_key_hash.pp + c) + Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) + (function Drain_delegate_on_unregistered_delegate c -> Some c | _ -> None) + (fun c -> Drain_delegate_on_unregistered_delegate c) ; + register_error_kind + `Temporary + ~id:"operation.invalid_drain.inactive_key" + ~title:"Drain delegate with an inactive consensus key" + ~description:"Cannot drain with an inactive consensus key." + ~pp:(fun ppf (delegate, consensus_key, active_consensus_key) -> + Format.fprintf + ppf + "Consensus key %a is not active consensus key for delegate %a. The \ + active consensus key is %a." + Signature.Public_key_hash.pp + consensus_key + Signature.Public_key_hash.pp + delegate + Signature.Public_key_hash.pp + active_consensus_key) + Data_encoding.( + obj3 + (req "delegate" Signature.Public_key_hash.encoding) + (req "consensus_key" Signature.Public_key_hash.encoding) + (req "active_consensus_key" Signature.Public_key_hash.encoding)) + (function + | Invalid_drain_delegate_inactive_key + {delegate; consensus_key; active_consensus_key} -> + Some (delegate, consensus_key, active_consensus_key) + | _ -> None) + (fun (delegate, consensus_key, active_consensus_key) -> + Invalid_drain_delegate_inactive_key + {delegate; consensus_key; active_consensus_key}) ; + register_error_kind + `Temporary + ~id:"operation.invalid_drain.no_consensus_key" + ~title:"Drain a delegate without consensus key" + ~description:"Cannot drain a delegate without consensus key." + ~pp:(fun ppf delegate -> + Format.fprintf + ppf + "There is no active consensus key for delegate %a." + Signature.Public_key_hash.pp + delegate) + Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) + (function Invalid_drain_delegate_no_consensus_key c -> Some c | _ -> None) + (fun c -> Invalid_drain_delegate_no_consensus_key c) ; + register_error_kind + `Temporary + ~id:"operation.invalid_drain.noop" + ~title:"Invalid drain delegate: noop" + ~description:"Cannot drain a delegate to itself." + ~pp:(fun ppf delegate -> + Format.fprintf + ppf + "The destination of a drain operation cannot be the delegate itself \ + (%a)." + Signature.Public_key_hash.pp + delegate) + Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) + (function Invalid_drain_delegate_noop c -> Some c | _ -> None) + (fun c -> Invalid_drain_delegate_noop c) ; + register_error_kind + `Temporary + ~id:"operation.unauthorized_key_for_drain" + ~title:"Unauthorized key for draining a delegate" + ~description:"Unauthorized key for draining a delegate." + ~pp:(fun ppf c -> + Format.fprintf + ppf + "The source key (%a) is not an active consensus key. It cannot be used \ + for draining a delegate." + Signature.Public_key_hash.pp + c) + Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) + (function Unauthorized_key_for_drain c -> Some c | _ -> None) + (fun c -> Unauthorized_key_for_drain c) ; + register_error_kind + `Temporary + ~id:"operation.invalid_drain.no_allocation_burn" + ~title:"Drain delegate without enough balance for allocation burn" + ~description:"Cannot drain without enough allocation burn." + ~pp:(fun ppf (delegate, destination) -> + Format.fprintf + ppf + "Cannot drain delegate %a to %a. The destination is not allocated and \ + there is not enough funds in the delegate account to pay for the \ + allocation burn." + Signature.Public_key_hash.pp + delegate + Signature.Public_key_hash.pp + destination) + Data_encoding.( + obj2 + (req "delegate" Signature.Public_key_hash.encoding) + (req "destination" Signature.Public_key_hash.encoding)) + (function + | Invalid_drain_delegate_no_allocation_burn {delegate; destination} -> + Some (delegate, destination) + | _ -> None) + (fun (delegate, destination) -> + Invalid_drain_delegate_no_allocation_burn {delegate; destination}) ; register_error_kind `Branch ~id:"contract.empty_transaction" @@ -617,19 +757,6 @@ let () = | _ -> None) (fun (kind, delegate1, delegate2) -> Inconsistent_denunciation {kind; delegate1; delegate2}) ; - register_error_kind - `Branch - ~id:"block.unrequired_denunciation" - ~title:"Unrequired denunciation" - ~description:"A denunciation is unrequired" - ~pp:(fun ppf _ -> - Format.fprintf - ppf - "A valid denunciation cannot be applied: the associated delegate has \ - already been denounced for this level.") - Data_encoding.unit - (function Unrequired_denunciation -> Some () | _ -> None) - (fun () -> Unrequired_denunciation) ; register_error_kind `Temporary ~id:"block.too_early_denunciation" @@ -794,7 +921,7 @@ let update_script_storage_and_ticket_balances ctxt ~self storage Ticket_accounting.update_ticket_balances ctxt ~self ~ticket_diffs operations let apply_delegation ~ctxt ~source ~delegate ~before_operation = - Delegate.set ctxt source delegate >|=? fun ctxt -> + Contract.Delegate.set ctxt source delegate >|=? fun ctxt -> (ctxt, Gas.consumed ~since:before_operation ~until:ctxt, []) type execution_arg = @@ -1063,7 +1190,7 @@ let apply_origination ~ctxt ~storage_type ~storage ~unparsed_code let contract = Contract.Originated contract_hash in (match delegate with | None -> return ctxt - | Some delegate -> Delegate.init ctxt contract delegate) + | Some delegate -> Contract.Delegate.init ctxt contract delegate) >>=? fun ctxt -> Token.transfer ctxt (`Contract source) (`Contract contract) credit >>=? fun (ctxt, balance_updates) -> @@ -1501,7 +1628,7 @@ let apply_external_manager_operation_content : Tez.(limit > max_limit) (Set_deposits_limit_too_high {limit; max_limit})) >>?= fun () -> - Delegate.registered ctxt source >>=? fun is_registered -> + Delegate.registered ctxt source >>= fun is_registered -> error_unless is_registered (Set_deposits_limit_on_unregistered_delegate source) @@ -1512,6 +1639,19 @@ let apply_external_manager_operation_content : Set_deposits_limit_result {consumed_gas = Gas.consumed ~since:before_operation ~until:ctxt}, [] ) + | Update_consensus_key (cycle, pk) -> + Delegate.registered ctxt source >>= fun is_registered -> + error_unless + is_registered + (Update_consensus_key_on_unregistered_delegate source) + >>?= fun () -> + Delegate.Consensus_key.register_update ctxt source cycle pk + >>=? fun ctxt -> + return + ( ctxt, + Update_consensus_key_result + {consumed_gas = Gas.consumed ~since:before_operation ~until:ctxt}, + [] ) | Tx_rollup_origination -> Tx_rollup.originate ctxt >>=? fun (ctxt, originated_tx_rollup) -> let result = @@ -1925,7 +2065,7 @@ let precheck_manager_contents (type kind) ctxt (op : kind Kind.manager contents) @@ (* See comment in the Transaction branch *) ( Script.force_decode_in_context ~consume_deserialization_gas ctxt value >|? fun (_value, ctxt) -> ctxt ) - | Delegation _ | Set_deposits_limit _ -> return ctxt + | Delegation _ | Set_deposits_limit _ | Update_consensus_key _ | Tx_rollup_origination -> assert_tx_rollup_feature_enabled ctxt >|=? fun () -> ctxt | Tx_rollup_submit_batch {content; _} -> @@ -2131,7 +2271,8 @@ let burn_manager_storage_fees : size_of_constant = payload.size_of_constant; global_address = payload.global_address; } ) - | Set_deposits_limit_result _ -> return (ctxt, storage_limit, smopr) + | Set_deposits_limit_result _ | Update_consensus_key_result _ -> + return (ctxt, storage_limit, smopr) | Tx_rollup_origination_result payload -> Fees.burn_tx_rollup_origination_fees ctxt ~storage_limit ~payer >|=? fun (ctxt, storage_limit, origination_bus) -> @@ -2232,7 +2373,8 @@ let burn_internal_storage_fees : ( ctxt, storage_limit, Register_global_constant_result {payload with balance_updates} ) - | Set_deposits_limit_result _ -> return (ctxt, storage_limit, smopr) + | Set_deposits_limit_result _ | Update_consensus_key_result _ -> + return (ctxt, storage_limit, smopr) | Tx_rollup_origination_result ({balance_updates; _} as payload) -> Fees.burn_tx_rollup_origination_fees ctxt ~storage_limit ~payer >>=? fun (ctxt, storage_limit, origination_bus) -> @@ -2386,7 +2528,7 @@ let skipped_operation_result : let rec mark_skipped : type kind. - payload_producer:Signature.Public_key_hash.t -> + payload_producer:Consensus_key.t -> Level.t -> kind Kind.manager prechecked_contents_list -> kind Kind.manager contents_result_list = @@ -2515,11 +2657,55 @@ let check_manager_signature ctxt chain_id (op : _ Kind.manager contents_list) find_manager_public_key ctxt op >>=? fun public_key -> Lwt.return (Operation.check_signature public_key chain_id raw_operation) +let check_drain ctxt + (Single (Drain_delegate {delegate; destination; consensus_key})) = + Delegate.registered ctxt delegate >>= fun is_registered -> + error_unless is_registered (Drain_delegate_on_unregistered_delegate delegate) + >>?= fun () -> + Delegate.Consensus_key.active_pubkey ctxt delegate >>=? fun active_pk -> + if not (Signature.Public_key_hash.equal active_pk.consensus_pkh consensus_key) + then + fail + (Invalid_drain_delegate_inactive_key + { + delegate; + consensus_key; + active_consensus_key = active_pk.consensus_pkh; + }) + else if Signature.Public_key_hash.equal active_pk.consensus_pkh delegate then + fail (Invalid_drain_delegate_no_consensus_key delegate) + else if Signature.Public_key_hash.equal destination delegate then + fail (Invalid_drain_delegate_noop delegate) + else return active_pk.consensus_pk + +let precheck_drain ctxt + (Single (Drain_delegate {delegate; destination; _}) as op) = + check_drain ctxt op >>=? fun _active_pk -> + Contract.allocated ctxt (Contract.Implicit destination) + >>= fun is_destination_allocated -> + (if is_destination_allocated then ok Tez.zero + else + let cost_per_byte = Constants.cost_per_byte ctxt in + let origination_size = Constants.origination_size ctxt in + Tez.(cost_per_byte *? Int64.of_int origination_size)) + >>?= fun allocation_burn -> + Contract.get_balance ctxt (Contract.Implicit delegate) >>=? fun balance -> + match Tez.(balance -? allocation_burn) with + | Error _ -> + fail (Invalid_drain_delegate_no_allocation_burn {delegate; destination}) + | Ok balance -> Lwt.return (Delegate.drain_fees balance) + +let check_drain_signature ctxt chain_id (Single (Drain_delegate {delegate; _})) + raw_operation = + Delegate.Consensus_key.active_pubkey ctxt delegate >>=? fun active_pk -> + Lwt.return + (Operation.check_signature active_pk.consensus_pk chain_id raw_operation) + let rec apply_manager_contents_list_rec : type kind. context -> Script_ir_translator.unparsing_mode -> - payload_producer:public_key_hash -> + payload_producer:Consensus_key.t -> Chain_id.t -> kind Kind.manager prechecked_contents_list -> (success_or_failure * kind Kind.manager contents_result_list) Lwt.t = @@ -2658,7 +2844,8 @@ let record_operation (type kind) ctxt (operation : kind operation) : context = | Single ( Failing_noop _ | Proposals _ | Ballot _ | Seed_nonce_revelation _ | Double_endorsement_evidence _ | Double_preendorsement_evidence _ - | Double_baking_evidence _ | Activate_account _ | Manager_operation _ ) + | Double_baking_evidence _ | Activate_account _ | Drain_delegate _ + | Manager_operation _ ) | Cons (Manager_operation _, _) -> let hash = Operation.hash operation in record_non_consensus_operation_hash ctxt hash @@ -2838,7 +3025,7 @@ let validate_consensus_contents (type kind) ctxt chain_id (operation_kind : kind consensus_operation_type) (operation : kind operation) (apply_mode : apply_mode) (content : consensus_content) : - (context * public_key_hash * int) tzresult Lwt.t = + (context * Consensus_key.t * int) tzresult Lwt.t = let current_level = Level.current ctxt in let proposal_level = get_predecessor_level apply_mode in let slot_map = @@ -2864,14 +3051,15 @@ let validate_consensus_contents (type kind) ctxt chain_id >>?= fun () -> match Slot.Map.find content.slot slot_map with | None -> fail Wrong_slot_used_for_consensus_operation - | Some (delegate_pk, delegate_pkh, voting_power) -> - Delegate.frozen_deposits ctxt delegate_pkh >>=? fun frozen_deposits -> + | Some (consensus_pk, voting_power) -> + Delegate.frozen_deposits ctxt consensus_pk.delegate + >>=? fun frozen_deposits -> fail_unless Tez.(frozen_deposits.current_amount > zero) - (Zero_frozen_deposits delegate_pkh) + (Zero_frozen_deposits consensus_pk.delegate) >>=? fun () -> - Operation.check_signature delegate_pk chain_id operation >>?= fun () -> - return (ctxt, delegate_pkh, voting_power) + Operation.check_signature consensus_pk.consensus_pk chain_id operation + >>?= fun () -> return (ctxt, Consensus_key.pkh consensus_pk, voting_power) let apply_manager_contents_list ctxt mode ~payload_producer chain_id prechecked_contents_list = @@ -2903,24 +3091,18 @@ let check_denunciation_age ctxt kind given_level = {kind; level = given_level; last_cycle = last_slashable_cycle}) let punish_delegate ctxt delegate level mistake mk_result ~payload_producer = - let already_slashed, punish = + let punish = match mistake with - | `Double_baking -> - ( Delegate.already_slashed_for_double_baking, - Delegate.punish_double_baking ) - | `Double_endorsing -> - ( Delegate.already_slashed_for_double_endorsing, - Delegate.punish_double_endorsing ) + | `Double_baking -> Delegate.punish_double_baking + | `Double_endorsing -> Delegate.punish_double_endorsing in - already_slashed ctxt delegate level >>=? fun slashed -> - fail_when slashed Unrequired_denunciation >>=? fun () -> punish ctxt delegate level >>=? fun (ctxt, burned, punish_balance_updates) -> (match Tez.(burned /? 2L) with | Ok reward -> Token.transfer ctxt `Double_signing_evidence_rewards - (`Contract (Contract.Implicit payload_producer)) + (`Contract (Contract.Implicit payload_producer.Consensus_key.delegate)) reward | Error _ -> (* reward is Tez.zero *) return (ctxt, [])) >|=? fun (ctxt, reward_balance_updates) -> @@ -2964,19 +3146,27 @@ let punish_double_endorsement_or_preendorsement (type kind) ctxt ~chain_id let level = Level.from_raw ctxt e1.level in check_denunciation_age ctxt kind level.level >>=? fun () -> Stake_distribution.slot_owner ctxt level e1.slot - >>=? fun (ctxt, (delegate1_pk, delegate1)) -> + >>=? fun (ctxt, consensus_pk1) -> Stake_distribution.slot_owner ctxt level e2.slot - >>=? fun (ctxt, (_delegate2_pk, delegate2)) -> + >>=? fun (ctxt, consensus_pk2) -> fail_unless - (Signature.Public_key_hash.equal delegate1 delegate2) - (Inconsistent_denunciation {kind; delegate1; delegate2}) + (Signature.Public_key_hash.equal + consensus_pk1.delegate + consensus_pk2.delegate) + (Inconsistent_denunciation + { + kind; + delegate1 = consensus_pk1.delegate; + delegate2 = consensus_pk2.delegate; + }) >>=? fun () -> - let delegate_pk, delegate = (delegate1_pk, delegate1) in - Operation.check_signature delegate_pk chain_id op1 >>?= fun () -> - Operation.check_signature delegate_pk chain_id op2 >>?= fun () -> + Operation.check_signature consensus_pk1.consensus_pk chain_id op1 + >>?= fun () -> + Operation.check_signature consensus_pk2.consensus_pk chain_id op2 + >>?= fun () -> punish_delegate ctxt - delegate + consensus_pk1.delegate level `Double_endorsing mk_result @@ -3006,20 +3196,26 @@ let punish_double_baking ctxt chain_id bh1 bh2 ~payload_producer = let committee_size = Constants.consensus_committee_size ctxt in Round.to_slot round1 ~committee_size >>?= fun slot1 -> Stake_distribution.slot_owner ctxt level slot1 - >>=? fun (ctxt, (delegate1_pk, delegate1)) -> + >>=? fun (ctxt, consensus_pk1) -> Round.to_slot round2 ~committee_size >>?= fun slot2 -> Stake_distribution.slot_owner ctxt level slot2 - >>=? fun (ctxt, (_delegate2_pk, delegate2)) -> + >>=? fun (ctxt, consensus_pk2) -> fail_unless - Signature.Public_key_hash.(delegate1 = delegate2) - (Inconsistent_denunciation {kind = Block; delegate1; delegate2}) + Signature.Public_key_hash.(consensus_pk1.delegate = consensus_pk2.delegate) + (Inconsistent_denunciation + { + kind = Block; + delegate1 = consensus_pk1.delegate; + delegate2 = consensus_pk2.delegate; + }) >>=? fun () -> - let delegate_pk, delegate = (delegate1_pk, delegate1) in - Block_header.check_signature bh1 chain_id delegate_pk >>?= fun () -> - Block_header.check_signature bh2 chain_id delegate_pk >>?= fun () -> + Block_header.check_signature bh1 chain_id consensus_pk1.consensus_pk + >>?= fun () -> + Block_header.check_signature bh2 chain_id consensus_pk2.consensus_pk + >>?= fun () -> punish_delegate ctxt - delegate + consensus_pk1.delegate level `Double_baking ~payload_producer @@ -3046,16 +3242,19 @@ let validate_grand_parent_endorsement ctxt chain_id | Single (Endorsement e) -> let level = Level.from_raw ctxt e.level in Stake_distribution.slot_owner ctxt level e.slot - >>=? fun (ctxt, (delegate_pk, pkh)) -> - Operation.check_signature delegate_pk chain_id op >>?= fun () -> - Consensus.record_grand_parent_endorsement ctxt pkh >>?= fun ctxt -> + >>=? fun (ctxt, consensus_pk) -> + Operation.check_signature consensus_pk.consensus_pk chain_id op + >>?= fun () -> + Consensus.record_grand_parent_endorsement ctxt consensus_pk.delegate + >>?= fun ctxt -> return ( ctxt, Single_result (Endorsement_result { balance_updates = []; - delegate = pkh; + delegate = consensus_pk.delegate; + consensus_key = consensus_pk.consensus_pkh; endorsement_power = 0 (* dummy endorsement power: this will never be used *); }) ) @@ -3078,7 +3277,7 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) mode operation apply_mode consensus_content - >>=? fun (ctxt, delegate, voting_power) -> + >>=? fun (ctxt, {delegate; consensus_pkh}, voting_power) -> Consensus.record_preendorsement ctxt ~initial_slot:consensus_content.slot @@ -3092,6 +3291,7 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) mode { balance_updates = []; delegate; + consensus_key = consensus_pkh; preendorsement_power = voting_power; }) ) | Single (Endorsement consensus_content) -> ( @@ -3113,7 +3313,7 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) mode operation apply_mode consensus_content - >>=? fun (ctxt, delegate, voting_power) -> + >>=? fun (ctxt, {delegate; consensus_pkh}, voting_power) -> Consensus.record_endorsement ctxt ~initial_slot:consensus_content.slot @@ -3126,13 +3326,16 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) mode { balance_updates = []; delegate; + consensus_key = consensus_pkh; endorsement_power = voting_power; }) )) | Single (Seed_nonce_revelation {level; nonce}) -> let level = Level.from_raw ctxt level in Nonce.reveal ctxt level nonce >>=? fun ctxt -> let tip = Constants.seed_nonce_revelation_tip ctxt in - let contract = Contract.Implicit payload_producer in + let contract = + Contract.Implicit payload_producer.Consensus_key.delegate + in Token.transfer ctxt `Revelation_rewards (`Contract contract) tip >|=? fun (ctxt, balance_updates) -> (ctxt, Single_result (Seed_nonce_revelation_result balance_updates)) @@ -3167,7 +3370,7 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) mode >>=? fun (ctxt, bupds) -> return (ctxt, Single_result (Activate_account_result bupds)) | Single (Proposals {source; period; proposals}) -> - Delegate.pubkey ctxt source >>=? fun delegate -> + Delegate.manager_pubkey ctxt source >>=? fun delegate -> Operation.check_signature delegate chain_id operation >>?= fun () -> Voting_period.get_current ctxt >>=? fun {index = current_period; _} -> error_unless @@ -3177,7 +3380,7 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) mode Amendment.record_proposals ctxt source proposals >|=? fun ctxt -> (ctxt, Single_result Proposals_result) | Single (Ballot {source; period; proposal; ballot}) -> - Delegate.pubkey ctxt source >>=? fun delegate -> + Delegate.manager_pubkey ctxt source >>=? fun delegate -> Operation.check_signature delegate chain_id operation >>?= fun () -> Voting_period.get_current ctxt >>=? fun {index = current_period; _} -> error_unless @@ -3186,6 +3389,26 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) mode >>?= fun () -> Amendment.record_ballot ctxt source proposal ballot >|=? fun ctxt -> (ctxt, Single_result Ballot_result) + | Single (Drain_delegate {delegate; destination; consensus_key = _}) as op -> + check_drain ctxt op >>=? fun active_pk -> + Operation.check_signature active_pk chain_id operation >>?= fun () -> + Delegate.drain ctxt ~delegate ~destination + >>=? fun ( ctxt, + allocated_destination_contract, + fees, + drain_balance_updates ) -> + Token.transfer + ctxt + (`Delegate_balance delegate) + (`Contract (Contract.Implicit payload_producer.Consensus_key.delegate)) + fees + >>=? fun (ctxt, fees_balance_updates) -> + let balance_updates = drain_balance_updates @ fees_balance_updates in + return + ( ctxt, + Single_result + (Drain_delegate_result + {balance_updates; allocated_destination_contract}) ) | Single (Failing_noop _) -> (* Failing_noop _ always fails *) fail Failing_noop_error @@ -3239,8 +3462,7 @@ let may_start_new_cycle ctxt = match Level.dawn_of_a_new_cycle ctxt with | None -> return (ctxt, [], []) | Some last_cycle -> - Seed.cycle_end ctxt last_cycle >>=? fun (ctxt, unrevealed) -> - Delegate.cycle_end ctxt last_cycle unrevealed + Delegate.cycle_end ctxt last_cycle >>=? fun (ctxt, balance_updates, deactivated) -> Bootstrap.cycle_end ctxt last_cycle >|=? fun ctxt -> (ctxt, balance_updates, deactivated) @@ -3427,8 +3649,8 @@ let apply_liquidity_baking_subsidy ctxt ~toggle_vote = type 'a full_construction = { ctxt : t; protocol_data : 'a; - payload_producer : Signature.public_key_hash; - block_producer : Signature.public_key_hash; + payload_producer : Consensus_key.t; + block_producer : Consensus_key.t; round : Round.t; implicit_operations_results : packed_successful_manager_operation_result list; liquidity_baking_toggle_ema : Liquidity_baking.Toggle_EMA.t; @@ -3447,17 +3669,18 @@ let begin_full_construction ctxt ~predecessor_timestamp ~predecessor_level >>?= fun () -> let current_level = Level.current ctxt in Stake_distribution.baking_rights_owner ctxt current_level ~round - >>=? fun (ctxt, _slot, (_block_producer_pk, block_producer)) -> - Delegate.frozen_deposits ctxt block_producer >>=? fun frozen_deposits -> + >>=? fun (ctxt, _slot, block_producer) -> + Delegate.frozen_deposits ctxt block_producer.delegate + >>=? fun frozen_deposits -> fail_unless Tez.(frozen_deposits.current_amount > zero) - (Zero_frozen_deposits block_producer) + (Zero_frozen_deposits block_producer.delegate) >>=? fun () -> Stake_distribution.baking_rights_owner ctxt current_level ~round:protocol_data.Block_header.payload_round - >>=? fun (ctxt, _slot, (_payload_producer_pk, payload_producer)) -> + >>=? fun (ctxt, _slot, payload_producer) -> init_allowed_consensus_operations ctxt ~endorsement_level:predecessor_level @@ -3471,8 +3694,8 @@ let begin_full_construction ctxt ~predecessor_timestamp ~predecessor_level { ctxt; protocol_data; - payload_producer; - block_producer; + payload_producer = Consensus_key.pkh payload_producer; + block_producer = Consensus_key.pkh block_producer; round; implicit_operations_results = liquidity_baking_operations_results; liquidity_baking_toggle_ema; @@ -3494,7 +3717,7 @@ let begin_application ctxt chain_id (block_header : Block_header.t) fitness let round = Fitness.round fitness in let current_level = Level.current ctxt in Stake_distribution.baking_rights_owner ctxt current_level ~round - >>=? fun (ctxt, _slot, (block_producer_pk, block_producer)) -> + >>=? fun (ctxt, _slot, block_producer) -> let timestamp = block_header.shell.timestamp in Block_header.begin_validate_block_header ~block_header @@ -3503,21 +3726,22 @@ let begin_application ctxt chain_id (block_header : Block_header.t) fitness ~predecessor_round ~fitness ~timestamp - ~delegate_pk:block_producer_pk + ~delegate_pk:block_producer.consensus_pk ~round_durations:(Constants.round_durations ctxt) ~proof_of_work_threshold:(Constants.proof_of_work_threshold ctxt) ~expected_commitment:current_level.expected_commitment >>?= fun () -> - Delegate.frozen_deposits ctxt block_producer >>=? fun frozen_deposits -> + Delegate.frozen_deposits ctxt block_producer.delegate + >>=? fun frozen_deposits -> fail_unless Tez.(frozen_deposits.current_amount > zero) - (Zero_frozen_deposits block_producer) + (Zero_frozen_deposits block_producer.delegate) >>=? fun () -> Stake_distribution.baking_rights_owner ctxt current_level ~round:block_header.protocol_data.contents.payload_round - >>=? fun (ctxt, _slot, (payload_producer_pk, _payload_producer)) -> + >>=? fun (ctxt, _slot, payload_producer) -> init_allowed_consensus_operations ctxt ~endorsement_level:predecessor_level @@ -3532,8 +3756,8 @@ let begin_application ctxt chain_id (block_header : Block_header.t) fitness liquidity_baking_operations_results, liquidity_baking_toggle_ema ) -> ( ctxt, - payload_producer_pk, - block_producer, + Consensus_key.pkh payload_producer, + Consensus_key.pkh block_producer, liquidity_baking_operations_results, liquidity_baking_toggle_ema ) @@ -3620,7 +3844,7 @@ let finalize_application_check_validity ctxt (mode : finalize_application_mode) let record_endorsing_participation ctxt = let validators = Consensus.allowed_endorsements ctxt in Slot.Map.fold_es - (fun initial_slot (_delegate_pk, delegate, power) ctxt -> + (fun initial_slot ((consensus_pk : Consensus_key.pk), power) ctxt -> let participation = if Slot.Set.mem initial_slot (Consensus.endorsements_seen ctxt) then Delegate.Participated @@ -3628,15 +3852,16 @@ let record_endorsing_participation ctxt = in Delegate.record_endorsing_participation ctxt - ~delegate + ~delegate:consensus_pk.delegate ~participation ~endorsing_power:power) validators ctxt let finalize_application ctxt (mode : finalize_application_mode) protocol_data - ~payload_producer ~block_producer liquidity_baking_toggle_ema - implicit_operations_results ~round ~predecessor ~migration_balance_updates = + ~(payload_producer : Consensus_key.t) ~(block_producer : Consensus_key.t) + liquidity_baking_toggle_ema implicit_operations_results ~round ~predecessor + ~migration_balance_updates = (* Then we finalize the consensus. *) let level = Level.current ctxt in let block_endorsing_power = Consensus.current_endorsement_power ctxt in @@ -3671,7 +3896,7 @@ let finalize_application ctxt (mode : finalize_application_mode) protocol_data (match protocol_data.Block_header.seed_nonce_hash with | None -> return ctxt | Some nonce_hash -> - Nonce.record_hash ctxt {nonce_hash; delegate = block_producer}) + Nonce.record_hash ctxt {nonce_hash; delegate = block_producer.delegate}) >>=? fun ctxt -> (if required_endorsements then record_endorsing_participation ctxt >>=? fun ctxt -> @@ -3682,8 +3907,8 @@ let finalize_application ctxt (mode : finalize_application_mode) protocol_data let baking_reward = Constants.baking_reward_fixed_portion ctxt in Delegate.record_baking_activity_and_pay_rewards_and_fees ctxt - ~payload_producer - ~block_producer + ~payload_producer:payload_producer.delegate + ~block_producer:block_producer.delegate ~baking_reward ~reward_bonus >>=? fun (ctxt, baking_receipts) -> diff --git a/src/proto_alpha/lib_protocol/apply.mli b/src/proto_alpha/lib_protocol/apply.mli index 3e3a0688450ab544bfa5ac02894a5bd7011eefd7..43403c38a8989b3ec72bda75691a11ac65f8ab45 100644 --- a/src/proto_alpha/lib_protocol/apply.mli +++ b/src/proto_alpha/lib_protocol/apply.mli @@ -58,8 +58,8 @@ val begin_partial_construction : type 'a full_construction = { ctxt : context; protocol_data : 'a; - payload_producer : Signature.public_key_hash; - block_producer : Signature.public_key_hash; + payload_producer : Consensus_key.t; + block_producer : Consensus_key.t; round : Round.t; implicit_operations_results : packed_successful_manager_operation_result list; liquidity_baking_toggle_ema : Liquidity_baking.Toggle_EMA.t; @@ -83,8 +83,8 @@ val begin_application : predecessor_level:Level.t -> predecessor_round:Round.t -> (t - * Signature.public_key - * Signature.public_key_hash + * Consensus_key.t + * Consensus_key.t * packed_successful_manager_operation_result list * Liquidity_baking.Toggle_EMA.t) tzresult @@ -118,7 +118,7 @@ val apply_operation : Chain_id.t -> apply_mode -> Script_ir_translator.unparsing_mode -> - payload_producer:public_key_hash -> + payload_producer:Consensus_key.t -> Operation_list_hash.elt -> 'a operation -> (context * 'a operation_metadata, error trace) result Lwt.t @@ -134,8 +134,8 @@ val finalize_application : context -> finalize_application_mode -> Block_header.contents -> - payload_producer:public_key_hash -> - block_producer:public_key_hash -> + payload_producer:Consensus_key.t -> + block_producer:Consensus_key.t -> Liquidity_baking.Toggle_EMA.t -> packed_successful_manager_operation_result list -> round:Round.t -> @@ -146,7 +146,7 @@ val finalize_application : val apply_manager_contents_list : context -> Script_ir_translator.unparsing_mode -> - payload_producer:public_key_hash -> + payload_producer:Consensus_key.t -> Chain_id.t -> 'a Kind.manager prechecked_contents_list -> (context * 'a Kind.manager contents_result_list) Lwt.t @@ -156,7 +156,7 @@ val apply_contents_list : Chain_id.t -> apply_mode -> Script_ir_translator.unparsing_mode -> - payload_producer:public_key_hash -> + payload_producer:Consensus_key.t -> 'kind operation -> 'kind contents_list -> (context * 'kind contents_result_list) tzresult Lwt.t @@ -210,4 +210,16 @@ val check_manager_signature : Chain_id.t -> 'a Kind.manager contents_list -> 'b operation -> - (unit, error trace) result Lwt.t + unit tzresult Lwt.t + +(** See [Main.precheck_drain] *) +val precheck_drain : + context -> Kind.drain_delegate contents_list -> Tez.t tzresult Lwt.t + +(** See [Main.check_drain_signature] *) +val check_drain_signature : + context -> + Chain_id.t -> + Kind.drain_delegate contents_list -> + Kind.drain_delegate operation -> + unit tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/apply_results.ml b/src/proto_alpha/lib_protocol/apply_results.ml index 0eeb36942f7d5b7ea3b903485dcaa73e80105199..fe097547cd6fa56696cca65fe6dd12fe68bbf53c 100644 --- a/src/proto_alpha/lib_protocol/apply_results.ml +++ b/src/proto_alpha/lib_protocol/apply_results.ml @@ -162,6 +162,10 @@ type _ successful_manager_operation_result = consumed_gas : Gas.Arith.fp; } -> Kind.set_deposits_limit successful_manager_operation_result + | Update_consensus_key_result : { + consumed_gas : Gas.Arith.fp; + } + -> Kind.update_consensus_key successful_manager_operation_result | Tx_rollup_origination_result : { balance_updates : Receipt.balance_updates; consumed_gas : Gas.Arith.fp; @@ -613,6 +617,26 @@ module Manager_result = struct assert (Gas.Arith.(equal (ceil consumed_milligas) consumed_gas)) ; Delegation_result {consumed_gas = consumed_milligas}) + let update_consensus_key_case = + make + ~op_case:Operation.Encoding.Manager_operations.update_consensus_key_case + ~encoding: + Data_encoding.( + obj2 + (dft "consumed_gas" Gas.Arith.n_integral_encoding Gas.Arith.zero) + (dft "consumed_milligas" Gas.Arith.n_fp_encoding Gas.Arith.zero)) + ~select:(function + | Successful_manager_result (Update_consensus_key_result _ as op) -> + Some op + | _ -> None) + ~kind:Kind.Update_consensus_key_manager_kind + ~proj:(function[@coq_match_with_default] + | Update_consensus_key_result {consumed_gas} -> + (Gas.Arith.ceil consumed_gas, consumed_gas)) + ~inj:(fun (consumed_gas, consumed_milligas) -> + assert (Gas.Arith.(equal (ceil consumed_milligas) consumed_gas)) ; + Update_consensus_key_result {consumed_gas = consumed_milligas}) + let set_deposits_limit_case = make ~op_case:Operation.Encoding.Manager_operations.set_deposits_limit_case @@ -1429,6 +1453,7 @@ let successful_manager_operation_result_encoding : make Manager_result.transaction_case; make Manager_result.origination_case; make Manager_result.delegation_case; + make Manager_result.update_consensus_key_case; make Manager_result.set_deposits_limit_case; make Manager_result.sc_rollup_originate_case; ] @@ -1436,13 +1461,15 @@ let successful_manager_operation_result_encoding : type 'kind contents_result = | Preendorsement_result : { balance_updates : Receipt.balance_updates; - delegate : Signature.Public_key_hash.t; + delegate : Signature.public_key_hash; + consensus_key : Signature.public_key_hash; preendorsement_power : int; } -> Kind.preendorsement contents_result | Endorsement_result : { balance_updates : Receipt.balance_updates; - delegate : Signature.Public_key_hash.t; + delegate : Signature.public_key_hash; + consensus_key : Signature.public_key_hash; endorsement_power : int; } -> Kind.endorsement contents_result @@ -1463,6 +1490,11 @@ type 'kind contents_result = -> Kind.activate_account contents_result | Proposals_result : Kind.proposals contents_result | Ballot_result : Kind.ballot contents_result + | Drain_delegate_result : { + balance_updates : Receipt.balance_updates; + allocated_destination_contract : bool; + } + -> Kind.drain_delegate contents_result | Manager_operation_result : { balance_updates : Receipt.balance_updates; operation_result : 'kind manager_operation_result; @@ -1492,9 +1524,10 @@ let equal_manager_kind : | Kind.Origination_manager_kind, _ -> None | Kind.Delegation_manager_kind, Kind.Delegation_manager_kind -> Some Eq | Kind.Delegation_manager_kind, _ -> None - | ( Kind.Register_global_constant_manager_kind, - Kind.Register_global_constant_manager_kind ) -> + | ( Kind.Update_consensus_key_manager_kind, + Kind.Update_consensus_key_manager_kind ) -> Some Eq + | Kind.Update_consensus_key_manager_kind, _ -> None | Kind.Register_global_constant_manager_kind, _ -> None | Kind.Set_deposits_limit_manager_kind, Kind.Set_deposits_limit_manager_kind -> @@ -1587,10 +1620,11 @@ module Encoding = struct { op_case = Operation.Encoding.preendorsement_case; encoding = - obj3 + obj4 (dft "balance_updates" Receipt.balance_updates_encoding []) (req "delegate" Signature.Public_key_hash.encoding) - (req "preendorsement_power" int31); + (req "preendorsement_power" int31) + (req "consensus_key" Signature.Public_key_hash.encoding); select = (function | Contents_result (Preendorsement_result _ as op) -> Some op @@ -1602,12 +1636,13 @@ module Encoding = struct proj = (function | Preendorsement_result - {balance_updates; delegate; preendorsement_power} -> - (balance_updates, delegate, preendorsement_power)); + {balance_updates; delegate; consensus_key; preendorsement_power} + -> + (balance_updates, delegate, preendorsement_power, consensus_key)); inj = - (fun (balance_updates, delegate, preendorsement_power) -> + (fun (balance_updates, delegate, preendorsement_power, consensus_key) -> Preendorsement_result - {balance_updates; delegate; preendorsement_power}); + {balance_updates; delegate; consensus_key; preendorsement_power}); } let[@coq_axiom_with_reason "gadt"] endorsement_case = @@ -1615,10 +1650,11 @@ module Encoding = struct { op_case = Operation.Encoding.endorsement_case; encoding = - obj3 + obj4 (dft "balance_updates" Receipt.balance_updates_encoding []) (req "delegate" Signature.Public_key_hash.encoding) - (req "endorsement_power" int31); + (req "endorsement_power" int31) + (req "consensus_key" Signature.Public_key_hash.encoding); select = (function | Contents_result (Endorsement_result _ as op) -> Some op | _ -> None); @@ -1628,11 +1664,13 @@ module Encoding = struct | _ -> None); proj = (function - | Endorsement_result {balance_updates; delegate; endorsement_power} -> - (balance_updates, delegate, endorsement_power)); + | Endorsement_result + {balance_updates; delegate; consensus_key; endorsement_power} -> + (balance_updates, delegate, endorsement_power, consensus_key)); inj = - (fun (balance_updates, delegate, endorsement_power) -> - Endorsement_result {balance_updates; delegate; endorsement_power}); + (fun (balance_updates, delegate, endorsement_power, consensus_key) -> + Endorsement_result + {balance_updates; delegate; consensus_key; endorsement_power}); } let[@coq_axiom_with_reason "gadt"] seed_nonce_revelation_case = @@ -1765,6 +1803,34 @@ module Encoding = struct inj = (fun () -> Ballot_result); } + let drain_delegate_case = + Case + { + op_case = Operation.Encoding.drain_delegate_case; + encoding = + Data_encoding.( + obj2 + (dft "balance_updates" Receipt.balance_updates_encoding []) + (dft "allocated_destination_contract" bool false)); + select = + (function + | Contents_result (Drain_delegate_result _ as op) -> Some op + | _ -> None); + mselect = + (function + | Contents_and_result ((Drain_delegate _ as op), res) -> Some (op, res) + | _ -> None); + proj = + (function + | Drain_delegate_result + {balance_updates; allocated_destination_contract} -> + (balance_updates, allocated_destination_contract)); + inj = + (fun (balance_updates, allocated_destination_contract) -> + Drain_delegate_result + {balance_updates; allocated_destination_contract}); + } + let[@coq_axiom_with_reason "gadt"] make_manager_case (type kind) (Operation.Encoding.Case op_case : kind Kind.manager Operation.Encoding.case) @@ -1826,6 +1892,7 @@ module Encoding = struct | Contents_result (Double_preendorsement_evidence_result _) -> None | Contents_result (Double_baking_evidence_result _) -> None | Contents_result (Activate_account_result _) -> None + | Contents_result (Drain_delegate_result _) -> None | Contents_result Proposals_result -> None); mselect; proj = @@ -1886,6 +1953,17 @@ module Encoding = struct Some (op, res) | _ -> None) + let[@coq_axiom_with_reason "gadt"] update_consensus_key_case = + make_manager_case + Operation.Encoding.update_consensus_key_case + Manager_result.update_consensus_key_case + (function + | Contents_and_result + ( (Manager_operation {operation = Update_consensus_key _; _} as op), + res ) -> + Some (op, res) + | _ -> None) + let[@coq_axiom_with_reason "gadt"] register_global_constant_case = make_manager_case Operation.Encoding.register_global_constant_case @@ -2117,12 +2195,14 @@ let contents_result_encoding = make activate_account_case; make proposals_case; make ballot_case; + make drain_delegate_case; make reveal_case; make transaction_case; make origination_case; make delegation_case; make register_global_constant_case; make set_deposits_limit_case; + make update_consensus_key_case; make tx_rollup_origination_case; make tx_rollup_submit_batch_case; make tx_rollup_commit_case; @@ -2180,6 +2260,8 @@ let contents_and_result_encoding = make delegation_case; make register_global_constant_case; make set_deposits_limit_case; + make update_consensus_key_case; + make drain_delegate_case; make tx_rollup_origination_case; make tx_rollup_submit_batch_case; make tx_rollup_commit_case; @@ -2321,6 +2403,8 @@ let kind_equal : | Proposals _, _ -> None | Ballot _, Ballot_result -> Some Eq | Ballot _, _ -> None + | Drain_delegate _, Drain_delegate_result _ -> Some Eq + | Drain_delegate _, _ -> None | Failing_noop _, _ -> (* the Failing_noop operation always fails and can't have result *) None @@ -2417,6 +2501,32 @@ let kind_equal : } ) -> Some Eq | Manager_operation {operation = Delegation _; _}, _ -> None + | ( Manager_operation {operation = Update_consensus_key _; _}, + Manager_operation_result + {operation_result = Applied (Update_consensus_key_result _); _} ) -> + Some Eq + | ( Manager_operation {operation = Update_consensus_key _; _}, + Manager_operation_result + {operation_result = Backtracked (Update_consensus_key_result _, _); _} ) + -> + Some Eq + | ( Manager_operation {operation = Update_consensus_key _; _}, + Manager_operation_result + { + operation_result = + Failed (Alpha_context.Kind.Update_consensus_key_manager_kind, _); + _; + } ) -> + Some Eq + | ( Manager_operation {operation = Update_consensus_key _; _}, + Manager_operation_result + { + operation_result = + Skipped Alpha_context.Kind.Update_consensus_key_manager_kind; + _; + } ) -> + Some Eq + | Manager_operation {operation = Update_consensus_key _; _}, _ -> None | ( Manager_operation {operation = Register_global_constant _; _}, Manager_operation_result {operation_result = Applied (Register_global_constant_result _); _} ) -> @@ -3001,8 +3111,8 @@ let operation_data_and_metadata_encoding = ] type block_metadata = { - proposer : Signature.Public_key_hash.t; - baker : Signature.Public_key_hash.t; + proposer : Consensus_key.t; + baker : Consensus_key.t; level_info : Level.t; voting_period_info : Voting_period.info; nonce_hash : Nonce_hash.t option; @@ -3018,8 +3128,9 @@ let block_metadata_encoding = def "block_header.alpha.metadata" @@ conv (fun { - proposer; - baker; + proposer = + {delegate = proposer; consensus_pkh = proposer_active_key}; + baker = {delegate = baker; consensus_pkh = baker_active_key}; level_info; voting_period_info; nonce_hash; @@ -3039,7 +3150,7 @@ let block_metadata_encoding = balance_updates, liquidity_baking_toggle_ema, implicit_operations_results ), - consumed_gas )) + (proposer_active_key, baker_active_key, consumed_gas) )) (fun ( ( proposer, baker, level_info, @@ -3050,10 +3161,10 @@ let block_metadata_encoding = balance_updates, liquidity_baking_toggle_ema, implicit_operations_results ), - _consumed_millgas ) -> + (proposer_active_key, baker_active_key, _consumed_millgas) ) -> { - proposer; - baker; + proposer = {delegate = proposer; consensus_pkh = proposer_active_key}; + baker = {delegate = baker; consensus_pkh = baker_active_key}; level_info; voting_period_info; nonce_hash; @@ -3079,7 +3190,10 @@ let block_metadata_encoding = (req "implicit_operations_results" (list successful_manager_operation_result_encoding))) - (obj1 (req "consumed_milligas" Gas.Arith.n_fp_encoding))) + (obj3 + (req "proposer_consensus_key" Signature.Public_key_hash.encoding) + (req "baker_consensus_key" Signature.Public_key_hash.encoding) + (req "consumed_milligas" Gas.Arith.n_fp_encoding))) type precheck_result = { consumed_gas : Gas.Arith.fp; diff --git a/src/proto_alpha/lib_protocol/apply_results.mli b/src/proto_alpha/lib_protocol/apply_results.mli index 69822f79d6dd2c99e4e02f73e6f98139edd1204e..71a57aabb3f3e841ae05c3968d16a6a16c9272f6 100644 --- a/src/proto_alpha/lib_protocol/apply_results.mli +++ b/src/proto_alpha/lib_protocol/apply_results.mli @@ -89,13 +89,15 @@ and packed_contents_result_list = and 'kind contents_result = | Preendorsement_result : { balance_updates : Receipt.balance_updates; - delegate : Signature.Public_key_hash.t; + delegate : Signature.public_key_hash; + consensus_key : Signature.public_key_hash; preendorsement_power : int; } -> Kind.preendorsement contents_result | Endorsement_result : { balance_updates : Receipt.balance_updates; - delegate : Signature.Public_key_hash.t; + delegate : Signature.public_key_hash; + consensus_key : Signature.public_key_hash; endorsement_power : int; } -> Kind.endorsement contents_result @@ -116,6 +118,11 @@ and 'kind contents_result = -> Kind.activate_account contents_result | Proposals_result : Kind.proposals contents_result | Ballot_result : Kind.ballot contents_result + | Drain_delegate_result : { + balance_updates : Receipt.balance_updates; + allocated_destination_contract : bool; + } + -> Kind.drain_delegate contents_result | Manager_operation_result : { balance_updates : Receipt.balance_updates; operation_result : 'kind manager_operation_result; @@ -205,6 +212,10 @@ and _ successful_manager_operation_result = consumed_gas : Gas.Arith.fp; } -> Kind.set_deposits_limit successful_manager_operation_result + | Update_consensus_key_result : { + consumed_gas : Gas.Arith.fp; + } + -> Kind.update_consensus_key successful_manager_operation_result | Tx_rollup_origination_result : { balance_updates : Receipt.balance_updates; consumed_gas : Gas.Arith.fp; @@ -362,8 +373,8 @@ val kind_equal_list : ('kind, 'kind2) eq option type block_metadata = { - proposer : Signature.Public_key_hash.t; - baker : Signature.Public_key_hash.t; + proposer : Consensus_key.t; + baker : Consensus_key.t; level_info : Level.t; voting_period_info : Voting_period.info; nonce_hash : Nonce_hash.t option; diff --git a/src/proto_alpha/lib_protocol/baking.ml b/src/proto_alpha/lib_protocol/baking.ml index 00bc864d91028aaa0517cd4f0246cf39b2c7a37f..4cf700d66586ea345403af34ff41d27cfde4b23e 100644 --- a/src/proto_alpha/lib_protocol/baking.ml +++ b/src/proto_alpha/lib_protocol/baking.ml @@ -72,12 +72,17 @@ let bonus_baking_reward ctxt ~endorsing_power = let baking_rights c level = let rec f c round = Stake_distribution.baking_rights_owner c level ~round - >>=? fun (c, _slot, (delegate, _)) -> - return (LCons (delegate, fun () -> f c (Round.succ round))) + >>=? fun (c, _slot, consensus_pk) -> + return + (LCons (Consensus_key.pkh consensus_pk, fun () -> f c (Round.succ round))) in f c Round.zero -type ordered_slots = Slot.t list +type ordered_slots = { + delegate : Signature.public_key_hash; + consensus_key : Signature.public_key_hash; + slots : Slot.t list; +} (* Slots returned by this function are assumed by consumers to be in increasing order, hence the use of [Slot.Range.rev_fold_es]. *) @@ -86,11 +91,20 @@ let endorsing_rights (ctxt : t) level = Slot.Range.create ~min:0 ~count:consensus_committee_size >>?= fun slots -> Slot.Range.rev_fold_es (fun (ctxt, map) slot -> - Stake_distribution.slot_owner ctxt level slot >>=? fun (ctxt, (_, pkh)) -> + Stake_distribution.slot_owner ctxt level slot + >>=? fun (ctxt, consensus_pk) -> let map = Signature.Public_key_hash.Map.update - pkh - (function None -> Some [slot] | Some slots -> Some (slot :: slots)) + consensus_pk.delegate + (function + | None -> + Some + { + delegate = consensus_pk.delegate; + consensus_key = consensus_pk.consensus_pkh; + slots = [slot]; + } + | Some slots -> Some {slots with slots = slot :: slots.slots}) map in return (ctxt, map)) @@ -103,11 +117,17 @@ let endorsing_rights_by_first_slot ctxt level = Slot.Range.fold_es (fun (ctxt, (delegates_map, slots_map)) slot -> Stake_distribution.slot_owner ctxt level slot - >|=? fun (ctxt, (pk, pkh)) -> + >|=? fun (ctxt, consensus_pk) -> let initial_slot, delegates_map = - match Signature.Public_key_hash.Map.find pkh delegates_map with + match + Signature.Public_key_hash.Map.find consensus_pk.delegate delegates_map + with | None -> - (slot, Signature.Public_key_hash.Map.add pkh slot delegates_map) + ( slot, + Signature.Public_key_hash.Map.add + consensus_pk.delegate + slot + delegates_map ) | Some initial_slot -> (initial_slot, delegates_map) in (* [slots_map]'keys are the minimal slots of delegates because @@ -116,8 +136,8 @@ let endorsing_rights_by_first_slot ctxt level = Slot.Map.update initial_slot (function - | None -> Some (pk, pkh, 1) - | Some (pk, pkh, count) -> Some (pk, pkh, count + 1)) + | None -> Some (consensus_pk, 1) + | Some (consensus_pk, count) -> Some (consensus_pk, count + 1)) slots_map in (ctxt, (delegates_map, slots_map))) diff --git a/src/proto_alpha/lib_protocol/baking.mli b/src/proto_alpha/lib_protocol/baking.mli index 9d9281b735a3922496eb29ebff754b683cd13595..9e90eb4ea479969793121d7c08db0d831422e973 100644 --- a/src/proto_alpha/lib_protocol/baking.mli +++ b/src/proto_alpha/lib_protocol/baking.mli @@ -34,7 +34,11 @@ type error += consensus_threshold : int; } -type ordered_slots = private Slot.t list +type ordered_slots = private { + delegate : Signature.public_key_hash; + consensus_key : Signature.public_key_hash; + slots : Slot.t list; +} (** For a given level computes who has the right to include an endorsement in the next block. @@ -48,14 +52,14 @@ val endorsing_rights : Level.t -> (context * ordered_slots Signature.Public_key_hash.Map.t) tzresult Lwt.t -(** Computes endorsing rights for a given level. +(** Computes endorsing rights for a given level. @return map from allocated first slots to their owner's public key, public key hash, and endorsing power. *) val endorsing_rights_by_first_slot : context -> Level.t -> - (context * (public_key * public_key_hash * int) Slot.Map.t) tzresult Lwt.t + (context * (Consensus_key.pk * int) Slot.Map.t) tzresult Lwt.t (** Computes the bonus baking reward depending on the endorsing power. *) val bonus_baking_reward : context -> endorsing_power:int -> Tez.t tzresult @@ -63,4 +67,4 @@ val bonus_baking_reward : context -> endorsing_power:int -> Tez.t tzresult (** [baking_rights ctxt level] is the lazy list of contract's public key hashes that are allowed to propose for [level] at each round. *) -val baking_rights : context -> Level.t -> public_key lazy_list +val baking_rights : context -> Level.t -> Consensus_key.t lazy_list diff --git a/src/proto_alpha/lib_protocol/bootstrap_storage.ml b/src/proto_alpha/lib_protocol/bootstrap_storage.ml index ce7a64777383033371ced4c3243360916cb36d11..af0f71358bb6a7d81d3d2e39225db00df37275c3 100644 --- a/src/proto_alpha/lib_protocol/bootstrap_storage.ml +++ b/src/proto_alpha/lib_protocol/bootstrap_storage.ml @@ -24,7 +24,7 @@ (*****************************************************************************) let init_account (ctxt, balance_updates) - ({public_key_hash; public_key; amount; delegate_to} : + ({public_key_hash; public_key; amount; delegate_to; consensus_key} : Parameters_repr.bootstrap_account) = let contract = Contract_repr.Implicit public_key_hash in Token.transfer @@ -41,10 +41,16 @@ let init_account (ctxt, balance_updates) public_key_hash public_key >>=? fun ctxt -> - Delegate_storage.set + Delegate_storage.Contract.set ctxt contract (Some (Option.value ~default:public_key_hash delegate_to)) + >>=? fun ctxt -> + (match consensus_key with + | None -> return ctxt + | Some consensus_key -> + Delegate_consensus_key.init ctxt public_key_hash consensus_key) + >>=? fun ctxt -> return ctxt | None -> return ctxt) >|=? fun ctxt -> (ctxt, new_balance_updates @ balance_updates) @@ -62,7 +68,7 @@ let init_contract ~typecheck (ctxt, balance_updates) let contract = Contract_repr.Originated contract_hash in (match delegate with | None -> return ctxt - | Some delegate -> Delegate_storage.init ctxt contract delegate) + | Some delegate -> Delegate_storage.Contract.init ctxt contract delegate) >>=? fun ctxt -> let origin = Receipt_repr.Protocol_migration in Token.transfer ~origin ctxt `Bootstrap (`Contract contract) amount diff --git a/src/proto_alpha/lib_protocol/contract_delegate_storage.ml b/src/proto_alpha/lib_protocol/contract_delegate_storage.ml index 8dbb1063250d137bb618ecbbea219224ce703804..75efd48243daa84662d3c0079af54998d5758763 100644 --- a/src/proto_alpha/lib_protocol/contract_delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/contract_delegate_storage.ml @@ -25,14 +25,6 @@ let find = Storage.Contract.Delegate.find -(* A delegate is registered if its "implicit account" delegates to itself. *) -let registered c delegate = - Storage.Contract.Delegate.find c (Contract_repr.Implicit delegate) - >|=? function - | Some current_delegate -> - Signature.Public_key_hash.equal delegate current_delegate - | None -> false - let init ctxt contract delegate = Storage.Contract.Delegate.init ctxt contract delegate >>=? fun ctxt -> let delegate_contract = Contract_repr.Implicit delegate in diff --git a/src/proto_alpha/lib_protocol/contract_delegate_storage.mli b/src/proto_alpha/lib_protocol/contract_delegate_storage.mli index 45a05ca408ca08e7fde512f1895c367a6a308929..7fe992903bc0aaa985c4656cb687945300f36f71 100644 --- a/src/proto_alpha/lib_protocol/contract_delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/contract_delegate_storage.mli @@ -30,15 +30,9 @@ val find : Contract_repr.t -> Signature.Public_key_hash.t option tzresult Lwt.t -(** [registered ctxt delegate] returns true iff delegate is an implicit contract - that delegates to itself. *) -val registered : - Raw_context.t -> Signature.Public_key_hash.t -> bool tzresult Lwt.t - (** [init ctxt contract delegate] sets the [delegate] associated to [contract]. - This function is undefined if [contract] is not allocated, or if [contract] - has already a delegate. *) + This function returns an error if [contract] has already a delegate. *) val init : Raw_context.t -> Contract_repr.t -> @@ -60,10 +54,7 @@ val unlink : Raw_context.t -> Contract_repr.t -> Raw_context.t tzresult Lwt.t This function is undefined if [contract] is not allocated. *) val delete : Raw_context.t -> Contract_repr.t -> Raw_context.t tzresult Lwt.t -(** [set ctxt contract delegate] updates the [delegate] associated to [contract]. - - This function is undefined if [contract] is not allocated, or if [contract] - does not have a delegate. *) +(** [set ctxt contract delegate] updates the [delegate] associated to [contract]. *) val set : Raw_context.t -> Contract_repr.t -> diff --git a/src/proto_alpha/lib_protocol/contract_services.ml b/src/proto_alpha/lib_protocol/contract_services.ml index ee2a3a66a40e4bad08256bc7d0d4716689219e63..bb0115cc0942caa376b69520803d0d0f8bc79efd 100644 --- a/src/proto_alpha/lib_protocol/contract_services.ml +++ b/src/proto_alpha/lib_protocol/contract_services.ml @@ -370,7 +370,7 @@ let[@coq_axiom_with_reason "gadt"] register () = | false -> return_some None | true -> Contract.get_manager_key ctxt mgr >|=? fun key -> Some (Some key))) ; - register_opt_field ~chunked:false S.delegate Delegate.find ; + register_opt_field ~chunked:false S.delegate Contract.Delegate.find ; opt_register1 ~chunked:false S.counter (fun ctxt contract () () -> match contract with | Originated _ -> return_none @@ -501,7 +501,7 @@ let[@coq_axiom_with_reason "gadt"] register () = S.info (fun ctxt contract {normalize_types} -> Contract.get_balance ctxt contract >>=? fun balance -> - Delegate.find ctxt contract >>=? fun delegate -> + Contract.Delegate.find ctxt contract >>=? fun delegate -> (match contract with | Implicit manager -> Contract.get_counter ctxt manager >>=? fun counter -> diff --git a/src/proto_alpha/lib_protocol/delegate_consensus_key.ml b/src/proto_alpha/lib_protocol/delegate_consensus_key.ml new file mode 100644 index 0000000000000000000000000000000000000000..53646ff4e493f3f9c7b5a066f0a459d346bacb82 --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_consensus_key.ml @@ -0,0 +1,228 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +type error += + | Invalid_consensus_key_update_cycle of Cycle_repr.t + | Invalid_consensus_key_update_noop of Cycle_repr.t + | Invalid_consensus_key_update_active + +let () = + register_error_kind + `Permanent + ~id:"delegate.consensus_key.invalid_cycle" + ~title:"Invalid cycle for consensus key update" + ~description:"Tried to update the consensus key for an invalid cycle" + ~pp:(fun ppf cycle -> + Format.fprintf + ppf + "Invalid cycle while updating a consensus key (expected: %a)." + Cycle_repr.pp + cycle) + Data_encoding.(obj1 (req "cycle" Cycle_repr.encoding)) + (function Invalid_consensus_key_update_cycle c -> Some c | _ -> None) + (fun c -> Invalid_consensus_key_update_cycle c) ; + register_error_kind + `Permanent + ~id:"delegate.consensus_key.invalid_noop" + ~title:"Invalid key for consensus key update" + ~description:"Tried to update the consensus key with an active key" + ~pp:(fun ppf cycle -> + Format.fprintf + ppf + "Invalid key while updating a consensus key (active since %a)." + Cycle_repr.pp + cycle) + Data_encoding.(obj1 (req "cycle" Cycle_repr.encoding)) + (function Invalid_consensus_key_update_noop c -> Some c | _ -> None) + (fun c -> Invalid_consensus_key_update_noop c) ; + register_error_kind + `Permanent + ~id:"delegate.consensus_key.active" + ~title:"Active consensus key" + ~description: + "The delegate consensus key is already used by another delegate" + ~pp:(fun ppf () -> + Format.fprintf + ppf + "The delegate consensus key is already used by another delegate") + Data_encoding.empty + (function Invalid_consensus_key_update_active -> Some () | _ -> None) + (fun () -> Invalid_consensus_key_update_active) + +type pk = Raw_context.consensus_pk = { + delegate : Signature.Public_key_hash.t; + consensus_pk : Signature.Public_key.t; + consensus_pkh : Signature.Public_key_hash.t; +} + +type t = { + delegate : Signature.Public_key_hash.t; + consensus_pkh : Signature.Public_key_hash.t; +} + +let pkh {delegate; consensus_pkh; consensus_pk = _} = {delegate; consensus_pkh} + +let zero = + { + consensus_pkh = Signature.Public_key_hash.zero; + delegate = Signature.Public_key_hash.zero; + } + +let pp ppf {delegate; consensus_pkh} = + Format.fprintf ppf "@[%a" Signature.Public_key_hash.pp delegate ; + if not (Signature.Public_key_hash.equal delegate consensus_pkh) then + Format.fprintf + ppf + "@,Active key: %a" + Signature.Public_key_hash.pp + consensus_pkh ; + Format.fprintf ppf "@]" + +let check_inactive ctxt pkh = + let open Lwt_tzresult_syntax in + let*! is_active = Storage.Consensus_keys.mem ctxt pkh in + fail_when is_active Invalid_consensus_key_update_active + +let set_inactive = Storage.Consensus_keys.remove + +let set_active = Storage.Consensus_keys.add + +let init ctxt delegate pk = + let open Lwt_tzresult_syntax in + let pkh = Signature.Public_key.hash pk in + let* () = check_inactive ctxt pkh in + let*! ctxt = set_active ctxt pkh in + Storage.Contract.Consensus_key.init ctxt (Contract_repr.Implicit delegate) pk + +let active_pubkey ctxt delegate = + let open Lwt_tzresult_syntax in + let* pk = + Storage.Contract.Consensus_key.get ctxt (Contract_repr.Implicit delegate) + in + let pkh = Signature.Public_key.hash pk in + return {consensus_pk = pk; consensus_pkh = pkh; delegate} + +let active_key ctxt delegate = + let open Lwt_tzresult_syntax in + let* pk = active_pubkey ctxt delegate in + return (pkh pk) + +let raw_pending_updates ctxt delegate = + let open Lwt_tzresult_syntax in + let*! pendings = + Storage.Contract.Pending_consensus_keys.bindings + (ctxt, Contract_repr.Implicit delegate) + in + return pendings + +let pending_updates ctxt delegate = + let open Lwt_tzresult_syntax in + let* updates = raw_pending_updates ctxt delegate in + let updates = + List.sort (fun (c1, _) (c2, _) -> Cycle_repr.compare c1 c2) updates + in + return (List.map (fun (c, pk) -> (c, Signature.Public_key.hash pk)) updates) + +let raw_active_pubkey_for_cycle ctxt delegate cycle = + let open Lwt_tzresult_syntax in + let* pendings = raw_pending_updates ctxt delegate in + let* active = active_pubkey ctxt delegate in + let current_level = Raw_context.current_level ctxt in + let active_for_cycle = + List.fold_left + (fun (c1, active) (c2, pk) -> + if Cycle_repr.(c1 < c2 && c2 <= cycle) then (c2, pk) else (c1, active)) + (current_level.cycle, active.consensus_pk) + pendings + in + return active_for_cycle + +let active_pubkey_for_cycle ctxt delegate cycle = + let open Lwt_tzresult_syntax in + let* _, consensus_pk = raw_active_pubkey_for_cycle ctxt delegate cycle in + return + { + consensus_pk; + consensus_pkh = Signature.Public_key.hash consensus_pk; + delegate; + } + +let register_update ctxt delegate cycle pk = + let open Lwt_tzresult_syntax in + let* () = + let current_level = Raw_context.current_level ctxt in + let preserved_cycles = Constants_storage.preserved_cycles ctxt in + let required_cycle = + Cycle_repr.add current_level.cycle (preserved_cycles + 1) + in + fail_when + Cycle_repr.(cycle <> required_cycle) + (Invalid_consensus_key_update_cycle required_cycle) + in + let* () = + let* cycle, active_pubkey = + raw_active_pubkey_for_cycle ctxt delegate cycle + in + fail_when + Signature.Public_key.(pk = active_pubkey) + (Invalid_consensus_key_update_noop cycle) + in + let pkh = Signature.Public_key.hash pk in + let* () = check_inactive ctxt pkh in + let*! ctxt = set_active ctxt pkh in + let* {consensus_pkh = old_pkh; _} = + active_pubkey_for_cycle ctxt delegate cycle + in + let*! ctxt = set_inactive ctxt old_pkh in + let*! ctxt = + Storage.Contract.Pending_consensus_keys.add + (ctxt, Contract_repr.Implicit delegate) + cycle + pk + in + return ctxt + +let activate ctxt ~new_cycle = + let open Lwt_tzresult_syntax in + Storage.Delegates.fold + ctxt + ~order:`Undefined + ~init:(ok ctxt) + ~f:(fun delegate ctxt -> + let*? ctxt = ctxt in + let delegate = Contract_repr.Implicit delegate in + let* update = + Storage.Contract.Pending_consensus_keys.find (ctxt, delegate) new_cycle + in + match update with + | None -> return ctxt + | Some pk -> + let*! ctxt = Storage.Contract.Consensus_key.add ctxt delegate pk in + let*! ctxt = + Storage.Contract.Pending_consensus_keys.remove + (ctxt, delegate) + new_cycle + in + return ctxt) diff --git a/src/proto_alpha/lib_protocol/delegate_consensus_key.mli b/src/proto_alpha/lib_protocol/delegate_consensus_key.mli new file mode 100644 index 0000000000000000000000000000000000000000..f9e89bbae48ca86aa67916768c9cdd0301079b6b --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_consensus_key.mli @@ -0,0 +1,89 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +type error += + | Invalid_consensus_key_update_cycle of Cycle_repr.t + | Invalid_consensus_key_update_noop of Cycle_repr.t + | Invalid_consensus_key_update_active + +(** The public key of a consensus key and the associated delegate. *) +type pk = Raw_context.consensus_pk = { + delegate : Signature.Public_key_hash.t; + consensus_pk : Signature.Public_key.t; + consensus_pkh : Signature.Public_key_hash.t; +} + +(** The public key hash of a consensus key and the associated delegate. *) +type t = { + delegate : Signature.Public_key_hash.t; + consensus_pkh : Signature.Public_key_hash.t; +} + +val zero : t + +val pp : Format.formatter -> t -> unit + +val pkh : pk -> t + +(** Initialize the consensus key when registering a delegate. *) +val init : + Raw_context.t -> + Signature.Public_key_hash.t -> + Signature.Public_key.t -> + Raw_context.t tzresult Lwt.t + +(** Returns the active consensus key for the current cycle. *) +val active_pubkey : + Raw_context.t -> Signature.Public_key_hash.t -> pk tzresult Lwt.t + +(** Returns the active consensus key for the current cycle. *) +val active_key : + Raw_context.t -> Signature.Public_key_hash.t -> t tzresult Lwt.t + +(** Returns the active consensus key for the given cycle. *) +val active_pubkey_for_cycle : + Raw_context.t -> + Signature.Public_key_hash.t -> + Cycle_repr.t -> + pk tzresult Lwt.t + +(** Returns the list of pending consensus-key updates in upcoming cycles. *) +val pending_updates : + Raw_context.t -> + Signature.Public_key_hash.t -> + (Cycle_repr.t * Signature.Public_key_hash.t) list tzresult Lwt.t + +(** Register a consensus-key update in a given cycles. *) +val register_update : + Raw_context.t -> + Signature.Public_key_hash.t -> + Cycle_repr.t -> + Signature.Public_key.t -> + Raw_context.t tzresult Lwt.t + +(** Activate consensus keys at the beggining of cycle [new_cycle]. + This function iterates on all registered delegates. *) +val activate : + Raw_context.t -> new_cycle:Cycle_repr.t -> Raw_context.t tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/delegate_cycles.ml b/src/proto_alpha/lib_protocol/delegate_cycles.ml new file mode 100644 index 0000000000000000000000000000000000000000..438d57d7aecea6b49c7eb248a4a78a387b6f3735 --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_cycles.ml @@ -0,0 +1,309 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +let update_activity ctxt last_cycle = + let preserved = Constants_storage.preserved_cycles ctxt in + match Cycle_repr.sub last_cycle preserved with + | None -> return (ctxt, []) + | Some _unfrozen_cycle -> + Stake_storage.fold_on_active_delegates_with_rolls + ctxt + ~order:`Sorted + ~init:(Ok (ctxt, [])) + ~f:(fun delegate () acc -> + acc >>?= fun (ctxt, deactivated) -> + Delegate_activation_storage.last_cycle_before_deactivation + ctxt + delegate + >>=? fun cycle -> + if Cycle_repr.(cycle <= last_cycle) then + Stake_storage.set_inactive ctxt delegate >>= fun ctxt -> + return (ctxt, delegate :: deactivated) + else return (ctxt, deactivated)) + >|=? fun (ctxt, deactivated) -> (ctxt, deactivated) + +(* Return a map from delegates (with active stake at some cycle + in the cycle window [from_cycle, to_cycle]) to the maximum + of the stake to be deposited for each such cycle (which is just the + [frozen_deposits_percentage] of the active stake at that cycle). Also + return the delegates that have fallen out of the sliding window. *) +let max_frozen_deposits_and_delegates_to_remove ctxt ~from_cycle ~to_cycle = + let frozen_deposits_percentage = + Constants_storage.frozen_deposits_percentage ctxt + in + let cycles = Cycle_repr.(from_cycle ---> to_cycle) in + (match Cycle_repr.pred from_cycle with + | None -> return Signature.Public_key_hash.Set.empty + | Some cleared_cycle -> ( + Stake_storage.find_selected_distribution ctxt cleared_cycle + >|=? fun cleared_cycle_delegates -> + match cleared_cycle_delegates with + | None -> Signature.Public_key_hash.Set.empty + | Some delegates -> + List.fold_left + (fun set (d, _) -> Signature.Public_key_hash.Set.add d set) + Signature.Public_key_hash.Set.empty + delegates)) + >>=? fun cleared_cycle_delegates -> + List.fold_left_es + (fun (maxima, delegates_to_remove) (cycle : Cycle_repr.t) -> + Stake_storage.get_selected_distribution ctxt cycle + >|=? fun active_stakes -> + List.fold_left + (fun (maxima, delegates_to_remove) (delegate, stake) -> + let stake_to_be_deposited = + Tez_repr.(div_exn (mul_exn stake frozen_deposits_percentage) 100) + in + let maxima = + Signature.Public_key_hash.Map.update + delegate + (function + | None -> Some stake_to_be_deposited + | Some maximum -> + Some (Tez_repr.max maximum stake_to_be_deposited)) + maxima + in + let delegates_to_remove = + Signature.Public_key_hash.Set.remove delegate delegates_to_remove + in + (maxima, delegates_to_remove)) + (maxima, delegates_to_remove) + active_stakes) + (Signature.Public_key_hash.Map.empty, cleared_cycle_delegates) + cycles + +let freeze_deposits ?(origin = Receipt_repr.Block_application) ctxt ~new_cycle + ~balance_updates = + let max_slashable_period = Constants_storage.max_slashing_period ctxt in + (* We want to be able to slash for at most [max_slashable_period] *) + (match Cycle_repr.(sub new_cycle (max_slashable_period - 1)) with + | None -> + Storage.Tenderbake.First_level_of_protocol.get ctxt + >>=? fun first_level_of_protocol -> + let cycle_eras = Raw_context.cycle_eras ctxt in + let level = + Level_repr.level_from_raw ~cycle_eras first_level_of_protocol + in + return level.cycle + | Some cycle -> return cycle) + >>=? fun from_cycle -> + let preserved_cycles = Constants_storage.preserved_cycles ctxt in + let to_cycle = Cycle_repr.(add new_cycle preserved_cycles) in + max_frozen_deposits_and_delegates_to_remove ctxt ~from_cycle ~to_cycle + >>=? fun (maxima, delegates_to_remove) -> + Signature.Public_key_hash.Map.fold_es + (fun delegate maximum_stake_to_be_deposited (ctxt, balance_updates) -> + (* Here we make sure to preserve the following invariant : + maximum_stake_to_be_deposited <= frozen_deposits + balance + See select_distribution_for_cycle *) + let delegate_contract = Contract_repr.Implicit delegate in + Frozen_deposits_storage.update_initial_amount + ctxt + delegate_contract + maximum_stake_to_be_deposited + >>=? fun ctxt -> + Frozen_deposits_storage.get ctxt delegate_contract >>=? fun deposits -> + let current_amount = deposits.current_amount in + if Tez_repr.(current_amount > maximum_stake_to_be_deposited) then + Tez_repr.(current_amount -? maximum_stake_to_be_deposited) + >>?= fun to_reimburse -> + Token.transfer + ~origin + ctxt + (`Frozen_deposits delegate) + (`Delegate_balance delegate) + to_reimburse + >|=? fun (ctxt, bupds) -> (ctxt, bupds @ balance_updates) + else if Tez_repr.(current_amount < maximum_stake_to_be_deposited) then + Tez_repr.(maximum_stake_to_be_deposited -? current_amount) + >>?= fun desired_to_freeze -> + Contract_storage.get_balance ctxt delegate_contract >>=? fun balance -> + (* In case the delegate hasn't been slashed in this cycle, + the following invariant holds: + maximum_stake_to_be_deposited <= frozen_deposits + balance + See select_distribution_for_cycle + + If the delegate has been slashed during the cycle, the invariant + above doesn't necessarily hold. In this case, we freeze the max + we can for the delegate. *) + let to_freeze = Tez_repr.(min balance desired_to_freeze) in + Token.transfer + ~origin + ctxt + (`Delegate_balance delegate) + (`Frozen_deposits delegate) + to_freeze + >|=? fun (ctxt, bupds) -> (ctxt, bupds @ balance_updates) + else return (ctxt, balance_updates)) + maxima + (ctxt, balance_updates) + >>=? fun (ctxt, balance_updates) -> + (* Unfreeze deposits (that is, set them to zero) for delegates that + were previously in the relevant window (and therefore had some + frozen deposits) but are not in the new window; because that means + that such a delegate had no active stake in the relevant cycles, + and therefore it should have no frozen deposits. *) + Signature.Public_key_hash.Set.fold_es + (fun delegate (ctxt, balance_updates) -> + let delegate_contract = Contract_repr.Implicit delegate in + Frozen_deposits_storage.update_initial_amount + ctxt + delegate_contract + Tez_repr.zero + >>=? fun ctxt -> + Frozen_deposits_storage.get ctxt delegate_contract + >>=? fun frozen_deposits -> + if Tez_repr.(frozen_deposits.current_amount > zero) then + Token.transfer + ~origin + ctxt + (`Frozen_deposits delegate) + (`Delegate_balance delegate) + frozen_deposits.current_amount + >|=? fun (ctxt, bupds) -> (ctxt, bupds @ balance_updates) + else return (ctxt, balance_updates)) + delegates_to_remove + (ctxt, balance_updates) + +let delegate_has_revealed_nonces delegate unrevelead_nonces_set = + not (Signature.Public_key_hash.Set.mem delegate unrevelead_nonces_set) + +let distribute_endorsing_rewards ctxt last_cycle unrevealed_nonces = + let endorsing_reward_per_slot = + Constants_storage.endorsing_reward_per_slot ctxt + in + let unrevealed_nonces_set = + List.fold_left + (fun set {Storage.Seed.nonce_hash = _; delegate} -> + Signature.Public_key_hash.Set.add delegate set) + Signature.Public_key_hash.Set.empty + unrevealed_nonces + in + Stake_storage.get_total_active_stake ctxt last_cycle + >>=? fun total_active_stake -> + Stake_storage.get_selected_distribution ctxt last_cycle >>=? fun delegates -> + List.fold_left_es + (fun (ctxt, balance_updates) (delegate, active_stake) -> + let delegate_contract = Contract_repr.Implicit delegate in + Delegate_missed_endorsements_storage.reset_delegate_participation + ctxt + delegate + >>=? fun (ctxt, sufficient_participation) -> + let has_revealed_nonces = + delegate_has_revealed_nonces delegate unrevealed_nonces_set + in + let expected_slots = + Delegate_missed_endorsements_storage + .expected_slots_for_given_active_stake + ctxt + ~total_active_stake + ~active_stake + in + let rewards = Tez_repr.mul_exn endorsing_reward_per_slot expected_slots in + if sufficient_participation && has_revealed_nonces then + (* Sufficient participation: we pay the rewards *) + Token.transfer + ctxt + `Endorsing_rewards + (`Contract delegate_contract) + rewards + >|=? fun (ctxt, payed_rewards_receipts) -> + (ctxt, payed_rewards_receipts @ balance_updates) + else + (* Insufficient participation or unrevealed nonce: no rewards *) + Token.transfer + ctxt + `Endorsing_rewards + (`Lost_endorsing_rewards + (delegate, not sufficient_participation, not has_revealed_nonces)) + rewards + >|=? fun (ctxt, payed_rewards_receipts) -> + (ctxt, payed_rewards_receipts @ balance_updates)) + (ctxt, []) + delegates + +let cycle_end ctxt last_cycle = + Seed_storage.cycle_end ctxt last_cycle >>=? fun (ctxt, unrevealed_nonces) -> + let new_cycle = Cycle_repr.add last_cycle 1 in + Delegate_sampler.select_new_distribution_at_cycle_end ctxt ~new_cycle + >>=? fun ctxt -> + Delegate_consensus_key.activate ctxt ~new_cycle >>=? fun ctxt -> + Delegate_slashed_deposits_storage.clear_outdated_slashed_deposits + ctxt + ~new_cycle + >>= fun ctxt -> + distribute_endorsing_rewards ctxt last_cycle unrevealed_nonces + >>=? fun (ctxt, balance_updates) -> + freeze_deposits ctxt ~new_cycle ~balance_updates + >>=? fun (ctxt, balance_updates) -> + Stake_storage.clear_at_cycle_end ctxt ~new_cycle >>=? fun ctxt -> + Delegate_sampler.clear_outdated_sampling_data ctxt ~new_cycle >>=? fun ctxt -> + update_activity ctxt last_cycle >>=? fun (ctxt, deactivated_delagates) -> + return (ctxt, balance_updates, deactivated_delagates) + +let init_first_cycles ctxt ~origin = + let preserved = Constants_storage.preserved_cycles ctxt in + List.fold_left_es + (fun ctxt c -> + let cycle = Cycle_repr.of_int32_exn (Int32.of_int c) in + Stake_storage.snapshot ctxt >>=? fun ctxt -> + (* NB: we need to take several snapshots because + select_distribution_for_cycle deletes the snapshots *) + Delegate_sampler.select_distribution_for_cycle ctxt cycle) + ctxt + Misc.(0 --> preserved) + >>=? fun ctxt -> + let cycle = (Raw_context.current_level ctxt).cycle in + freeze_deposits ~origin ~new_cycle:cycle ~balance_updates:[] ctxt + +module Migration_from_Jakarta = struct + let update_delegate ctxt pkh = + let open Lwt_tzresult_syntax in + (* Consensus key*) + let* pk = Contract_manager_storage.get_manager_key ctxt pkh in + let* ctxt = Delegate_consensus_key.init ctxt pkh pk in + return ctxt + + let update ctxt = + let open Lwt_tzresult_syntax in + let*! delegates = Storage.Delegates.elements ctxt in + let* ctxt = List.fold_left_es update_delegate ctxt delegates in + let*! cycles = + Storage.Migration_from_Jakarta.Delegate_sampler_state.keys ctxt + in + let*! ctxt = + Storage.Migration_from_Jakarta.Delegate_sampler_state.clear ctxt + in + let*? ctxt = Raw_context.Migration_from_Jakarta.reset_samplers ctxt in + let* ctxt = + List.fold_left_es + Delegate_sampler.Migration_from_Jakarta.update_sampler + ctxt + cycles + in + return ctxt +end diff --git a/src/proto_alpha/lib_protocol/delegate_cycles.mli b/src/proto_alpha/lib_protocol/delegate_cycles.mli new file mode 100644 index 0000000000000000000000000000000000000000..a51e8a9a16e20ef66e1e80400d89f391c6937a2a --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_cycles.mli @@ -0,0 +1,53 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(** Trigger the context maintenance at the end of cycle 'n', i.e.: + unfreeze the endorsing rewards, potentially deactivate delegates. + Return the corresponding balances updates and the list of + deactivated delegates. *) +val cycle_end : + Raw_context.t -> + Cycle_repr.t -> + (Raw_context.t + * Receipt_repr.balance_updates + * Signature.Public_key_hash.t list) + tzresult + Lwt.t + +(** [init_first_cycles ctxt ~origin] computes and records the distribution of + the total active stake among active delegates. This concerns the total + active stake involved in the calculation of baking rights for all cycles + in the range [0, preserved_cycles]. It also freezes the deposits for all + the active delegates. *) +val init_first_cycles : + Raw_context.t -> + origin:Receipt_repr.update_origin -> + (Raw_context.t * Receipt_repr.balance_updates) tzresult Lwt.t + +module Migration_from_Jakarta : sig + val update : Raw_context.t -> Raw_context.t tzresult Lwt.t +end diff --git a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml new file mode 100644 index 0000000000000000000000000000000000000000..7869cfca16ec0432d2f108efbbfe43982413b2aa --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml @@ -0,0 +1,218 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(** This module is responsible for: + - [Storage.Contract.Missed_endorsements] +*) + +let expected_slots_for_given_active_stake ctxt ~total_active_stake ~active_stake + = + let blocks_per_cycle = + Int32.to_int (Constants_storage.blocks_per_cycle ctxt) + in + let consensus_committee_size = + Constants_storage.consensus_committee_size ctxt + in + let number_of_endorsements_per_cycle = + blocks_per_cycle * consensus_committee_size + in + Z.to_int + (Z.div + (Z.mul + (Z.of_int64 (Tez_repr.to_mutez active_stake)) + (Z.of_int number_of_endorsements_per_cycle)) + (Z.of_int64 (Tez_repr.to_mutez total_active_stake))) + +type level_participation = Participated | Didn't_participate + +(* Note that the participation for the last block of a cycle is + recorded in the next cycle. *) +let record_endorsing_participation ctxt ~delegate ~participation + ~endorsing_power = + match participation with + | Participated -> Stake_storage.set_active ctxt delegate + | Didn't_participate -> ( + let contract = Contract_repr.Implicit delegate in + Storage.Contract.Missed_endorsements.find ctxt contract >>=? function + | Some {remaining_slots; missed_levels} -> + let remaining_slots = remaining_slots - endorsing_power in + Storage.Contract.Missed_endorsements.update + ctxt + contract + {remaining_slots; missed_levels = missed_levels + 1} + | None -> ( + let level = Level_storage.current ctxt in + Raw_context.stake_distribution_for_current_cycle ctxt + >>?= fun stake_distribution -> + match + Signature.Public_key_hash.Map.find delegate stake_distribution + with + | None -> + (* This happens when the block is the first one in a + cycle, and therefore the endorsements are for the last + block of the previous cycle, and when the delegate does + not have an active stake at the current cycle; in this + case its participation is simply ignored. *) + assert (Compare.Int32.(level.cycle_position = 0l)) ; + return ctxt + | Some active_stake -> + Stake_storage.get_total_active_stake ctxt level.cycle + >>=? fun total_active_stake -> + let expected_slots = + expected_slots_for_given_active_stake + ctxt + ~total_active_stake + ~active_stake + in + let Ratio_repr.{numerator; denominator} = + Constants_storage.minimal_participation_ratio ctxt + in + let minimal_activity = expected_slots * numerator / denominator in + let maximal_inactivity = expected_slots - minimal_activity in + let remaining_slots = maximal_inactivity - endorsing_power in + Storage.Contract.Missed_endorsements.init + ctxt + contract + {remaining_slots; missed_levels = 1})) + +let record_baking_activity_and_pay_rewards_and_fees ctxt ~payload_producer + ~block_producer ~baking_reward ~reward_bonus = + Stake_storage.set_active ctxt payload_producer >>=? fun ctxt -> + (if not (Signature.Public_key_hash.equal payload_producer block_producer) then + Stake_storage.set_active ctxt block_producer + else return ctxt) + >>=? fun ctxt -> + let pay_payload_producer ctxt delegate = + let contract = Contract_repr.Implicit delegate in + Token.balance ctxt `Block_fees >>=? fun (ctxt, block_fees) -> + Token.transfer_n + ctxt + [(`Block_fees, block_fees); (`Baking_rewards, baking_reward)] + (`Contract contract) + in + let pay_block_producer ctxt delegate bonus = + let contract = Contract_repr.Implicit delegate in + Token.transfer ctxt `Baking_bonuses (`Contract contract) bonus + in + pay_payload_producer ctxt payload_producer + >>=? fun (ctxt, balance_updates_payload_producer) -> + (match reward_bonus with + | Some bonus -> pay_block_producer ctxt block_producer bonus + | None -> return (ctxt, [])) + >>=? fun (ctxt, balance_updates_block_producer) -> + return + (ctxt, balance_updates_payload_producer @ balance_updates_block_producer) + +type participation_info = { + expected_cycle_activity : int; + minimal_cycle_activity : int; + missed_slots : int; + missed_levels : int; + remaining_allowed_missed_slots : int; + expected_endorsing_rewards : Tez_repr.t; +} + +(* Inefficient, only for RPC *) +let participation_info ctxt delegate = + let level = Level_storage.current ctxt in + Stake_storage.get_selected_distribution ctxt level.cycle + >>=? fun stake_distribution -> + match + List.assoc_opt + ~equal:Signature.Public_key_hash.equal + delegate + stake_distribution + with + | None -> + (* delegate does not have an active stake at the current cycle *) + return + { + expected_cycle_activity = 0; + minimal_cycle_activity = 0; + missed_slots = 0; + missed_levels = 0; + remaining_allowed_missed_slots = 0; + expected_endorsing_rewards = Tez_repr.zero; + } + | Some active_stake -> + Stake_storage.get_total_active_stake ctxt level.cycle + >>=? fun total_active_stake -> + let expected_cycle_activity = + expected_slots_for_given_active_stake + ctxt + ~total_active_stake + ~active_stake + in + let Ratio_repr.{numerator; denominator} = + Constants_storage.minimal_participation_ratio ctxt + in + let endorsing_reward_per_slot = + Constants_storage.endorsing_reward_per_slot ctxt + in + let minimal_cycle_activity = + expected_cycle_activity * numerator / denominator + in + let maximal_cycle_inactivity = + expected_cycle_activity - minimal_cycle_activity + in + let expected_endorsing_rewards = + Tez_repr.mul_exn endorsing_reward_per_slot expected_cycle_activity + in + let contract = Contract_repr.Implicit delegate in + Storage.Contract.Missed_endorsements.find ctxt contract + >>=? fun missed_endorsements -> + let missed_slots, missed_levels, remaining_allowed_missed_slots = + match missed_endorsements with + | None -> (0, 0, maximal_cycle_inactivity) + | Some {remaining_slots; missed_levels} -> + ( maximal_cycle_inactivity - remaining_slots, + missed_levels, + Compare.Int.max 0 remaining_slots ) + in + let expected_endorsing_rewards = + match missed_endorsements with + | Some r when Compare.Int.(r.remaining_slots < 0) -> Tez_repr.zero + | _ -> expected_endorsing_rewards + in + return + { + expected_cycle_activity; + minimal_cycle_activity; + missed_slots; + missed_levels; + remaining_allowed_missed_slots; + expected_endorsing_rewards; + } + +let reset_delegate_participation ctxt delegate = + let contract = Contract_repr.Implicit delegate in + Storage.Contract.Missed_endorsements.find ctxt contract >>=? fun missed -> + match missed with + | None -> return (ctxt, true) + | Some missed_endorsements -> + Storage.Contract.Missed_endorsements.remove ctxt contract >>= fun ctxt -> + return (ctxt, Compare.Int.(missed_endorsements.remaining_slots >= 0)) diff --git a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.mli b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.mli new file mode 100644 index 0000000000000000000000000000000000000000..d2267d33a6a353fa2d1329053aefbb4e2e97285a --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.mli @@ -0,0 +1,93 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +val expected_slots_for_given_active_stake : + Raw_context.t -> + total_active_stake:Tez_repr.tez -> + active_stake:Tez_repr.tez -> + int + +type level_participation = Participated | Didn't_participate + +(** Record the participation of a delegate as a validator. *) +val record_endorsing_participation : + Raw_context.t -> + delegate:Signature.Public_key_hash.t -> + participation:level_participation -> + endorsing_power:int -> + Raw_context.t tzresult Lwt.t + +(** Check that a delegate participated enough in the last cycle, + and then reset the participation for preparing the next cycle. *) +val reset_delegate_participation : + Raw_context.t -> + Signature.Public_key_hash.t -> + (Raw_context.t * bool) tzresult Lwt.t + +(** Participation information. We denote by: + - "static" information that does not change during the cycle + - "dynamic" information that may change during the cycle *) +type participation_info = { + expected_cycle_activity : int; + (** The total expected slots to be endorsed in the cycle. (static) *) + minimal_cycle_activity : int; + (** The minimal endorsing slots in the cycle to get endorsing + rewards. (static) *) + missed_slots : int; + (** The number of missed endorsing slots in the cycle. (dynamic) *) + missed_levels : int; + (** The number of missed endorsing levels in the cycle. (dynamic) *) + remaining_allowed_missed_slots : int; + (** Remaining amount of endorsing slots that can be missed in the + cycle before forfeiting the rewards. (dynamic) *) + expected_endorsing_rewards : Tez_repr.t; + (** Endorsing rewards that will be distributed at the end of the + cycle if activity at that point will be greater than the minimal + required. If the activity is already known to be below the + required minimum, then the rewards are zero. (dynamic) *) +} + +(** Only use this function for RPC: this is expensive. + + [delegate_participation_info] and [!val:check_delegate] forms the + implementation of RPC call "/context/delegates//participation". + *) +val participation_info : + Raw_context.t -> + Signature.Public_key_hash.t -> + participation_info tzresult Lwt.t + +(** Sets the payload and block producer as active. Pays the baking + reward and the fees to the payload producer and the reward bonus to + the payload producer (if the reward_bonus is not None).*) +val record_baking_activity_and_pay_rewards_and_fees : + Raw_context.t -> + payload_producer:Signature.Public_key_hash.t -> + block_producer:Signature.Public_key_hash.t -> + baking_reward:Tez_repr.t -> + reward_bonus:Tez_repr.t option -> + (Raw_context.t * Receipt_repr.balance_updates) tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/delegate_sampler.ml b/src/proto_alpha/lib_protocol/delegate_sampler.ml new file mode 100644 index 0000000000000000000000000000000000000000..85240f203e3afa0481d2e5cbeff22221304fbedc --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_sampler.ml @@ -0,0 +1,266 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(** This module is responsible for: + - [Storage.Seed.For_cycle] + + And the submodule `Delegate_sampler_state` is responsible for: + - [Storage.Delegate_sampler_state] +*) + +module Delegate_sampler_state = struct + module Cache_client = struct + type cached_value = Delegate_consensus_key.pk Sampler.t + + let namespace = Cache_repr.create_namespace "sampler_state" + + let cache_index = 2 + + let value_of_identifier ctxt identifier = + let cycle = Cycle_repr.of_string_exn identifier in + Storage.Delegate_sampler_state.get ctxt cycle + end + + module Cache = (val Cache_repr.register_exn (module Cache_client)) + + let identifier_of_cycle cycle = Format.asprintf "%a" Cycle_repr.pp cycle + + let init ctxt cycle sampler_state = + let id = identifier_of_cycle cycle in + Storage.Delegate_sampler_state.init ctxt cycle sampler_state + >>=? fun ctxt -> + let size = 1 (* that's symbolic: 1 cycle = 1 entry *) in + Cache.update ctxt id (Some (sampler_state, size)) >>?= fun ctxt -> + return ctxt + + let get ctxt cycle = + let id = identifier_of_cycle cycle in + Cache.find ctxt id >>=? function + | None -> Storage.Delegate_sampler_state.get ctxt cycle + | Some v -> return v + + let remove_existing ctxt cycle = + let id = identifier_of_cycle cycle in + Cache.update ctxt id None >>?= fun ctxt -> + Storage.Delegate_sampler_state.remove_existing ctxt cycle +end + +module Random = struct + (* [init_random_state] initialize a random sequence drawing state + that's unique for a given (seed, level, index) triple. Elements + from this sequence are drawn using [take_int64], updating the + state for the next draw. The initial state is the Blake2b hash of + the three randomness sources, and an offset set to zero + (indicating that zero bits of randomness have been + consumed). When drawing random elements, bits are extracted from + the state until exhaustion (256 bits), at which point the state + is rehashed and the offset reset to 0. *) + + let init_random_state seed level index = + ( Raw_hashes.blake2b + (Data_encoding.Binary.to_bytes_exn + Data_encoding.(tup3 Seed_repr.seed_encoding int32 int32) + (seed, level.Level_repr.cycle_position, Int32.of_int index)), + 0 ) + + let take_int64 bound state = + let drop_if_over = + (* This function draws random values in [0-(bound-1)] by drawing + in [0-(2^63-1)] (64-bit) and computing the value modulo + [bound]. For the application of [mod bound] to preserve + uniformity, the input space must be of the form + [0-(n*bound-1)]. We enforce this by rejecting 64-bit samples + above this limit (in which case, we draw a new 64-sample from + the sequence and try again). *) + Int64.sub Int64.max_int (Int64.rem Int64.max_int bound) + in + let rec loop (bytes, n) = + let consumed_bytes = 8 in + let state_size = Bytes.length bytes in + if Compare.Int.(n > state_size - consumed_bytes) then + loop (Raw_hashes.blake2b bytes, 0) + else + let r = TzEndian.get_int64 bytes n in + (* The absolute value of min_int is min_int. Also, every + positive integer is represented twice (positive and negative), + but zero is only represented once. We fix both problems at + once. *) + let r = if Compare.Int64.(r = Int64.min_int) then 0L else Int64.abs r in + if Compare.Int64.(r >= drop_if_over) then + loop (bytes, n + consumed_bytes) + else + let v = Int64.rem r bound in + (v, (bytes, n + consumed_bytes)) + in + loop state + + (** [sampler_for_cycle ctxt cycle] reads the sampler for [cycle] from + [ctxt] if it has been previously inited. Otherwise it initializes + the sampler and caches it in [ctxt] with + [Raw_context.set_sampler_for_cycle]. *) + let sampler_for_cycle ctxt cycle = + let read ctxt = + Storage.Seed.For_cycle.get ctxt cycle >>=? fun seed -> + Delegate_sampler_state.get ctxt cycle >>=? fun state -> + return (seed, state) + in + Raw_context.sampler_for_cycle ~read ctxt cycle + + let owner c (level : Level_repr.t) offset = + let cycle = level.Level_repr.cycle in + sampler_for_cycle c cycle >>=? fun (c, seed, state) -> + let sample ~int_bound ~mass_bound = + let state = init_random_state seed level offset in + let i, state = take_int64 (Int64.of_int int_bound) state in + let elt, _ = take_int64 mass_bound state in + (Int64.to_int i, elt) + in + let pk = Sampler.sample state sample in + return (c, pk) +end + +let slot_owner c level slot = Random.owner c level (Slot_repr.to_int slot) + +let baking_rights_owner c (level : Level_repr.t) ~round = + Round_repr.to_int round >>?= fun round -> + let consensus_committee_size = Constants_storage.consensus_committee_size c in + Slot_repr.of_int (round mod consensus_committee_size) >>?= fun slot -> + slot_owner c level slot >>=? fun (ctxt, pk) -> return (ctxt, slot, pk) + +let get_stakes_for_selected_index ctxt index = + Stake_storage.fold_snapshot + ctxt + ~index + ~f:(fun (delegate, staking_balance) (acc, total_stake) -> + let delegate_contract = Contract_repr.Implicit delegate in + let open Tez_repr in + let open Lwt_result_syntax in + let* frozen_deposits_limit = + Delegate_storage.frozen_deposits_limit ctxt delegate + in + + let* balance_and_frozen_bonds = + Contract_storage.get_balance_and_frozen_bonds ctxt delegate_contract + in + let* frozen_deposits = + Frozen_deposits_storage.get ctxt delegate_contract + in + let*? total_balance = + balance_and_frozen_bonds +? frozen_deposits.current_amount + in + let* stake_for_cycle = + let frozen_deposits_percentage = + Int64.of_int @@ Constants_storage.frozen_deposits_percentage ctxt + in + let max_mutez = of_mutez_exn Int64.max_int in + let frozen_deposits_limit = + match frozen_deposits_limit with Some fdp -> fdp | None -> max_mutez + in + let aux = min total_balance frozen_deposits_limit in + let*? overflow_bound = max_mutez /? 100L in + if aux <= overflow_bound then + let*? aux = aux *? 100L in + let*? v = aux /? frozen_deposits_percentage in + return (min v staking_balance) + else + let*? sbal = staking_balance /? 100L in + let*? a = aux /? frozen_deposits_percentage in + if sbal <= a then return staking_balance + else + let*? r = max_mutez /? frozen_deposits_percentage in + return r + in + let*? total_stake = Tez_repr.(total_stake +? stake_for_cycle) in + return ((delegate, stake_for_cycle) :: acc, total_stake)) + ~init:([], Tez_repr.zero) + +let compute_snapshot_index_for_seed ~max_snapshot_index seed = + let rd = Seed_repr.initialize_new seed [Bytes.of_string "stake_snapshot"] in + let seq = Seed_repr.sequence rd 0l in + Seed_repr.take_int32 seq (Int32.of_int max_snapshot_index) + |> fst |> Int32.to_int |> return + +let compute_snapshot_index ctxt cycle ~max_snapshot_index = + Storage.Seed.For_cycle.get ctxt cycle >>=? fun seed -> + compute_snapshot_index_for_seed ~max_snapshot_index seed + +let select_distribution_for_cycle ctxt cycle = + Stake_storage.max_snapshot_index ctxt >>=? fun max_snapshot_index -> + Storage.Seed.For_cycle.get ctxt cycle >>=? fun seed -> + compute_snapshot_index_for_seed ~max_snapshot_index seed + >>=? fun selected_index -> + get_stakes_for_selected_index ctxt selected_index + >>=? fun (stakes, total_stake) -> + Stake_storage.set_selected_distribution_for_cycle + ctxt + cycle + stakes + total_stake + >>=? fun ctxt -> + List.fold_left_es + (fun acc (pkh, stake) -> + Delegate_consensus_key.active_pubkey_for_cycle ctxt pkh cycle + >|=? fun pk -> (pk, Tez_repr.to_mutez stake) :: acc) + [] + stakes + >>=? fun stakes_pk -> + let state = Sampler.create stakes_pk in + Delegate_sampler_state.init ctxt cycle state >>=? fun ctxt -> + (* pre-allocate the sampler *) + Lwt.return (Raw_context.init_sampler_for_cycle ctxt cycle seed state) + +let select_new_distribution_at_cycle_end ctxt ~new_cycle = + let preserved = Constants_storage.preserved_cycles ctxt in + let for_cycle = Cycle_repr.add new_cycle preserved in + select_distribution_for_cycle ctxt for_cycle + +let clear_outdated_sampling_data ctxt ~new_cycle = + let max_slashing_period = Constants_storage.max_slashing_period ctxt in + match Cycle_repr.sub new_cycle max_slashing_period with + | None -> return ctxt + | Some outdated_cycle -> + Delegate_sampler_state.remove_existing ctxt outdated_cycle + >>=? fun ctxt -> + Storage.Seed.For_cycle.remove_existing ctxt outdated_cycle + +module Migration_from_Jakarta = struct + let update_sampler ctxt cycle = + let open Lwt_tzresult_syntax in + let* stakes = Stake_storage.get_selected_distribution ctxt cycle in + let* stakes_pk = + List.fold_left_es + (fun acc (delegate, stake) -> + Delegate_consensus_key.active_pubkey ctxt delegate >>=? fun pk -> + return ((pk, Tez_repr.to_mutez stake) :: acc)) + [] + stakes + in + let state = Sampler.create stakes_pk in + Delegate_sampler_state.init ctxt cycle state >>=? fun ctxt -> + Storage.Seed.For_cycle.get ctxt cycle >>=? fun seed -> + Lwt.return (Raw_context.init_sampler_for_cycle ctxt cycle seed state) +end diff --git a/src/proto_alpha/lib_protocol/delegate_sampler.mli b/src/proto_alpha/lib_protocol/delegate_sampler.mli new file mode 100644 index 0000000000000000000000000000000000000000..ae3132041a18719402bcc911d86a90aae3a46fd1 --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_sampler.mli @@ -0,0 +1,65 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(** Participation slots potentially associated to accounts. The + accounts that didn't place a deposit will be excluded from this + list. This function should only be used to compute the deposits to + freeze or initialize the protocol while stitching. RPCs can use this + function to predict an approximation of long term future slot + allocations. It shouldn't be used in the baker. *) +val slot_owner : + Raw_context.t -> + Level_repr.t -> + Slot_repr.t -> + (Raw_context.t * Delegate_consensus_key.pk) tzresult Lwt.t + +val baking_rights_owner : + Raw_context.t -> + Level_repr.t -> + round:Round_repr.round -> + (Raw_context.t * Slot_repr.t * Delegate_consensus_key.pk) tzresult Lwt.t + +(** [compute_snapshot_index ctxt cycle max_snapshot_index] Returns the index of + the selected snapshot for the [cycle] passed as argument, and for the max + index of snapshots taken so far, [max_snapshot_index] (see + [Stake_storage.max_snapshot_index]. *) +val compute_snapshot_index : + Raw_context.t -> Cycle_repr.t -> max_snapshot_index:int -> int tzresult Lwt.t + +val select_new_distribution_at_cycle_end : + Raw_context.t -> new_cycle:Cycle_repr.t -> Raw_context.t tzresult Lwt.t + +val clear_outdated_sampling_data : + Raw_context.t -> new_cycle:Cycle_repr.t -> Raw_context.t tzresult Lwt.t + +val select_distribution_for_cycle : + Raw_context.t -> Cycle_repr.t -> Raw_context.t tzresult Lwt.t + +module Migration_from_Jakarta : sig + val update_sampler : + Raw_context.t -> Cycle_repr.t -> Raw_context.t tzresult Lwt.t +end diff --git a/src/proto_alpha/lib_protocol/delegate_services.ml b/src/proto_alpha/lib_protocol/delegate_services.ml index b3daf7ff85bc01f77f5ef93159f7f41a1d6861be..d87b9610b4f898d60de8db83cf0ad42cb76d3a24 100644 --- a/src/proto_alpha/lib_protocol/delegate_services.ml +++ b/src/proto_alpha/lib_protocol/delegate_services.ml @@ -60,6 +60,8 @@ type info = { deactivated : bool; grace_period : Cycle.t; voting_info : Vote.delegate_info; + active_consensus_key : Signature.Public_key_hash.t; + pending_consensus_keys : (Cycle.t * Signature.Public_key_hash.t) list; } let info_encoding = @@ -76,6 +78,8 @@ let info_encoding = deactivated; grace_period; voting_info; + active_consensus_key; + pending_consensus_keys; } -> ( ( full_balance, current_frozen_deposits, @@ -86,7 +90,7 @@ let info_encoding = delegated_balance, deactivated, grace_period ), - voting_info )) + (voting_info, (active_consensus_key, pending_consensus_keys)) )) (fun ( ( full_balance, current_frozen_deposits, frozen_deposits, @@ -96,7 +100,7 @@ let info_encoding = delegated_balance, deactivated, grace_period ), - voting_info ) -> + (voting_info, (active_consensus_key, pending_consensus_keys)) ) -> { full_balance; current_frozen_deposits; @@ -108,6 +112,8 @@ let info_encoding = deactivated; grace_period; voting_info; + active_consensus_key; + pending_consensus_keys; }) (merge_objs (obj9 @@ -120,7 +126,17 @@ let info_encoding = (req "delegated_balance" Tez.encoding) (req "deactivated" bool) (req "grace_period" Cycle.encoding)) - Vote.delegate_info_encoding) + (merge_objs + Vote.delegate_info_encoding + (obj2 + (req "active_consensus_key" Signature.Public_key_hash.encoding) + (dft + "pending_consensus_keys" + (list + (obj2 + (req "cycle" Cycle.encoding) + (req "pkh" Signature.Public_key_hash.encoding))) + [])))) let participation_info_encoding = let open Data_encoding in @@ -307,6 +323,25 @@ module S = struct ~output:Vote.delegate_info_encoding RPC_path.(path / "voting_info") + let consensus_key = + RPC_service.get_service + ~description: + "The active consensus key for a given delegate and the pending \ + consensus keys." + ~query:RPC_query.empty + ~output: + Data_encoding.( + obj2 + (req "active" Signature.Public_key_hash.encoding) + (dft + "pendings" + (list + (obj2 + (req "cycle" Cycle.encoding) + (req "pkh" Signature.Public_key_hash.encoding))) + [])) + RPC_path.(path / "consensus_key") + let participation = RPC_service.get_service ~description: @@ -365,7 +400,7 @@ let register () = | {with_minimal_stake = false; without_minimal_stake = false; _} -> return delegates) ; register1 ~chunked:false S.info (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.full_balance ctxt pkh >>=? fun full_balance -> Delegate.frozen_deposits ctxt pkh >>=? fun frozen_deposits -> Delegate.staking_balance ctxt pkh >>=? fun staking_balance -> @@ -374,7 +409,9 @@ let register () = Delegate.delegated_balance ctxt pkh >>=? fun delegated_balance -> Delegate.deactivated ctxt pkh >>=? fun deactivated -> Delegate.last_cycle_before_deactivation ctxt pkh >>=? fun grace_period -> - Vote.get_delegate_info ctxt pkh >|=? fun voting_info -> + Vote.get_delegate_info ctxt pkh >>=? fun voting_info -> + Delegate.Consensus_key.active_pubkey ctxt pkh >>=? fun consensus_key -> + Delegate.Consensus_key.pending_updates ctxt pkh >|=? fun pendings -> { full_balance; current_frozen_deposits = frozen_deposits.current_amount; @@ -386,45 +423,52 @@ let register () = deactivated; grace_period; voting_info; + active_consensus_key = consensus_key.consensus_pkh; + pending_consensus_keys = pendings; }) ; register1 ~chunked:false S.full_balance (fun ctxt pkh () () -> - trace (Balance_rpc_non_delegate pkh) (Delegate.check_delegate ctxt pkh) + trace (Balance_rpc_non_delegate pkh) (Delegate.check_registered ctxt pkh) >>=? fun () -> Delegate.full_balance ctxt pkh) ; register1 ~chunked:false S.current_frozen_deposits (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.frozen_deposits ctxt pkh >>=? fun deposits -> return deposits.current_amount) ; register1 ~chunked:false S.frozen_deposits (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.frozen_deposits ctxt pkh >>=? fun deposits -> return deposits.initial_amount) ; register1 ~chunked:false S.staking_balance (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.staking_balance ctxt pkh) ; register1 ~chunked:false S.frozen_deposits_limit (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.frozen_deposits_limit ctxt pkh) ; register1 ~chunked:true S.delegated_contracts (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.delegated_contracts ctxt pkh >|= ok) ; register1 ~chunked:false S.delegated_balance (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.delegated_balance ctxt pkh) ; register1 ~chunked:false S.deactivated (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.deactivated ctxt pkh) ; register1 ~chunked:false S.grace_period (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.last_cycle_before_deactivation ctxt pkh) ; register1 ~chunked:false S.voting_power (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Vote.get_voting_power_free ctxt pkh) ; register1 ~chunked:false S.voting_info (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Vote.get_delegate_info ctxt pkh) ; + register1 ~chunked:false S.consensus_key (fun ctxt pkh () () -> + Delegate.check_registered ctxt pkh >>=? fun () -> + Delegate.Consensus_key.active_pubkey ctxt pkh >>=? fun pk -> + Delegate.Consensus_key.pending_updates ctxt pkh >>=? fun pendings -> + return (pk.consensus_pkh, pendings)) ; register1 ~chunked:false S.participation (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> - Delegate.delegate_participation_info ctxt pkh) + Delegate.check_registered ctxt pkh >>=? fun () -> + Delegate.participation_info ctxt pkh) let list ctxt block ?(active = true) ?(inactive = false) ?(with_minimal_stake = true) ?(without_minimal_stake = false) () = @@ -470,5 +514,8 @@ let voting_power ctxt block pkh = let voting_info ctxt block pkh = RPC_context.make_call1 S.voting_info ctxt block pkh () () +let consensus_key ctxt block pkh = + RPC_context.make_call1 S.consensus_key ctxt block pkh () () + let participation ctxt block pkh = RPC_context.make_call1 S.participation ctxt block pkh () () diff --git a/src/proto_alpha/lib_protocol/delegate_services.mli b/src/proto_alpha/lib_protocol/delegate_services.mli index 8092f2c368cd0652ae1c5bbb7076c0ba60b163e6..e53ac412ba1618f74fb15442109a0ecef3eaf218 100644 --- a/src/proto_alpha/lib_protocol/delegate_services.mli +++ b/src/proto_alpha/lib_protocol/delegate_services.mli @@ -52,6 +52,8 @@ type info = { deactivated : bool; grace_period : Cycle.t; voting_info : Vote.delegate_info; + active_consensus_key : Signature.Public_key_hash.t; + pending_consensus_keys : (Cycle.t * Signature.Public_key_hash.t) list; } val info_encoding : info Data_encoding.t @@ -125,6 +127,14 @@ val voting_info : public_key_hash -> Vote.delegate_info shell_tzresult Lwt.t +val consensus_key : + 'a #RPC_context.simple -> + 'a -> + Signature.Public_key_hash.t -> + (Signature.Public_key_hash.t * (Cycle.t * Signature.Public_key_hash.t) list) + shell_tzresult + Lwt.t + val participation : 'a #RPC_context.simple -> 'a -> diff --git a/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml new file mode 100644 index 0000000000000000000000000000000000000000..a67b302e57ba329d92762b785f28f3b374fac164 --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml @@ -0,0 +1,136 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(** This module is responsible for: + - [Storage.Slashed_deposits] +*) + +type error += Unrequired_denunciation + +let () = + register_error_kind + `Branch + ~id:"block.unrequired_denunciation" + ~title:"Unrequired denunciation" + ~description:"A denunciation is unrequired" + ~pp:(fun ppf _ -> + Format.fprintf + ppf + "A valid denunciation cannot be applied: the associated delegate has \ + already been denounced for this level.") + Data_encoding.unit + (function Unrequired_denunciation -> Some () | _ -> None) + (fun () -> Unrequired_denunciation) + +let clear_outdated_slashed_deposits ctxt ~new_cycle = + let max_slashable_period = Constants_storage.max_slashing_period ctxt in + match Cycle_repr.(sub new_cycle max_slashable_period) with + | None -> Lwt.return ctxt + | Some outdated_cycle -> Storage.Slashed_deposits.clear (ctxt, outdated_cycle) + +let punish_double_endorsing ctxt delegate (level : Level_repr.t) = + let open Lwt_tzresult_syntax in + let* slashed = + Storage.Slashed_deposits.find (ctxt, level.cycle) (level.level, delegate) + in + let* updated_slashed = + match slashed with + | None -> + return {Storage.for_double_endorsing = true; for_double_baking = false} + | Some slashed -> + let* () = + fail_when slashed.for_double_endorsing Unrequired_denunciation + in + return {slashed with for_double_endorsing = true} + in + let delegate_contract = Contract_repr.Implicit delegate in + let* frozen_deposits = Frozen_deposits_storage.get ctxt delegate_contract in + let slashing_ratio : Ratio_repr.t = + Constants_storage.ratio_of_frozen_deposits_slashed_per_double_endorsement + ctxt + in + let punish_value = + Tez_repr.( + div_exn + (mul_exn frozen_deposits.initial_amount slashing_ratio.numerator) + slashing_ratio.denominator) + in + let amount_to_burn = + Tez_repr.(min frozen_deposits.current_amount punish_value) + in + let* ctxt, balance_updates = + Token.transfer + ctxt + (`Frozen_deposits delegate) + `Double_signing_punishments + amount_to_burn + in + let* ctxt = Stake_storage.remove_stake ctxt delegate amount_to_burn in + let*! ctxt = + Storage.Slashed_deposits.add + (ctxt, level.cycle) + (level.level, delegate) + updated_slashed + in + return (ctxt, amount_to_burn, balance_updates) + +let punish_double_baking ctxt delegate (level : Level_repr.t) = + let open Lwt_tzresult_syntax in + let* slashed = + Storage.Slashed_deposits.find (ctxt, level.cycle) (level.level, delegate) + in + let* updated_slashed = + match slashed with + | None -> + return {Storage.for_double_baking = true; for_double_endorsing = false} + | Some slashed -> + let* () = fail_when slashed.for_double_baking Unrequired_denunciation in + return {slashed with for_double_baking = true} + in + let delegate_contract = Contract_repr.Implicit delegate in + let* frozen_deposits = Frozen_deposits_storage.get ctxt delegate_contract in + let slashing_for_one_block = + Constants_storage.double_baking_punishment ctxt + in + let amount_to_burn = + Tez_repr.(min frozen_deposits.current_amount slashing_for_one_block) + in + let* ctxt, balance_updates = + Token.transfer + ctxt + (`Frozen_deposits delegate) + `Double_signing_punishments + amount_to_burn + in + let* ctxt = Stake_storage.remove_stake ctxt delegate amount_to_burn in + let*! ctxt = + Storage.Slashed_deposits.add + (ctxt, level.cycle) + (level.level, delegate) + updated_slashed + in + return (ctxt, amount_to_burn, balance_updates) diff --git a/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.mli b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.mli new file mode 100644 index 0000000000000000000000000000000000000000..189a37d290116d3fdf77a8aaeb087047ecb3e53d --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.mli @@ -0,0 +1,59 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +type error += Unrequired_denunciation + +(** Burn some frozen deposit for a delegate at a given level and + record in the context that the given delegate has now been slashed + for double endorsing for the given level. + + Returns the burned amount. + + Fails with [Unrequired_denunciation] if the given delegate has + already been slashed for double endorsing for the given level. *) +val punish_double_endorsing : + Raw_context.t -> + Signature.Public_key_hash.t -> + Level_repr.t -> + (Raw_context.t * Tez_repr.t * Receipt_repr.balance_updates) tzresult Lwt.t + +(** Burn some frozen deposit for a delegate at a given level and + record in the context that the given delegate has now been slashed + for double baking for the given level. + + Returns the burned amount. + + Fails with [Unrequired_denunciation] if the given delegate has + already been slashed for double baking for the given level. *) +val punish_double_baking : + Raw_context.t -> + Signature.Public_key_hash.t -> + Level_repr.t -> + (Raw_context.t * Tez_repr.t * Receipt_repr.balance_updates) tzresult Lwt.t + +val clear_outdated_slashed_deposits : + Raw_context.t -> new_cycle:Cycle_repr.t -> Raw_context.t Lwt.t diff --git a/src/proto_alpha/lib_protocol/delegate_storage.ml b/src/proto_alpha/lib_protocol/delegate_storage.ml index d2f0fa7c765693732737e996abd195f996c6f61c..c323433c11cacbaa390d6f5803a6980ab52ea79a 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_storage.ml @@ -3,6 +3,7 @@ (* Open Source License *) (* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) (* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -24,132 +25,20 @@ (* *) (*****************************************************************************) -type error += - | (* `Permanent *) No_deletion of Signature.Public_key_hash.t - | (* `Temporary *) Active_delegate - | (* `Temporary *) Current_delegate - | (* `Permanent *) Empty_delegate_account of Signature.Public_key_hash.t - | (* `Permanent *) Unregistered_delegate of Signature.Public_key_hash.t - | (* `Permanent *) Unassigned_validation_slot_for_level of Level_repr.t * int - | (* `Permanent *) - Cannot_find_active_stake of { - cycle : Cycle_repr.t; - delegate : Signature.Public_key_hash.t; - } - | (* `Temporary *) Not_registered of Signature.Public_key_hash.t +(** This is module regroups everything related to delegate registration + (see the invariant maintained by the submodule `Contract`. + + It also regroups "trivial" getter/setter related to delegates. + + It is responsible for: + - [Storage.Contract.Frozen_deposits_limit] + - [Storage.Delegates] + +*) + +type error += (* `Temporary *) Not_registered of Signature.Public_key_hash.t let () = - register_error_kind - `Permanent - ~id:"delegate.no_deletion" - ~title:"Forbidden delegate deletion" - ~description:"Tried to unregister a delegate" - ~pp:(fun ppf delegate -> - Format.fprintf - ppf - "Delegate deletion is forbidden (%a)" - Signature.Public_key_hash.pp - delegate) - Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) - (function No_deletion c -> Some c | _ -> None) - (fun c -> No_deletion c) ; - register_error_kind - `Temporary - ~id:"delegate.already_active" - ~title:"Delegate already active" - ~description:"Useless delegate reactivation" - ~pp:(fun ppf () -> - Format.fprintf ppf "The delegate is still active, no need to refresh it") - Data_encoding.empty - (function Active_delegate -> Some () | _ -> None) - (fun () -> Active_delegate) ; - register_error_kind - `Temporary - ~id:"delegate.unchanged" - ~title:"Unchanged delegated" - ~description:"Contract already delegated to the given delegate" - ~pp:(fun ppf () -> - Format.fprintf - ppf - "The contract is already delegated to the same delegate") - Data_encoding.empty - (function Current_delegate -> Some () | _ -> None) - (fun () -> Current_delegate) ; - register_error_kind - `Permanent - ~id:"delegate.empty_delegate_account" - ~title:"Empty delegate account" - ~description:"Cannot register a delegate when its implicit account is empty" - ~pp:(fun ppf delegate -> - Format.fprintf - ppf - "Delegate registration is forbidden when the delegate\n\ - \ implicit account is empty (%a)" - Signature.Public_key_hash.pp - delegate) - Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) - (function Empty_delegate_account c -> Some c | _ -> None) - (fun c -> Empty_delegate_account c) ; - (* Unregistered delegate *) - register_error_kind - `Permanent - ~id:"contract.manager.unregistered_delegate" - ~title:"Unregistered delegate" - ~description:"A contract cannot be delegated to an unregistered delegate" - ~pp:(fun ppf k -> - Format.fprintf - ppf - "The provided public key (with hash %a) is not registered as valid \ - delegate key." - Signature.Public_key_hash.pp - k) - Data_encoding.(obj1 (req "hash" Signature.Public_key_hash.encoding)) - (function Unregistered_delegate k -> Some k | _ -> None) - (fun k -> Unregistered_delegate k) ; - (* Unassigned_validation_slot_for_level *) - register_error_kind - `Permanent - ~id:"delegate.unassigned_validation_slot_for_level" - ~title:"Unassigned validation slot for level" - ~description: - "The validation slot for the given level is not assigned. Nobody payed \ - for that slot, or the level is either in the past or too far in the \ - future (further than the validatiors_selection_offset constant)" - ~pp:(fun ppf (l, slot) -> - Format.fprintf - ppf - "The validation slot %i for the level %a is not assigned. Nobody payed \ - for that slot, or the level is either in the past or too far in the \ - future (further than the validatiors_selection_offset constant)" - slot - Level_repr.pp - l) - Data_encoding.(obj2 (req "level" Level_repr.encoding) (req "slot" int31)) - (function - | Unassigned_validation_slot_for_level (l, s) -> Some (l, s) | _ -> None) - (fun (l, s) -> Unassigned_validation_slot_for_level (l, s)) ; - register_error_kind - `Permanent - ~id:"delegate.cannot_find_active_stake" - ~title:"Cannot find active stake" - ~description: - "The active stake of a delegate cannot be found for the given cycle." - ~pp:(fun ppf (cycle, delegate) -> - Format.fprintf - ppf - "The active stake of the delegate %a cannot be found for the cycle %a." - Cycle_repr.pp - cycle - Signature.Public_key_hash.pp - delegate) - Data_encoding.( - obj2 - (req "cycle" Cycle_repr.encoding) - (req "delegate" Signature.Public_key_hash.encoding)) - (function - | Cannot_find_active_stake {cycle; delegate} -> Some (cycle, delegate) - | _ -> None) - (fun (cycle, delegate) -> Cannot_find_active_stake {cycle; delegate}) ; register_error_kind `Temporary ~id:"delegate.not_registered" @@ -170,105 +59,19 @@ let () = (function Not_registered pkh -> Some pkh | _ -> None) (fun pkh -> Not_registered pkh) -let set_inactive ctxt delegate = - Delegate_activation_storage.set_inactive ctxt delegate >>= fun ctxt -> - Stake_storage.deactivate_only_call_from_delegate_storage ctxt delegate >|= ok +let registered = Storage.Delegates.mem -let set_active ctxt delegate = - Delegate_activation_storage.set_active ctxt delegate - >>=? fun (ctxt, inactive) -> - if not inactive then return ctxt - else Stake_storage.activate_only_call_from_delegate_storage ctxt delegate - -let staking_balance ctxt delegate = - Contract_delegate_storage.registered ctxt delegate >>=? fun is_registered -> - if is_registered then Stake_storage.get_staking_balance ctxt delegate - else return Tez_repr.zero +let check_registered ctxt pkh = + registered ctxt pkh >>= function + | true -> return_unit + | false -> fail (Not_registered pkh) -let pubkey ctxt delegate = - Contract_manager_storage.get_manager_key - ctxt - delegate - ~error:(Unregistered_delegate delegate) +let fold = Storage.Delegates.fold -let init ctxt contract delegate = - Contract_manager_storage.is_manager_key_revealed ctxt delegate - >>=? fun known_delegate -> - error_unless known_delegate (Unregistered_delegate delegate) >>?= fun () -> - Contract_delegate_storage.registered ctxt delegate >>=? fun is_registered -> - error_unless is_registered (Unregistered_delegate delegate) >>?= fun () -> - Contract_delegate_storage.init ctxt contract delegate >>=? fun ctxt -> - Contract_storage.get_balance_and_frozen_bonds ctxt contract - >>=? fun balance_and_frozen_bonds -> - Stake_storage.add_stake ctxt delegate balance_and_frozen_bonds +let list = Storage.Delegates.elements -let set c contract delegate = - match delegate with - | None -> ( - (* check if contract is a registered delegate *) - (match contract with - | Contract_repr.Implicit pkh -> - Contract_delegate_storage.registered c pkh >>=? fun is_registered -> - fail_when is_registered (No_deletion pkh) - | Originated _ -> return_unit) - >>=? fun () -> - Contract_delegate_storage.find c contract >>=? function - | None -> return c - | Some delegate -> - (* Removes the balance of the contract from the delegate *) - Contract_storage.get_balance_and_frozen_bonds c contract - >>=? fun balance_and_frozen_bonds -> - Stake_storage.remove_stake c delegate balance_and_frozen_bonds - >>=? fun c -> Contract_delegate_storage.delete c contract) - | Some delegate -> - Contract_manager_storage.is_manager_key_revealed c delegate - >>=? fun known_delegate -> - Contract_delegate_storage.registered c delegate - >>=? fun registered_delegate -> - let self_delegation = - match contract with - | Implicit pkh -> Signature.Public_key_hash.equal pkh delegate - | Originated _ -> false - in - if (not known_delegate) || not (registered_delegate || self_delegation) - then fail (Unregistered_delegate delegate) - else - Contract_delegate_storage.find c contract >>=? fun current_delegate -> - (match current_delegate with - | Some current_delegate - when Signature.Public_key_hash.equal delegate current_delegate -> - if self_delegation then - Delegate_activation_storage.is_inactive c delegate >>=? function - | true -> return_unit - | false -> fail Active_delegate - else fail Current_delegate - | None | Some _ -> return_unit) - >>=? fun () -> - (* check if contract is a registered delegate *) - (match contract with - | Contract_repr.Implicit pkh -> - Contract_delegate_storage.registered c pkh >>=? fun is_registered -> - (* allow self-delegation to re-activate *) - if (not self_delegation) && is_registered then - fail (No_deletion pkh) - else return_unit - | Originated _ -> return_unit) - >>=? fun () -> - Storage.Contract.Spendable_balance.mem c contract >>= fun exists -> - error_when - (self_delegation && not exists) - (Empty_delegate_account delegate) - >>?= fun () -> - Contract_storage.get_balance_and_frozen_bonds c contract - >>=? fun balance_and_frozen_bonds -> - Stake_storage.remove_contract_stake c contract balance_and_frozen_bonds - >>=? fun c -> - Contract_delegate_storage.set c contract delegate >>=? fun c -> - Stake_storage.add_stake c delegate balance_and_frozen_bonds - >>=? fun c -> - if self_delegation then - Storage.Delegates.add c delegate >>= fun c -> set_active c delegate - else return c +let manager_pubkey ctxt pkh = + Contract_manager_storage.get_manager_key ctxt pkh ~error:(Not_registered pkh) let frozen_deposits_limit ctxt delegate = Storage.Contract.Frozen_deposits_limit.find @@ -281,423 +84,17 @@ let set_frozen_deposits_limit ctxt delegate limit = (Contract_repr.Implicit delegate) limit -let update_activity ctxt last_cycle = - let preserved = Constants_storage.preserved_cycles ctxt in - match Cycle_repr.sub last_cycle preserved with - | None -> return (ctxt, []) - | Some _unfrozen_cycle -> - Stake_storage.fold_on_active_delegates_with_rolls - ctxt - ~order:`Sorted - ~init:(Ok (ctxt, [])) - ~f:(fun delegate () acc -> - acc >>?= fun (ctxt, deactivated) -> - Delegate_activation_storage.last_cycle_before_deactivation - ctxt - delegate - >>=? fun cycle -> - if Cycle_repr.(cycle <= last_cycle) then - set_inactive ctxt delegate >|=? fun ctxt -> - (ctxt, delegate :: deactivated) - else return (ctxt, deactivated)) - >|=? fun (ctxt, deactivated) -> (ctxt, deactivated) - -let expected_slots_for_given_active_stake ctxt ~total_active_stake ~active_stake - = - let blocks_per_cycle = - Int32.to_int (Constants_storage.blocks_per_cycle ctxt) - in - let consensus_committee_size = - Constants_storage.consensus_committee_size ctxt - in - let number_of_endorsements_per_cycle = - blocks_per_cycle * consensus_committee_size - in - Result.return - (Z.to_int - (Z.div - (Z.mul - (Z.of_int64 (Tez_repr.to_mutez active_stake)) - (Z.of_int number_of_endorsements_per_cycle)) - (Z.of_int64 (Tez_repr.to_mutez total_active_stake)))) - -let delegate_participated_enough ctxt delegate = - Storage.Contract.Missed_endorsements.find ctxt delegate >>=? function - | None -> return_true - | Some missed_endorsements -> - return Compare.Int.(missed_endorsements.remaining_slots >= 0) - -let delegate_has_revealed_nonces delegate unrevelead_nonces_set = - not (Signature.Public_key_hash.Set.mem delegate unrevelead_nonces_set) - -let distribute_endorsing_rewards ctxt last_cycle unrevealed_nonces = - let endorsing_reward_per_slot = - Constants_storage.endorsing_reward_per_slot ctxt - in - let unrevealed_nonces_set = - List.fold_left - (fun set {Storage.Seed.nonce_hash = _; delegate} -> - Signature.Public_key_hash.Set.add delegate set) - Signature.Public_key_hash.Set.empty - unrevealed_nonces - in - Stake_storage.get_total_active_stake ctxt last_cycle - >>=? fun total_active_stake -> - Stake_storage.get_selected_distribution ctxt last_cycle >>=? fun delegates -> - List.fold_left_es - (fun (ctxt, balance_updates) (delegate, active_stake) -> - let delegate_contract = Contract_repr.Implicit delegate in - delegate_participated_enough ctxt delegate_contract - >>=? fun sufficient_participation -> - let has_revealed_nonces = - delegate_has_revealed_nonces delegate unrevealed_nonces_set - in - expected_slots_for_given_active_stake - ctxt - ~total_active_stake - ~active_stake - >>?= fun expected_slots -> - let rewards = Tez_repr.mul_exn endorsing_reward_per_slot expected_slots in - (if sufficient_participation && has_revealed_nonces then - (* Sufficient participation: we pay the rewards *) - Token.transfer - ctxt - `Endorsing_rewards - (`Contract delegate_contract) - rewards - >|=? fun (ctxt, payed_rewards_receipts) -> - (ctxt, payed_rewards_receipts @ balance_updates) - else - (* Insufficient participation or unrevealed nonce: no rewards *) - Token.transfer - ctxt - `Endorsing_rewards - (`Lost_endorsing_rewards - (delegate, not sufficient_participation, not has_revealed_nonces)) - rewards - >|=? fun (ctxt, payed_rewards_receipts) -> - (ctxt, payed_rewards_receipts @ balance_updates)) - >>=? fun (ctxt, balance_updates) -> - Storage.Contract.Missed_endorsements.remove ctxt delegate_contract - >>= fun ctxt -> return (ctxt, balance_updates)) - (ctxt, []) - delegates - -let clear_outdated_slashed_deposits ctxt ~new_cycle = - let max_slashable_period = Constants_storage.max_slashing_period ctxt in - match Cycle_repr.(sub new_cycle max_slashable_period) with - | None -> Lwt.return ctxt - | Some outdated_cycle -> Storage.Slashed_deposits.clear (ctxt, outdated_cycle) - -(* Return a map from delegates (with active stake at some cycle - in the cycle window [from_cycle, to_cycle]) to the maximum - of the stake to be deposited for each such cycle (which is just the - [frozen_deposits_percentage] of the active stake at that cycle). Also - return the delegates that have fallen out of the sliding window. *) -let max_frozen_deposits_and_delegates_to_remove ctxt ~from_cycle ~to_cycle = - let frozen_deposits_percentage = - Constants_storage.frozen_deposits_percentage ctxt - in - let cycles = Cycle_repr.(from_cycle ---> to_cycle) in - (match Cycle_repr.pred from_cycle with - | None -> return Signature.Public_key_hash.Set.empty - | Some cleared_cycle -> ( - Stake_storage.find_selected_distribution ctxt cleared_cycle - >|=? fun cleared_cycle_delegates -> - match cleared_cycle_delegates with - | None -> Signature.Public_key_hash.Set.empty - | Some delegates -> - List.fold_left - (fun set (d, _) -> Signature.Public_key_hash.Set.add d set) - Signature.Public_key_hash.Set.empty - delegates)) - >>=? fun cleared_cycle_delegates -> - List.fold_left_es - (fun (maxima, delegates_to_remove) (cycle : Cycle_repr.t) -> - Stake_storage.get_selected_distribution ctxt cycle - >|=? fun active_stakes -> - List.fold_left - (fun (maxima, delegates_to_remove) (delegate, stake) -> - let stake_to_be_deposited = - Tez_repr.(div_exn (mul_exn stake frozen_deposits_percentage) 100) - in - let maxima = - Signature.Public_key_hash.Map.update - delegate - (function - | None -> Some stake_to_be_deposited - | Some maximum -> - Some (Tez_repr.max maximum stake_to_be_deposited)) - maxima - in - let delegates_to_remove = - Signature.Public_key_hash.Set.remove delegate delegates_to_remove - in - (maxima, delegates_to_remove)) - (maxima, delegates_to_remove) - active_stakes) - (Signature.Public_key_hash.Map.empty, cleared_cycle_delegates) - cycles - -let freeze_deposits ?(origin = Receipt_repr.Block_application) ctxt ~new_cycle - ~balance_updates = - let max_slashable_period = Constants_storage.max_slashing_period ctxt in - (* We want to be able to slash for at most [max_slashable_period] *) - (match Cycle_repr.(sub new_cycle (max_slashable_period - 1)) with - | None -> - Storage.Tenderbake.First_level_of_protocol.get ctxt - >>=? fun first_level_of_protocol -> - let cycle_eras = Raw_context.cycle_eras ctxt in - let level = - Level_repr.level_from_raw ~cycle_eras first_level_of_protocol - in - return level.cycle - | Some cycle -> return cycle) - >>=? fun from_cycle -> - let preserved_cycles = Constants_storage.preserved_cycles ctxt in - let to_cycle = Cycle_repr.(add new_cycle preserved_cycles) in - max_frozen_deposits_and_delegates_to_remove ctxt ~from_cycle ~to_cycle - >>=? fun (maxima, delegates_to_remove) -> - Signature.Public_key_hash.Map.fold_es - (fun delegate maximum_stake_to_be_deposited (ctxt, balance_updates) -> - (* Here we make sure to preserve the following invariant : - maximum_stake_to_be_deposited <= frozen_deposits + balance - See select_distribution_for_cycle *) - let delegate_contract = Contract_repr.Implicit delegate in - Frozen_deposits_storage.update_initial_amount - ctxt - delegate_contract - maximum_stake_to_be_deposited - >>=? fun ctxt -> - Frozen_deposits_storage.get ctxt delegate_contract >>=? fun deposits -> - let current_amount = deposits.current_amount in - if Tez_repr.(current_amount > maximum_stake_to_be_deposited) then - Tez_repr.(current_amount -? maximum_stake_to_be_deposited) - >>?= fun to_reimburse -> - Token.transfer - ~origin - ctxt - (`Frozen_deposits delegate) - (`Delegate_balance delegate) - to_reimburse - >|=? fun (ctxt, bupds) -> (ctxt, bupds @ balance_updates) - else if Tez_repr.(current_amount < maximum_stake_to_be_deposited) then - Tez_repr.(maximum_stake_to_be_deposited -? current_amount) - >>?= fun desired_to_freeze -> - Storage.Contract.Spendable_balance.get ctxt delegate_contract - >>=? fun balance -> - (* In case the delegate hasn't been slashed in this cycle, - the following invariant holds: - maximum_stake_to_be_deposited <= frozen_deposits + balance - See select_distribution_for_cycle - - If the delegate has been slashed during the cycle, the invariant - above doesn't necessarily hold. In this case, we freeze the max - we can for the delegate. *) - let to_freeze = Tez_repr.(min balance desired_to_freeze) in - Token.transfer - ~origin - ctxt - (`Delegate_balance delegate) - (`Frozen_deposits delegate) - to_freeze - >|=? fun (ctxt, bupds) -> (ctxt, bupds @ balance_updates) - else return (ctxt, balance_updates)) - maxima - (ctxt, balance_updates) - >>=? fun (ctxt, balance_updates) -> - (* Unfreeze deposits (that is, set them to zero) for delegates that - were previously in the relevant window (and therefore had some - frozen deposits) but are not in the new window; because that means - that such a delegate had no active stake in the relevant cycles, - and therefore it should have no frozen deposits. *) - Signature.Public_key_hash.Set.fold_es - (fun delegate (ctxt, balance_updates) -> - let delegate_contract = Contract_repr.Implicit delegate in - Frozen_deposits_storage.update_initial_amount - ctxt - delegate_contract - Tez_repr.zero - >>=? fun ctxt -> - Frozen_deposits_storage.get ctxt delegate_contract - >>=? fun frozen_deposits -> - if Tez_repr.(frozen_deposits.current_amount > zero) then - Token.transfer - ~origin - ctxt - (`Frozen_deposits delegate) - (`Delegate_balance delegate) - frozen_deposits.current_amount - >|=? fun (ctxt, bupds) -> (ctxt, bupds @ balance_updates) - else return (ctxt, balance_updates)) - delegates_to_remove - (ctxt, balance_updates) - -let freeze_deposits_do_not_call_except_for_migration = - freeze_deposits ~origin:Protocol_migration - -module Delegate_sampler_state = struct - module Cache_client = struct - type cached_value = - (Signature.Public_key.t * Signature.Public_key_hash.t) Sampler.t - - let namespace = Cache_repr.create_namespace "sampler_state" - - let cache_index = 2 - - let value_of_identifier ctxt identifier = - let cycle = Cycle_repr.of_string_exn identifier in - Storage.Delegate_sampler_state.get ctxt cycle - end - - module Cache = (val Cache_repr.register_exn (module Cache_client)) - - let identifier_of_cycle cycle = Format.asprintf "%a" Cycle_repr.pp cycle - - let init ctxt cycle sampler_state = - let id = identifier_of_cycle cycle in - Storage.Delegate_sampler_state.init ctxt cycle sampler_state - >>=? fun ctxt -> - let size = 1 (* that's symbolic: 1 cycle = 1 entry *) in - Cache.update ctxt id (Some (sampler_state, size)) >>?= fun ctxt -> - return ctxt - - let get ctxt cycle = - let id = identifier_of_cycle cycle in - Cache.find ctxt id >>=? function - | None -> Storage.Delegate_sampler_state.get ctxt cycle - | Some v -> return v - - let remove_existing ctxt cycle = - let id = identifier_of_cycle cycle in - Cache.update ctxt id None >>?= fun ctxt -> - Storage.Delegate_sampler_state.remove_existing ctxt cycle -end - -let get_stakes_for_selected_index ctxt index = - Stake_storage.fold_snapshot - ctxt - ~index - ~f:(fun (delegate, staking_balance) (acc, total_stake) -> - let delegate_contract = Contract_repr.Implicit delegate in - let open Tez_repr in - let open Lwt_result_syntax in - let* frozen_deposits_limit = - Storage.Contract.Frozen_deposits_limit.find ctxt delegate_contract - in - - let* balance_and_frozen_bonds = - Contract_storage.get_balance_and_frozen_bonds ctxt delegate_contract - in - let* frozen_deposits = - Frozen_deposits_storage.get ctxt delegate_contract - in - let*? total_balance = - balance_and_frozen_bonds +? frozen_deposits.current_amount - in - let* stake_for_cycle = - let frozen_deposits_percentage = - Int64.of_int @@ Constants_storage.frozen_deposits_percentage ctxt - in - let max_mutez = of_mutez_exn Int64.max_int in - let frozen_deposits_limit = - match frozen_deposits_limit with Some fdp -> fdp | None -> max_mutez - in - let aux = min total_balance frozen_deposits_limit in - let*? overflow_bound = max_mutez /? 100L in - if aux <= overflow_bound then - let*? aux = aux *? 100L in - let*? v = aux /? frozen_deposits_percentage in - return (min v staking_balance) - else - let*? sbal = staking_balance /? 100L in - let*? a = aux /? frozen_deposits_percentage in - if sbal <= a then return staking_balance - else - let*? r = max_mutez /? frozen_deposits_percentage in - return r - in - let*? total_stake = Tez_repr.(total_stake +? stake_for_cycle) in - return ((delegate, stake_for_cycle) :: acc, total_stake)) - ~init:([], Tez_repr.zero) - -let compute_snapshot_index_for_seed ~max_snapshot_index seed = - let rd = Seed_repr.initialize_new seed [Bytes.of_string "stake_snapshot"] in - let seq = Seed_repr.sequence rd 0l in - Seed_repr.take_int32 seq (Int32.of_int max_snapshot_index) - |> fst |> Int32.to_int |> return - -let compute_snapshot_index ctxt cycle ~max_snapshot_index = - Storage.Seed.For_cycle.get ctxt cycle >>=? fun seed -> - compute_snapshot_index_for_seed ~max_snapshot_index seed - -let select_distribution_for_cycle ctxt cycle = - Stake_storage.max_snapshot_index ctxt >>=? fun max_snapshot_index -> - Storage.Seed.For_cycle.get ctxt cycle >>=? fun seed -> - compute_snapshot_index_for_seed ~max_snapshot_index seed - >>=? fun selected_index -> - get_stakes_for_selected_index ctxt selected_index - >>=? fun (stakes, total_stake) -> - Stake_storage.set_selected_distribution_for_cycle - ctxt - cycle - stakes - total_stake - >>=? fun ctxt -> - List.fold_left_es - (fun acc (pkh, stake) -> - pubkey ctxt pkh >|=? fun pk -> ((pk, pkh), Tez_repr.to_mutez stake) :: acc) - [] - stakes - >>=? fun stakes_pk -> - let state = Sampler.create stakes_pk in - Delegate_sampler_state.init ctxt cycle state >>=? fun ctxt -> - (* pre-allocate the sampler *) - Lwt.return (Raw_context.init_sampler_for_cycle ctxt cycle seed state) - -let select_new_distribution_at_cycle_end ctxt ~new_cycle = - let preserved = Constants_storage.preserved_cycles ctxt in - let for_cycle = Cycle_repr.add new_cycle preserved in - select_distribution_for_cycle ctxt for_cycle - -let clear_outdated_sampling_data ctxt ~new_cycle = - let max_slashing_period = Constants_storage.max_slashing_period ctxt in - match Cycle_repr.sub new_cycle max_slashing_period with - | None -> return ctxt - | Some outdated_cycle -> - Delegate_sampler_state.remove_existing ctxt outdated_cycle - >>=? fun ctxt -> - Storage.Seed.For_cycle.remove_existing ctxt outdated_cycle - -let cycle_end ctxt last_cycle unrevealed_nonces = - let new_cycle = Cycle_repr.add last_cycle 1 in - select_new_distribution_at_cycle_end ctxt ~new_cycle >>=? fun ctxt -> - clear_outdated_slashed_deposits ctxt ~new_cycle >>= fun ctxt -> - distribute_endorsing_rewards ctxt last_cycle unrevealed_nonces - >>=? fun (ctxt, balance_updates) -> - freeze_deposits ctxt ~new_cycle ~balance_updates - >>=? fun (ctxt, balance_updates) -> - Stake_storage.clear_at_cycle_end ctxt ~new_cycle >>=? fun ctxt -> - clear_outdated_sampling_data ctxt ~new_cycle >>=? fun ctxt -> - update_activity ctxt last_cycle >>=? fun (ctxt, deactivated_delagates) -> - return (ctxt, balance_updates, deactivated_delagates) +let frozen_deposits ctxt delegate = + Frozen_deposits_storage.get ctxt (Contract_repr.Implicit delegate) let balance ctxt delegate = let contract = Contract_repr.Implicit delegate in Storage.Contract.Spendable_balance.get ctxt contract -let frozen_deposits ctxt delegate = - Frozen_deposits_storage.get ctxt (Contract_repr.Implicit delegate) - -let full_balance ctxt delegate = - frozen_deposits ctxt delegate >>=? fun frozen_deposits -> - let delegate_contract = Contract_repr.Implicit delegate in - Contract_storage.get_balance_and_frozen_bonds ctxt delegate_contract - >>=? fun balance_and_frozen_bonds -> - Lwt.return - Tez_repr.(frozen_deposits.current_amount +? balance_and_frozen_bonds) - -let deactivated = Delegate_activation_storage.is_inactive +let staking_balance ctxt delegate = + registered ctxt delegate >>= fun is_registered -> + if is_registered then Stake_storage.get_staking_balance ctxt delegate + else return Tez_repr.zero let delegated_balance ctxt delegate = staking_balance ctxt delegate >>=? fun staking_balance -> @@ -707,347 +104,247 @@ let delegated_balance ctxt delegate = >>?= fun self_staking_balance -> Lwt.return Tez_repr.(staking_balance -? self_staking_balance) -let fold = Storage.Delegates.fold - -let list = Storage.Delegates.elements - -(* The fact that this succeeds iff [registered ctxt pkh] returns true is an - invariant of the [set] function. *) -let check_delegate ctxt pkh = - Storage.Delegates.mem ctxt pkh >>= function - | true -> return_unit - | false -> fail (Not_registered pkh) - -module Random = struct - (* [init_random_state] initialize a random sequence drawing state - that's unique for a given (seed, level, index) triple. Elements - from this sequence are drawn using [take_int64], updating the - state for the next draw. The initial state is the Blake2b hash of - the three randomness sources, and an offset set to zero - (indicating that zero bits of randomness have been - consumed). When drawing random elements, bits are extracted from - the state until exhaustion (256 bits), at which point the state - is rehashed and the offset reset to 0. *) - - let init_random_state seed level index = - ( Raw_hashes.blake2b - (Data_encoding.Binary.to_bytes_exn - Data_encoding.(tup3 Seed_repr.seed_encoding int32 int32) - (seed, level.Level_repr.cycle_position, Int32.of_int index)), - 0 ) +let full_balance ctxt delegate = + frozen_deposits ctxt delegate >>=? fun frozen_deposits -> + let delegate_contract = Contract_repr.Implicit delegate in + Contract_storage.get_balance_and_frozen_bonds ctxt delegate_contract + >>=? fun balance_and_frozen_bonds -> + Lwt.return + Tez_repr.(frozen_deposits.current_amount +? balance_and_frozen_bonds) - let take_int64 bound state = - let drop_if_over = - (* This function draws random values in [0-(bound-1)] by drawing - in [0-(2^63-1)] (64-bit) and computing the value modulo - [bound]. For the application of [mod bound] to preserve - uniformity, the input space must be of the form - [0-(n*bound-1)]. We enforce this by rejecting 64-bit samples - above this limit (in which case, we draw a new 64-sample from - the sequence and try again). *) - Int64.sub Int64.max_int (Int64.rem Int64.max_int bound) +(** This module ensures the following invariants: + - registered delegates are self-delegated and appears in `Storage.Delegate`, + i.e. registered delegate cannot change their delegation + - stake is properly moved when changing delegation. +*) +module Contract = struct + type error += + | (* `Permanent *) Unregistered_delegate of Signature.Public_key_hash.t + + let () = + (* Unregistered delegate *) + register_error_kind + `Permanent + ~id:"contract.manager.unregistered_delegate" + ~title:"Unregistered delegate" + ~description:"A contract cannot be delegated to an unregistered delegate" + ~pp:(fun ppf k -> + Format.fprintf + ppf + "The provided public key (with hash %a) is not registered as valid \ + delegate key." + Signature.Public_key_hash.pp + k) + Data_encoding.(obj1 (req "hash" Signature.Public_key_hash.encoding)) + (function Unregistered_delegate k -> Some k | _ -> None) + (fun k -> Unregistered_delegate k) + + let init ctxt contract delegate = + Contract_manager_storage.is_manager_key_revealed ctxt delegate + >>=? fun known_delegate -> + error_unless known_delegate (Unregistered_delegate delegate) >>?= fun () -> + registered ctxt delegate >>= fun is_registered -> + error_unless is_registered (Unregistered_delegate delegate) >>?= fun () -> + Contract_delegate_storage.init ctxt contract delegate >>=? fun ctxt -> + Contract_storage.get_balance_and_frozen_bonds ctxt contract + >>=? fun balance_and_frozen_bonds -> + Stake_storage.add_stake ctxt delegate balance_and_frozen_bonds + + type error += + | (* `Temporary *) Active_delegate + | (* `Permanent *) Empty_delegate_account of Signature.Public_key_hash.t + + let () = + register_error_kind + `Temporary + ~id:"delegate.already_active" + ~title:"Delegate already active" + ~description:"Useless delegate reactivation" + ~pp:(fun ppf () -> + Format.fprintf ppf "The delegate is still active, no need to refresh it") + Data_encoding.empty + (function Active_delegate -> Some () | _ -> None) + (fun () -> Active_delegate) ; + register_error_kind + `Permanent + ~id:"delegate.empty_delegate_account" + ~title:"Empty delegate account" + ~description: + "Cannot register a delegate when its implicit account is empty" + ~pp:(fun ppf delegate -> + Format.fprintf + ppf + "Delegate registration is forbidden when the delegate\n\ + \ implicit account is empty (%a)" + Signature.Public_key_hash.pp + delegate) + Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) + (function Empty_delegate_account c -> Some c | _ -> None) + (fun c -> Empty_delegate_account c) + + let set_self_delegate c delegate = + let open Lwt_tzresult_syntax in + let*! is_registered = registered c delegate in + if is_registered then + let* () = + let* is_inactive = Delegate_activation_storage.is_inactive c delegate in + fail_unless is_inactive Active_delegate + in + Stake_storage.set_active c delegate + else + let contract = Contract_repr.Implicit delegate in + let* pk = + Contract_manager_storage.get_manager_key + c + ~error:(Unregistered_delegate delegate) + delegate + in + let* () = + let*! is_allocated = Contract_storage.allocated c contract in + fail_unless is_allocated (Empty_delegate_account delegate) + in + let* balance_and_frozen_bonds = + Contract_storage.get_balance_and_frozen_bonds c contract + in + let* c = + Stake_storage.remove_contract_stake c contract balance_and_frozen_bonds + in + let* c = Contract_delegate_storage.set c contract delegate in + let* c = Stake_storage.add_stake c delegate balance_and_frozen_bonds in + let*! c = Storage.Delegates.add c delegate in + let* c = Delegate_consensus_key.init c delegate pk in + let* c = Stake_storage.set_active c delegate in + return c + + type error += + | (* `Permanent *) No_deletion of Signature.Public_key_hash.t + | (* `Temporary *) Current_delegate + + let () = + register_error_kind + `Permanent + ~id:"delegate.no_deletion" + ~title:"Forbidden delegate deletion" + ~description:"Tried to unregister a delegate" + ~pp:(fun ppf delegate -> + Format.fprintf + ppf + "Delegate deletion is forbidden (%a)" + Signature.Public_key_hash.pp + delegate) + Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) + (function No_deletion c -> Some c | _ -> None) + (fun c -> No_deletion c) ; + register_error_kind + `Temporary + ~id:"delegate.unchanged" + ~title:"Unchanged delegated" + ~description:"Contract already delegated to the given delegate" + ~pp:(fun ppf () -> + Format.fprintf + ppf + "The contract is already delegated to the same delegate") + Data_encoding.empty + (function Current_delegate -> Some () | _ -> None) + (fun () -> Current_delegate) + + let set_delegate c contract delegate = + let open Lwt_tzresult_syntax in + let* () = + match contract with + | Contract_repr.Originated _ -> return_unit + | Implicit pkh -> + let*! is_registered = registered c pkh in + fail_when is_registered (No_deletion pkh) in - let rec loop (bytes, n) = - let consumed_bytes = 8 in - let state_size = Bytes.length bytes in - if Compare.Int.(n > state_size - consumed_bytes) then - loop (Raw_hashes.blake2b bytes, 0) - else - let r = TzEndian.get_int64 bytes n in - (* The absolute value of min_int is min_int. Also, every - positive integer is represented twice (positive and negative), - but zero is only represented once. We fix both problems at - once. *) - let r = if Compare.Int64.(r = Int64.min_int) then 0L else Int64.abs r in - if Compare.Int64.(r >= drop_if_over) then - loop (bytes, n + consumed_bytes) - else - let v = Int64.rem r bound in - (v, (bytes, n + consumed_bytes)) + let* () = + let* current_delegate = Contract_delegate_storage.find c contract in + match (delegate, current_delegate) with + | None, None -> + (* we don't fail in this case in order not to risk breaking + existing smart contracts. *) + return_unit + | Some delegate, Some current_delegate + when Signature.Public_key_hash.equal delegate current_delegate -> + fail Current_delegate + | _ -> return_unit in - loop state - - (** [sampler_for_cycle ctxt cycle] reads the sampler for [cycle] from - [ctxt] if it has been previously inited. Otherwise it initializes - the sampler and caches it in [ctxt] with - [Raw_context.set_sampler_for_cycle]. *) - let sampler_for_cycle ctxt cycle = - let read ctxt = - Storage.Seed.For_cycle.get ctxt cycle >>=? fun seed -> - Delegate_sampler_state.get ctxt cycle >>=? fun state -> - return (seed, state) + let* balance_and_frozen_bonds = + Contract_storage.get_balance_and_frozen_bonds c contract in - Raw_context.sampler_for_cycle ~read ctxt cycle - - let owner c (level : Level_repr.t) offset = - let cycle = level.Level_repr.cycle in - sampler_for_cycle c cycle >>=? fun (c, seed, state) -> - let sample ~int_bound ~mass_bound = - let state = init_random_state seed level offset in - let i, state = take_int64 (Int64.of_int int_bound) state in - let elt, _ = take_int64 mass_bound state in - (Int64.to_int i, elt) + let* c = + Stake_storage.remove_contract_stake c contract balance_and_frozen_bonds in - let pk, pkh = Sampler.sample state sample in - return (c, (pk, pkh)) + match delegate with + | None -> + let* c = Contract_delegate_storage.delete c contract in + return c + | Some delegate -> + let* () = + let*! is_delegate_registered = registered c delegate in + fail_when + (not is_delegate_registered) + (Unregistered_delegate delegate) + in + let* c = Contract_delegate_storage.set c contract delegate in + let* c = Stake_storage.add_stake c delegate balance_and_frozen_bonds in + return c + + let set c contract delegate = + match (delegate, contract) with + | Some delegate, Contract_repr.Implicit source + when Signature.Public_key_hash.equal source delegate -> + set_self_delegate c delegate + | _ -> set_delegate c contract delegate end -let slot_owner c level slot = Random.owner c level (Slot_repr.to_int slot) - -let baking_rights_owner c (level : Level_repr.t) ~round = - Round_repr.to_int round >>?= fun round -> - let consensus_committee_size = Constants_storage.consensus_committee_size c in - Slot_repr.of_int (round mod consensus_committee_size) >>?= fun slot -> - slot_owner c level slot >>=? fun (ctxt, pk) -> return (ctxt, slot, pk) - -let already_slashed_for_double_endorsing ctxt delegate (level : Level_repr.t) = - Storage.Slashed_deposits.find (ctxt, level.cycle) (level.level, delegate) - >>=? function - | None -> return_false - | Some slashed -> return slashed.for_double_endorsing +type error += (* `Temporary *) Invalid_drain -let already_slashed_for_double_baking ctxt delegate (level : Level_repr.t) = - Storage.Slashed_deposits.find (ctxt, level.cycle) (level.level, delegate) - >>=? function - | None -> return_false - | Some slashed -> return slashed.for_double_baking - -let punish_double_endorsing ctxt delegate (level : Level_repr.t) = - let delegate_contract = Contract_repr.Implicit delegate in - Frozen_deposits_storage.get ctxt delegate_contract >>=? fun frozen_deposits -> - let slashing_ratio : Ratio_repr.t = - Constants_storage.ratio_of_frozen_deposits_slashed_per_double_endorsement - ctxt - in - let punish_value = - Tez_repr.( - div_exn - (mul_exn frozen_deposits.initial_amount slashing_ratio.numerator) - slashing_ratio.denominator) - in - let amount_to_burn = - Tez_repr.(min frozen_deposits.current_amount punish_value) - in - Token.transfer - ctxt - (`Frozen_deposits delegate) - `Double_signing_punishments - amount_to_burn - >>=? fun (ctxt, balance_updates) -> - Stake_storage.remove_stake ctxt delegate amount_to_burn >>=? fun ctxt -> - Storage.Slashed_deposits.find (ctxt, level.cycle) (level.level, delegate) - >>=? fun slashed -> - let slashed : Storage.slashed_level = - match slashed with - | None -> {for_double_endorsing = true; for_double_baking = false} - | Some slashed -> - assert (Compare.Bool.(slashed.for_double_endorsing = false)) ; - {slashed with for_double_endorsing = true} - in - Storage.Slashed_deposits.add - (ctxt, level.cycle) - (level.level, delegate) - slashed - >>= fun ctxt -> return (ctxt, amount_to_burn, balance_updates) - -let punish_double_baking ctxt delegate (level : Level_repr.t) = - let delegate_contract = Contract_repr.Implicit delegate in - Frozen_deposits_storage.get ctxt delegate_contract >>=? fun frozen_deposits -> - let slashing_for_one_block = - Constants_storage.double_baking_punishment ctxt - in - let amount_to_burn = - Tez_repr.(min frozen_deposits.current_amount slashing_for_one_block) +let () = + register_error_kind + `Temporary + ~id:"delegate.invalid_drain" + ~title:"Invalid Drain operation" + ~description: + "The drain operation is not signed by the active consensus key." + ~pp:(fun ppf () -> + Format.fprintf + ppf + "The drain operation is invalid: not signed by the active consensus key") + Data_encoding.empty + (function Invalid_drain -> Some () | _ -> None) + (fun () -> Invalid_drain) + +let drain_fees manager_balance = + let open Result_syntax in + let* one_percent = Tez_repr.(manager_balance /? 100L) in + return Tez_repr.(min one one_percent) + +let drain ctxt ~delegate ~destination = + let open Lwt_tzresult_syntax in + let*! is_destination_allocated = + Contract_storage.allocated ctxt (Contract_repr.Implicit destination) in - Token.transfer - ctxt - (`Frozen_deposits delegate) - `Double_signing_punishments - amount_to_burn - >>=? fun (ctxt, balance_updates) -> - Stake_storage.remove_stake ctxt delegate amount_to_burn >>=? fun ctxt -> - Storage.Slashed_deposits.find (ctxt, level.cycle) (level.level, delegate) - >>=? fun slashed -> - let slashed : Storage.slashed_level = - match slashed with - | None -> {for_double_endorsing = false; for_double_baking = true} - | Some slashed -> - assert (Compare.Bool.(slashed.for_double_baking = false)) ; - {slashed with for_double_baking = true} + let* ctxt, _, balance_updates1 = + if not is_destination_allocated then + Fees_storage.burn_origination_fees + ctxt + ~storage_limit:(Z.of_int (Constants_storage.origination_size ctxt)) + ~payer:(`Delegate_balance delegate) + else return (ctxt, Z.zero, []) in - Storage.Slashed_deposits.add - (ctxt, level.cycle) - (level.level, delegate) - slashed - >>= fun ctxt -> return (ctxt, amount_to_burn, balance_updates) - -type level_participation = Participated | Didn't_participate - -(* Note that the participation for the last block of a cycle is - recorded in the next cycle. *) -let record_endorsing_participation ctxt ~delegate ~participation - ~endorsing_power = - match participation with - | Participated -> set_active ctxt delegate - | Didn't_participate -> ( - let contract = Contract_repr.Implicit delegate in - Storage.Contract.Missed_endorsements.find ctxt contract >>=? function - | Some {remaining_slots; missed_levels} -> - let remaining_slots = remaining_slots - endorsing_power in - Storage.Contract.Missed_endorsements.update - ctxt - contract - {remaining_slots; missed_levels = missed_levels + 1} - | None -> ( - let level = Level_storage.current ctxt in - Raw_context.stake_distribution_for_current_cycle ctxt - >>?= fun stake_distribution -> - match - Signature.Public_key_hash.Map.find delegate stake_distribution - with - | None -> - (* This happens when the block is the first one in a - cycle, and therefore the endorsements are for the last - block of the previous cycle, and when the delegate does - not have an active stake at the current cycle; in this - case its participation is simply ignored. *) - assert (Compare.Int32.(level.cycle_position = 0l)) ; - return ctxt - | Some active_stake -> - Stake_storage.get_total_active_stake ctxt level.cycle - >>=? fun total_active_stake -> - expected_slots_for_given_active_stake - ctxt - ~total_active_stake - ~active_stake - >>?= fun expected_slots -> - let Ratio_repr.{numerator; denominator} = - Constants_storage.minimal_participation_ratio ctxt - in - let minimal_activity = expected_slots * numerator / denominator in - let maximal_inactivity = expected_slots - minimal_activity in - let remaining_slots = maximal_inactivity - endorsing_power in - Storage.Contract.Missed_endorsements.init - ctxt - contract - {remaining_slots; missed_levels = 1})) - -let record_baking_activity_and_pay_rewards_and_fees ctxt ~payload_producer - ~block_producer ~baking_reward ~reward_bonus = - set_active ctxt payload_producer >>=? fun ctxt -> - (if not (Signature.Public_key_hash.equal payload_producer block_producer) then - set_active ctxt block_producer - else return ctxt) - >>=? fun ctxt -> - let pay_payload_producer ctxt delegate = - let contract = Contract_repr.Implicit delegate in - Token.balance ctxt `Block_fees >>=? fun (ctxt, block_fees) -> - Token.transfer_n + let* manager_balance = balance ctxt delegate in + let*? fees = drain_fees manager_balance in + let*? transfered = Tez_repr.(manager_balance -? fees) in + let* ctxt, balance_updates2 = + Token.transfer ctxt - [(`Block_fees, block_fees); (`Baking_rewards, baking_reward)] - (`Contract contract) - in - let pay_block_producer ctxt delegate bonus = - let contract = Contract_repr.Implicit delegate in - Token.transfer ctxt `Baking_bonuses (`Contract contract) bonus + (`Delegate_balance delegate) + (`Contract (Contract_repr.Implicit destination)) + transfered in - pay_payload_producer ctxt payload_producer - >>=? fun (ctxt, balance_updates_payload_producer) -> - (match reward_bonus with - | Some bonus -> pay_block_producer ctxt block_producer bonus - | None -> return (ctxt, [])) - >>=? fun (ctxt, balance_updates_block_producer) -> return - (ctxt, balance_updates_payload_producer @ balance_updates_block_producer) - -type participation_info = { - expected_cycle_activity : int; - minimal_cycle_activity : int; - missed_slots : int; - missed_levels : int; - remaining_allowed_missed_slots : int; - expected_endorsing_rewards : Tez_repr.t; -} - -(* Inefficient, only for RPC *) -let delegate_participation_info ctxt delegate = - let level = Level_storage.current ctxt in - Stake_storage.get_selected_distribution ctxt level.cycle - >>=? fun stake_distribution -> - match - List.assoc_opt - ~equal:Signature.Public_key_hash.equal - delegate - stake_distribution - with - | None -> - (* delegate does not have an active stake at the current cycle *) - return - { - expected_cycle_activity = 0; - minimal_cycle_activity = 0; - missed_slots = 0; - missed_levels = 0; - remaining_allowed_missed_slots = 0; - expected_endorsing_rewards = Tez_repr.zero; - } - | Some active_stake -> - Stake_storage.get_total_active_stake ctxt level.cycle - >>=? fun total_active_stake -> - expected_slots_for_given_active_stake - ctxt - ~total_active_stake - ~active_stake - >>?= fun expected_cycle_activity -> - let Ratio_repr.{numerator; denominator} = - Constants_storage.minimal_participation_ratio ctxt - in - let endorsing_reward_per_slot = - Constants_storage.endorsing_reward_per_slot ctxt - in - let minimal_cycle_activity = - expected_cycle_activity * numerator / denominator - in - let maximal_cycle_inactivity = - expected_cycle_activity - minimal_cycle_activity - in - let expected_endorsing_rewards = - Tez_repr.mul_exn endorsing_reward_per_slot expected_cycle_activity - in - let contract = Contract_repr.Implicit delegate in - Storage.Contract.Missed_endorsements.find ctxt contract - >>=? fun missed_endorsements -> - let missed_slots, missed_levels, remaining_allowed_missed_slots = - match missed_endorsements with - | None -> (0, 0, maximal_cycle_inactivity) - | Some {remaining_slots; missed_levels} -> - ( maximal_cycle_inactivity - remaining_slots, - missed_levels, - Compare.Int.max 0 remaining_slots ) - in - let expected_endorsing_rewards = - match missed_endorsements with - | Some r when Compare.Int.(r.remaining_slots < 0) -> Tez_repr.zero - | _ -> expected_endorsing_rewards - in - return - { - expected_cycle_activity; - minimal_cycle_activity; - missed_slots; - missed_levels; - remaining_allowed_missed_slots; - expected_endorsing_rewards; - } - -let init_first_cycles ctxt = - let preserved = Constants_storage.preserved_cycles ctxt in - List.fold_left_es - (fun ctxt c -> - let cycle = Cycle_repr.of_int32_exn (Int32.of_int c) in - Stake_storage.snapshot ctxt >>=? fun ctxt -> - (* NB: we need to take several snapshots because - select_distribution_for_cycle deletes the snapshots *) - select_distribution_for_cycle ctxt cycle) - ctxt - Misc.(0 --> preserved) + ( ctxt, + not is_destination_allocated, + fees, + balance_updates1 @ balance_updates2 ) diff --git a/src/proto_alpha/lib_protocol/delegate_storage.mli b/src/proto_alpha/lib_protocol/delegate_storage.mli index cba3a98d9746d652c2f235d4fcc5af48f36bf48d..2b84244ce947b4419218e8133de69c2f99ddfa53 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_storage.mli @@ -3,6 +3,7 @@ (* Open Source License *) (* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) (* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -24,92 +25,15 @@ (* *) (*****************************************************************************) -(** Allow to register a delegate when creating an account. *) -val init : - Raw_context.t -> - Contract_repr.t -> - Signature.Public_key_hash.t -> - Raw_context.t tzresult Lwt.t - -val pubkey : - Raw_context.t -> - Signature.Public_key_hash.t -> - Signature.Public_key.t tzresult Lwt.t - -(** Updating the delegate of a contract. - - When calling this function on an "implicit contract" and setting - the delegate to the contract manager registers it as a delegate. One - cannot unregister a delegate for now. The associate contract is now - 'undeletable'. *) -val set : - Raw_context.t -> - Contract_repr.t -> - Signature.Public_key_hash.t option -> - Raw_context.t tzresult Lwt.t - -val frozen_deposits_limit : - Raw_context.t -> - Signature.Public_key_hash.t -> - Tez_repr.t option tzresult Lwt.t - -val set_frozen_deposits_limit : - Raw_context.t -> - Signature.Public_key_hash.t -> - Tez_repr.t option -> - Raw_context.t Lwt.t +type error += (* `Temporary *) Not_registered of Signature.Public_key_hash.t -type error += - | (* `Permanent *) No_deletion of Signature.Public_key_hash.t - | (* `Temporary *) Active_delegate - | (* `Temporary *) Current_delegate - | (* `Permanent *) Empty_delegate_account of Signature.Public_key_hash.t - | (* `Permanent *) Unregistered_delegate of Signature.Public_key_hash.t - | (* `Permanent *) Unassigned_validation_slot_for_level of Level_repr.t * int - | (* `Permanent *) - Cannot_find_active_stake of { - cycle : Cycle_repr.t; - delegate : Signature.Public_key_hash.t; - } - | (* `Temporary *) Not_registered of Signature.Public_key_hash.t +(** Has a delegate been registered in the delegate table? *) +val registered : Raw_context.t -> Signature.Public_key_hash.t -> bool Lwt.t (** Check that a given implicit account is a registered delegate. *) -val check_delegate : +val check_registered : Raw_context.t -> Signature.Public_key_hash.t -> unit tzresult Lwt.t -(** Participation information. We denote by: - - "static" information that does not change during the cycle - - "dynamic" information that may change during the cycle *) -type participation_info = { - expected_cycle_activity : int; - (** The total expected slots to be endorsed in the cycle. (static) *) - minimal_cycle_activity : int; - (** The minimal endorsing slots in the cycle to get endorsing - rewards. (static) *) - missed_slots : int; - (** The number of missed endorsing slots in the cycle. (dynamic) *) - missed_levels : int; - (** The number of missed endorsing levels in the cycle. (dynamic) *) - remaining_allowed_missed_slots : int; - (** Remaining amount of endorsing slots that can be missed in the - cycle before forfeiting the rewards. (dynamic) *) - expected_endorsing_rewards : Tez_repr.t; - (** Endorsing rewards that will be distributed at the end of the - cycle if activity at that point will be greater than the minimal - required. If the activity is already known to be below the - required minimum, then the rewards are zero. (dynamic) *) -} - -(** Only use this function for RPC: this is expensive. - - [delegate_participation_info] and [!val:check_delegate] forms the - implementation of RPC call "/context/delegates//participation". - *) -val delegate_participation_info : - Raw_context.t -> - Signature.Public_key_hash.t -> - participation_info tzresult Lwt.t - (** Iterate on all registered delegates. *) val fold : Raw_context.t -> @@ -121,85 +45,35 @@ val fold : (** List all registered delegates. *) val list : Raw_context.t -> Signature.Public_key_hash.t list Lwt.t -val balance : - Raw_context.t -> Signature.public_key_hash -> Tez_repr.tez tzresult Lwt.t - -type level_participation = Participated | Didn't_participate - -(** Record the participation of a delegate as a validator. *) -val record_endorsing_participation : - Raw_context.t -> - delegate:Signature.Public_key_hash.t -> - participation:level_participation -> - endorsing_power:int -> - Raw_context.t tzresult Lwt.t - -(** Sets the payload and block producer as active. Pays the baking - reward and the fees to the payload producer and the reward bonus to - the payload producer (if the reward_bonus is not None).*) -val record_baking_activity_and_pay_rewards_and_fees : - Raw_context.t -> - payload_producer:Signature.Public_key_hash.t -> - block_producer:Signature.Public_key_hash.t -> - baking_reward:Tez_repr.t -> - reward_bonus:Tez_repr.t option -> - (Raw_context.t * Receipt_repr.balance_updates) tzresult Lwt.t - -(** Trigger the context maintenance at the end of cycle 'n', i.e.: - unfreeze the endorsing rewards, potentially deactivate delegates. - Return the corresponding balances updates and the list of - deactivated delegates. *) -val cycle_end : - Raw_context.t -> - Cycle_repr.t -> - Storage.Seed.unrevealed_nonce list -> - (Raw_context.t - * Receipt_repr.balance_updates - * Signature.Public_key_hash.t list) - tzresult - Lwt.t - -(** Returns true if the given delegate has already been slashed - for double baking for the given level. *) -val already_slashed_for_double_baking : - Raw_context.t -> - Signature.Public_key_hash.t -> - Level_repr.t -> - bool tzresult Lwt.t - -(** Returns true if the given delegate has already been slashed - for double preendorsing or double endorsing for the given level. *) -val already_slashed_for_double_endorsing : - Raw_context.t -> - Signature.Public_key_hash.t -> - Level_repr.t -> - bool tzresult Lwt.t - -(** Burn some frozen deposit for a delegate at a given level. Returns - the burned amount. *) -val punish_double_endorsing : +val frozen_deposits_limit : Raw_context.t -> Signature.Public_key_hash.t -> - Level_repr.t -> - (Raw_context.t * Tez_repr.t * Receipt_repr.balance_updates) tzresult Lwt.t + Tez_repr.t option tzresult Lwt.t -val punish_double_baking : +val set_frozen_deposits_limit : Raw_context.t -> Signature.Public_key_hash.t -> - Level_repr.t -> - (Raw_context.t * Tez_repr.t * Receipt_repr.balance_updates) tzresult Lwt.t + Tez_repr.t option -> + Raw_context.t Lwt.t (** Returns a delegate's frozen deposits, both the current amount and the initial freezed amount. A delegate's frozen balance is only composed of frozen deposits; - rewards and fees are not frozen, but simply credited at the right - moment. *) + rewards and fees are not frozen, but simply credited at the right + moment. *) val frozen_deposits : Raw_context.t -> Signature.Public_key_hash.t -> Storage.deposits tzresult Lwt.t +val staking_balance : + Raw_context.t -> Signature.Public_key_hash.t -> Tez_repr.t tzresult Lwt.t + +(** Only use this function for RPCs: this is expensive. *) +val delegated_balance : + Raw_context.t -> Signature.Public_key_hash.t -> Tez_repr.t tzresult Lwt.t + (** Returns the full 'balance' of the implicit contract associated to a given key, i.e. the sum of the spendable balance (given by [balance] or [Contract_storage.get_balance]) and of the frozen balance. The frozen @@ -211,55 +85,54 @@ val frozen_deposits : val full_balance : Raw_context.t -> Signature.Public_key_hash.t -> Tez_repr.t tzresult Lwt.t -val staking_balance : - Raw_context.t -> Signature.Public_key_hash.t -> Tez_repr.t tzresult Lwt.t - -(** Only use this function for RPCs: this is expensive. *) -val delegated_balance : - Raw_context.t -> Signature.Public_key_hash.t -> Tez_repr.t tzresult Lwt.t - -val deactivated : - Raw_context.t -> Signature.Public_key_hash.t -> bool tzresult Lwt.t - -(** Participation slots potentially associated to accounts. The - accounts that didn't place a deposit will be excluded from this - list. This function should only be used to compute the deposits to - freeze or initialize the protocol while stitching. RPCs can use this - function to predict an approximation of long term future slot - allocations. It shouldn't be used in the baker. *) -val slot_owner : +module Contract : sig + type error += + | (* `Permanent *) Unregistered_delegate of Signature.Public_key_hash.t + | (* `Temporary *) Active_delegate + | (* `Permanent *) Empty_delegate_account of Signature.Public_key_hash.t + | (* `Permanent *) No_deletion of Signature.Public_key_hash.t + | (* `Temporary *) Current_delegate + + (** [init ctxt contract delegate] registers a delegate when + creating a contract. + + This functions assumes that [contract] is allocated. + This function returns an error if [contract] already has a delegate or + if [delegate] is not a registered delegate. *) + val init : + Raw_context.t -> + Contract_repr.t -> + Signature.Public_key_hash.t -> + Raw_context.t tzresult Lwt.t + + (** [set ctxt contract delegate_opt] allows to set the + delegate of a contract to [delegate] when [delegate_opt = Some delegate] + or to unset the delegate when [delegate_opt = None]. + When [delegate_opt = Some contract] (aka self-delegation), + the function also registers the contract as a delegate and + sets the delegate as {{!module:Delegate_activation_storage}active}. + + It returns an error when trying to set the delegate to an unregistered delegate. + It returns an error when trying to unset or change the delegate of a registered delegate. + It returns an error when self-delegating and the delegate is not {{!Contract_storage.allocated}allocated}. + It returns an error when self-delegating and the delegate is already active. *) + val set : + Raw_context.t -> + Contract_repr.t -> + Signature.Public_key_hash.t option -> + Raw_context.t tzresult Lwt.t +end + +val manager_pubkey : Raw_context.t -> - Level_repr.t -> - Slot_repr.t -> - (Raw_context.t * (Signature.Public_key.t * Signature.Public_key_hash.t)) - tzresult - Lwt.t + Signature.Public_key_hash.t -> + Signature.Public_key.t tzresult Lwt.t -val baking_rights_owner : +val drain : Raw_context.t -> - Level_repr.t -> - round:Round_repr.round -> - (Raw_context.t - * Slot_repr.t - * (Signature.public_key * Signature.public_key_hash)) - tzresult + delegate:Signature.Public_key_hash.t -> + destination:Signature.Public_key_hash.t -> + (Raw_context.t * bool * Tez_repr.t * Receipt_repr.balance_updates) tzresult Lwt.t -val freeze_deposits_do_not_call_except_for_migration : - Raw_context.t -> - new_cycle:Cycle_repr.t -> - balance_updates:Receipt_repr.balance_updates -> - (Raw_context.t * Receipt_repr.balance_updates) tzresult Lwt.t - -(** [init_first_cycles ctxt] computes and records the distribution of the total - active stake among active delegates. This concerns the total active stake - involved in the calculation of baking rights for all cycles in the range - [0, preserved_cycles]. *) -val init_first_cycles : Raw_context.t -> Raw_context.t tzresult Lwt.t - -(** [compute_snapshot_index ctxt cycle max_snapshot_index] Returns the index of - the selected snapshot for the [cycle] passed as argument, and for the max - index of snapshots taken so far, [max_snapshot_index] (see - [Stake_storage.max_snapshot_index]. *) -val compute_snapshot_index : - Raw_context.t -> Cycle_repr.t -> max_snapshot_index:int -> int tzresult Lwt.t +val drain_fees : Tez_repr.t -> Tez_repr.t tzresult diff --git a/src/proto_alpha/lib_protocol/dune b/src/proto_alpha/lib_protocol/dune index 73761c300e8d123fe76ffe14f3e3337e7506eece..81a2f61ddd7857d8742c68d1b81e5d27bafb183d 100644 --- a/src/proto_alpha/lib_protocol/dune +++ b/src/proto_alpha/lib_protocol/dune @@ -138,10 +138,15 @@ Stake_storage Contract_storage Token + Fees_storage + Delegate_consensus_key Delegate_storage + Delegate_missed_endorsements_storage + Delegate_slashed_deposits_storage + Delegate_sampler + Delegate_cycles Bootstrap_storage Vote_storage - Fees_storage Ticket_storage Liquidity_baking_storage Liquidity_baking_cpmm @@ -351,10 +356,16 @@ stake_storage.ml stake_storage.mli contract_storage.ml contract_storage.mli token.ml token.mli + fees_storage.ml fees_storage.mli + delegate_consensus_key.ml delegate_consensus_key.mli delegate_storage.ml delegate_storage.mli + delegate_missed_endorsements_storage.ml + delegate_missed_endorsements_storage.mli + delegate_slashed_deposits_storage.ml delegate_slashed_deposits_storage.mli + delegate_sampler.ml delegate_sampler.mli + delegate_cycles.ml delegate_cycles.mli bootstrap_storage.ml bootstrap_storage.mli vote_storage.ml vote_storage.mli - fees_storage.ml fees_storage.mli ticket_storage.ml ticket_storage.mli liquidity_baking_storage.ml liquidity_baking_storage.mli liquidity_baking_cpmm.ml @@ -550,10 +561,16 @@ stake_storage.ml stake_storage.mli contract_storage.ml contract_storage.mli token.ml token.mli + fees_storage.ml fees_storage.mli + delegate_consensus_key.ml delegate_consensus_key.mli delegate_storage.ml delegate_storage.mli + delegate_missed_endorsements_storage.ml + delegate_missed_endorsements_storage.mli + delegate_slashed_deposits_storage.ml delegate_slashed_deposits_storage.mli + delegate_sampler.ml delegate_sampler.mli + delegate_cycles.ml delegate_cycles.mli bootstrap_storage.ml bootstrap_storage.mli vote_storage.ml vote_storage.mli - fees_storage.ml fees_storage.mli ticket_storage.ml ticket_storage.mli liquidity_baking_storage.ml liquidity_baking_storage.mli liquidity_baking_cpmm.ml @@ -769,10 +786,16 @@ stake_storage.ml stake_storage.mli contract_storage.ml contract_storage.mli token.ml token.mli + fees_storage.ml fees_storage.mli + delegate_consensus_key.ml delegate_consensus_key.mli delegate_storage.ml delegate_storage.mli + delegate_missed_endorsements_storage.ml + delegate_missed_endorsements_storage.mli + delegate_slashed_deposits_storage.ml delegate_slashed_deposits_storage.mli + delegate_sampler.ml delegate_sampler.mli + delegate_cycles.ml delegate_cycles.mli bootstrap_storage.ml bootstrap_storage.mli vote_storage.ml vote_storage.mli - fees_storage.ml fees_storage.mli ticket_storage.ml ticket_storage.mli liquidity_baking_storage.ml liquidity_baking_storage.mli liquidity_baking_cpmm.ml @@ -984,10 +1007,16 @@ stake_storage.ml stake_storage.mli contract_storage.ml contract_storage.mli token.ml token.mli + fees_storage.ml fees_storage.mli + delegate_consensus_key.ml delegate_consensus_key.mli delegate_storage.ml delegate_storage.mli + delegate_missed_endorsements_storage.ml + delegate_missed_endorsements_storage.mli + delegate_slashed_deposits_storage.ml delegate_slashed_deposits_storage.mli + delegate_sampler.ml delegate_sampler.mli + delegate_cycles.ml delegate_cycles.mli bootstrap_storage.ml bootstrap_storage.mli vote_storage.ml vote_storage.mli - fees_storage.ml fees_storage.mli ticket_storage.ml ticket_storage.mli liquidity_baking_storage.ml liquidity_baking_storage.mli liquidity_baking_cpmm.ml diff --git a/src/proto_alpha/lib_protocol/init_storage.ml b/src/proto_alpha/lib_protocol/init_storage.ml index 258702c6e1f02a990bd7d7de728d54bae8edf4d5..81ccb3ba85e6081c212b81872bda6d5a60feb144 100644 --- a/src/proto_alpha/lib_protocol/init_storage.ml +++ b/src/proto_alpha/lib_protocol/init_storage.ml @@ -97,12 +97,7 @@ let prepare_first_block _chain_id ctxt ~typecheck ~level ~timestamp = param.bootstrap_accounts param.bootstrap_contracts >>=? fun (ctxt, bootstrap_balance_updates) -> - Delegate_storage.init_first_cycles ctxt >>=? fun ctxt -> - let cycle = (Raw_context.current_level ctxt).cycle in - Delegate_storage.freeze_deposits_do_not_call_except_for_migration - ~new_cycle:cycle - ~balance_updates:[] - ctxt + Delegate_cycles.init_first_cycles ctxt ~origin:Protocol_migration >>=? fun (ctxt, deposits_balance_updates) -> Vote_storage.init ctxt @@ -123,7 +118,9 @@ let prepare_first_block _chain_id ctxt ~typecheck ~level ~timestamp = if that is done, do not set Storage.Tenderbake.First_level_of_protocol. *) Raw_level_repr.of_int32 level >>?= fun level -> Storage.Tenderbake.First_level_of_protocol.update ctxt level - >>=? fun ctxt -> return (ctxt, [])) + >>=? fun ctxt -> + Delegate_cycles.Migration_from_Jakarta.update ctxt >>=? fun ctxt -> + return (ctxt, [])) >>=? fun (ctxt, balance_updates) -> Receipt_repr.group_balance_updates balance_updates >>?= fun balance_updates -> Storage.Pending_migration.Balance_updates.add ctxt balance_updates diff --git a/src/proto_alpha/lib_protocol/level_repr.ml b/src/proto_alpha/lib_protocol/level_repr.ml index 0b5926f387f5739ae86379290e958bc48abc9c9f..f3100a55a1888d5cd00bf96b40d1833f5e7a450f 100644 --- a/src/proto_alpha/lib_protocol/level_repr.ml +++ b/src/proto_alpha/lib_protocol/level_repr.ml @@ -340,4 +340,13 @@ module Internal_for_tests = struct let raw_level = level.level in let new_raw_level = Raw_level_repr.add raw_level n in {level with level = new_raw_level} + + let add_cycles ~blocks_per_cycle level n = + { + level with + cycle = Cycle_repr.add level.cycle n; + level = Raw_level_repr.add level.level (n * blocks_per_cycle); + level_position = + Int32.add level.level_position (Int32.of_int (n * blocks_per_cycle)); + } end diff --git a/src/proto_alpha/lib_protocol/level_repr.mli b/src/proto_alpha/lib_protocol/level_repr.mli index b595ce324e69e1bb1cf7ab2c982e49a92cc6c79c..d6ab68499ba81523a9d29f10cbe91bdf8712a2b3 100644 --- a/src/proto_alpha/lib_protocol/level_repr.mli +++ b/src/proto_alpha/lib_protocol/level_repr.mli @@ -111,6 +111,8 @@ val last_of_cycle : cycle_eras:cycle_eras -> level -> bool module Internal_for_tests : sig val add_level : t -> int -> t + + val add_cycles : blocks_per_cycle:int -> t -> int -> t end (**/**) diff --git a/src/proto_alpha/lib_protocol/main.ml b/src/proto_alpha/lib_protocol/main.ml index 3a1dfce1293978acc75542ebff3c5b971593cf5d..d763dab346c23e84e4d9e35e66582d28abc5bc93 100644 --- a/src/proto_alpha/lib_protocol/main.ml +++ b/src/proto_alpha/lib_protocol/main.ml @@ -94,16 +94,16 @@ type validation_mode = | Application of { block_header : Alpha_context.Block_header.t; fitness : Alpha_context.Fitness.t; - payload_producer : Alpha_context.public_key_hash; - block_producer : Alpha_context.public_key_hash; + payload_producer : Alpha_context.Consensus_key.t; + block_producer : Alpha_context.Consensus_key.t; predecessor_round : Alpha_context.Round.t; predecessor_level : Alpha_context.Level.t; } | Partial_application of { block_header : Alpha_context.Block_header.t; fitness : Alpha_context.Fitness.t; - payload_producer : Alpha_context.public_key_hash; - block_producer : Alpha_context.public_key_hash; + payload_producer : Alpha_context.Consensus_key.t; + block_producer : Alpha_context.Consensus_key.t; predecessor_level : Alpha_context.Level.t; predecessor_round : Alpha_context.Round.t; } @@ -117,8 +117,8 @@ type validation_mode = (* Baker only *) | Full_construction of { predecessor : Block_hash.t; - payload_producer : Alpha_context.public_key_hash; - block_producer : Alpha_context.public_key_hash; + payload_producer : Alpha_context.Consensus_key.t; + block_producer : Alpha_context.Consensus_key.t; protocol_data_contents : Alpha_context.Block_header.contents; level : Int32.t; round : Alpha_context.Round.t; @@ -162,7 +162,7 @@ let begin_partial_application ~chain_id ~ancestor_context:ctxt ~predecessor_level ~predecessor_round >>=? fun ( ctxt, - payload_producer_pk, + payload_producer, block_producer, liquidity_baking_operations_results, liquidity_baking_toggle_ema ) -> @@ -173,7 +173,7 @@ let begin_partial_application ~chain_id ~ancestor_context:ctxt fitness; predecessor_level; predecessor_round; - payload_producer = Signature.Public_key.hash payload_producer_pk; + payload_producer; block_producer; } in @@ -229,7 +229,7 @@ let begin_application ~chain_id ~predecessor_context:ctxt ~predecessor_timestamp fitness; predecessor_round; predecessor_level; - payload_producer = Signature.Public_key.hash payload_producer; + payload_producer; block_producer; } in @@ -443,7 +443,7 @@ let apply_operation ({mode; chain_id; ctxt; op_count; _} as data) data op_count operation - ~payload_producer:Signature.Public_key_hash.zero + ~payload_producer:Alpha_context.Consensus_key.zero | Full_construction { payload_producer; @@ -573,8 +573,8 @@ let finalize_block ( ctxt, Apply_results. { - proposer = Signature.Public_key_hash.zero; - baker = Signature.Public_key_hash.zero; + proposer = Alpha_context.Consensus_key.zero; + baker = Alpha_context.Consensus_key.zero; level_info; voting_period_info; nonce_hash = None; @@ -609,7 +609,7 @@ let finalize_block ( ctxt, Apply_results. { - proposer = Signature.Public_key_hash.zero; + proposer = Alpha_context.Consensus_key.zero; (* We cannot retrieve the proposer as it requires the frozen deposit that might not be available depending on the context given to the partial application. *) @@ -715,6 +715,9 @@ let relative_position_within_block op1 op2 = | Single (Ballot _), Single (Ballot _) -> 0 | _, Single (Ballot _) -> 1 | Single (Ballot _), _ -> -1 + | Single (Drain_delegate _), Single (Drain_delegate _) -> 0 + | _, Single (Drain_delegate _) -> 1 + | Single (Drain_delegate _), _ -> -1 | Single (Failing_noop _), Single (Failing_noop _) -> 0 | _, Single (Failing_noop _) -> 1 | Single (Failing_noop _), _ -> -1 @@ -812,4 +815,9 @@ let precheck_manager {ctxt; _} op = * 'kind Alpha_context.Kind.manager Apply_results.prechecked_contents_list) -> () +let precheck_drain {ctxt; _} op = Apply.precheck_drain ctxt op + +let check_drain_signature {chain_id; ctxt; _} op raw_op = + Apply.check_drain_signature ctxt chain_id op raw_op + (* Vanity nonce: TBD *) diff --git a/src/proto_alpha/lib_protocol/main.mli b/src/proto_alpha/lib_protocol/main.mli index a6e20a489ec40d0612844ce7698175cbad37b9d2..59a051e9b23b1a132107df1fe340756a8cc987b7 100644 --- a/src/proto_alpha/lib_protocol/main.mli +++ b/src/proto_alpha/lib_protocol/main.mli @@ -56,8 +56,8 @@ type validation_mode = | Application of { block_header : Alpha_context.Block_header.t; fitness : Alpha_context.Fitness.t; - payload_producer : Alpha_context.public_key_hash; - block_producer : Alpha_context.public_key_hash; + payload_producer : Alpha_context.Consensus_key.t; + block_producer : Alpha_context.Consensus_key.t; predecessor_round : Alpha_context.Round.t; predecessor_level : Alpha_context.Level.t; } @@ -66,8 +66,8 @@ type validation_mode = | Partial_application of { block_header : Alpha_context.Block_header.t; fitness : Alpha_context.Fitness.t; - payload_producer : Alpha_context.public_key_hash; - block_producer : Alpha_context.public_key_hash; + payload_producer : Alpha_context.Consensus_key.t; + block_producer : Alpha_context.Consensus_key.t; predecessor_level : Alpha_context.Level.t; predecessor_round : Alpha_context.Round.t; } @@ -86,8 +86,8 @@ type validation_mode = {!val:Tezos_protocol_environment_sigs.V5.T.Updater.PROTOCOL.begin_construction} *) | Full_construction of { predecessor : Block_hash.t; - payload_producer : Alpha_context.public_key_hash; - block_producer : Alpha_context.public_key_hash; + payload_producer : Alpha_context.Consensus_key.t; + block_producer : Alpha_context.Consensus_key.t; protocol_data_contents : Alpha_context.Block_header.contents; level : Int32.t; round : Alpha_context.Round.t; @@ -148,6 +148,28 @@ val precheck_manager : 'a Alpha_context.Kind.manager Alpha_context.contents_list -> unit tzresult Lwt.t +(** [check_drain_signature validation_state op raw_operation] + The function starts by retrieving the active consensus public key [pk] + of the delegate. + + @return [Error Invalid_signature] if the signature check fails +*) +val check_drain_signature : + validation_state -> + Alpha_context.Kind.drain_delegate Alpha_context.contents_list -> + Alpha_context.Kind.drain_delegate Alpha_context.operation -> + unit tzresult Lwt.t + +(** [precheck_drain validation_state op] returns the fees that will be + credited to the baker if the operation [op] is solveable, returns + an error otherwise. A drain operation is solveable if it is + well-formed and can pay for the potential allocation burn of the + destination account. This function uses [Apply.precheck_drain]. *) +val precheck_drain : + validation_state -> + Alpha_context.Kind.drain_delegate Alpha_context.contents_list -> + Alpha_context.Tez.t tzresult Lwt.t + include Updater.PROTOCOL with type block_header_data = Alpha_context.Block_header.protocol_data diff --git a/src/proto_alpha/lib_protocol/operation_repr.ml b/src/proto_alpha/lib_protocol/operation_repr.ml index a45d1e4a03ad248eee9e830a38b314f87b0aa0ec..d761d7caf7b62469406de286d84581cce8561f4c 100644 --- a/src/proto_alpha/lib_protocol/operation_repr.ml +++ b/src/proto_alpha/lib_protocol/operation_repr.ml @@ -68,6 +68,10 @@ module Kind = struct type set_deposits_limit = Set_deposits_limit_kind + type update_consensus_key = Update_consensus_key_kind + + type drain_delegate = Drain_delegate_kind + type failing_noop = Failing_noop_kind type register_global_constant = Register_global_constant_kind @@ -111,6 +115,7 @@ module Kind = struct | Delegation_manager_kind : delegation manager | Register_global_constant_manager_kind : register_global_constant manager | Set_deposits_limit_manager_kind : set_deposits_limit manager + | Update_consensus_key_manager_kind : update_consensus_key manager | Tx_rollup_origination_manager_kind : tx_rollup_origination manager | Tx_rollup_submit_batch_manager_kind : tx_rollup_submit_batch manager | Tx_rollup_commit_manager_kind : tx_rollup_commit manager @@ -276,6 +281,12 @@ and _ contents = ballot : Vote_repr.ballot; } -> Kind.ballot contents + | Drain_delegate : { + consensus_key : Signature.Public_key_hash.t; + delegate : Signature.Public_key_hash.t; + destination : Signature.Public_key_hash.t; + } + -> Kind.drain_delegate contents | Failing_noop : string -> Kind.failing_noop contents | Manager_operation : { source : Signature.public_key_hash; @@ -307,6 +318,9 @@ and _ manager_operation = | Set_deposits_limit : Tez_repr.t option -> Kind.set_deposits_limit manager_operation + | Update_consensus_key : + Cycle_repr.t * Signature.Public_key.t + -> Kind.update_consensus_key manager_operation | Tx_rollup_origination : Kind.tx_rollup_origination manager_operation | Tx_rollup_submit_batch : { tx_rollup : Tx_rollup_repr.t; @@ -414,6 +428,7 @@ let manager_kind : type kind. kind manager_operation -> kind Kind.manager = | Delegation _ -> Kind.Delegation_manager_kind | Register_global_constant _ -> Kind.Register_global_constant_manager_kind | Set_deposits_limit _ -> Kind.Set_deposits_limit_manager_kind + | Update_consensus_key _ -> Kind.Update_consensus_key_manager_kind | Tx_rollup_origination -> Kind.Tx_rollup_origination_manager_kind | Tx_rollup_submit_batch _ -> Kind.Tx_rollup_submit_batch_manager_kind | Tx_rollup_commit _ -> Kind.Tx_rollup_commit_manager_kind @@ -652,6 +667,28 @@ module Encoding = struct inj = (fun key -> Set_deposits_limit key); } + let update_consensus_key_tag = 6 + + let[@coq_axiom_with_reason "gadt"] update_consensus_key_case = + MCase + { + tag = update_consensus_key_tag; + name = "update_consensus_key"; + encoding = + obj2 + (req "cycle" Cycle_repr.encoding) + (req "pk" Signature.Public_key.encoding); + select = + (function + | Manager (Update_consensus_key _ as op) -> Some op | _ -> None); + proj = + (function + | Update_consensus_key (cycle, consensus_pk) -> (cycle, consensus_pk)); + inj = + (fun (cycle, consensus_pk) -> + Update_consensus_key (cycle, consensus_pk)); + } + let[@coq_axiom_with_reason "gadt"] tx_rollup_origination_case = MCase { @@ -1321,6 +1358,27 @@ module Encoding = struct Ballot {source; period; proposal; ballot}); } + let[@coq_axiom_with_reason "gadt"] drain_delegate_case = + Case + { + tag = 8; + name = "drain_delegate"; + encoding = + obj3 + (req "consensus_key" Signature.Public_key_hash.encoding) + (req "delegate" Signature.Public_key_hash.encoding) + (req "destination" Signature.Public_key_hash.encoding); + select = + (function Contents (Drain_delegate _ as op) -> Some op | _ -> None); + proj = + (function + | Drain_delegate {consensus_key; delegate; destination} -> + (consensus_key, delegate, destination)); + inj = + (fun (consensus_key, delegate, destination) -> + Drain_delegate {consensus_key; delegate; destination}); + } + let failing_noop_case = Case { @@ -1389,6 +1447,9 @@ module Encoding = struct let set_deposits_limit_case = make_manager_case 112 Manager_operations.set_deposits_limit_case + let update_consensus_key_case = + make_manager_case 113 Manager_operations.update_consensus_key_case + let tx_rollup_origination_case = make_manager_case tx_rollup_operation_tag_offset @@ -1495,6 +1556,8 @@ module Encoding = struct make origination_case; make delegation_case; make set_deposits_limit_case; + make update_consensus_key_case; + make drain_delegate_case; make failing_noop_case; make register_global_constant_case; make tx_rollup_origination_case; @@ -1579,6 +1642,7 @@ let acceptable_passes (op : packed_operation) = | Single (Double_preendorsement_evidence _) -> [2] | Single (Double_baking_evidence _) -> [2] | Single (Activate_account _) -> [2] + | Single (Drain_delegate _) -> [2] | Single (Manager_operation _) -> [3] | Cons (Manager_operation _, _ops) -> [3] @@ -1653,8 +1717,8 @@ let check_signature (type kind) key chain_id | Single ( Failing_noop _ | Proposals _ | Ballot _ | Seed_nonce_revelation _ | Double_endorsement_evidence _ | Double_preendorsement_evidence _ - | Double_baking_evidence _ | Activate_account _ | Manager_operation _ - ) -> + | Double_baking_evidence _ | Activate_account _ | Drain_delegate _ + | Manager_operation _ ) -> check ~watermark:Generic_operation (Contents_list protocol_data.contents) @@ -1699,6 +1763,8 @@ let equal_manager_operation_kind : | Register_global_constant _, _ -> None | Set_deposits_limit _, Set_deposits_limit _ -> Some Eq | Set_deposits_limit _, _ -> None + | Update_consensus_key _, Update_consensus_key _ -> Some Eq + | Update_consensus_key _, _ -> None | Tx_rollup_origination, Tx_rollup_origination -> Some Eq | Tx_rollup_origination, _ -> None | Tx_rollup_submit_batch _, Tx_rollup_submit_batch _ -> Some Eq @@ -1755,6 +1821,8 @@ let equal_contents_kind : type a b. a contents -> b contents -> (a, b) eq option | Proposals _, _ -> None | Ballot _, Ballot _ -> Some Eq | Ballot _, _ -> None + | Drain_delegate _, Drain_delegate _ -> Some Eq + | Drain_delegate _, _ -> None | Failing_noop _, Failing_noop _ -> Some Eq | Failing_noop _, _ -> None | Manager_operation op1, Manager_operation op2 -> ( diff --git a/src/proto_alpha/lib_protocol/operation_repr.mli b/src/proto_alpha/lib_protocol/operation_repr.mli index 63a179cf3944cba19b31a3149fb2fe41fea777f9..33177956f4e21db06515198a75baaa83f02b4818 100644 --- a/src/proto_alpha/lib_protocol/operation_repr.mli +++ b/src/proto_alpha/lib_protocol/operation_repr.mli @@ -97,6 +97,10 @@ module Kind : sig type set_deposits_limit = Set_deposits_limit_kind + type update_consensus_key = Update_consensus_key_kind + + type drain_delegate = Drain_delegate_kind + type failing_noop = Failing_noop_kind type register_global_constant = Register_global_constant_kind @@ -140,6 +144,7 @@ module Kind : sig | Delegation_manager_kind : delegation manager | Register_global_constant_manager_kind : register_global_constant manager | Set_deposits_limit_manager_kind : set_deposits_limit manager + | Update_consensus_key_manager_kind : update_consensus_key manager | Tx_rollup_origination_manager_kind : tx_rollup_origination manager | Tx_rollup_submit_batch_manager_kind : tx_rollup_submit_batch manager | Tx_rollup_commit_manager_kind : tx_rollup_commit manager @@ -292,6 +297,15 @@ and _ contents = ballot : Vote_repr.ballot; } -> Kind.ballot contents + (* [Drain_delegate { consensus_key ; delegate ; destination }] + transfers the spendable balance of the [delegate] to [destination] + when [consensus_key] is the active consensus key of [delegate].. *) + | Drain_delegate : { + consensus_key : Signature.Public_key_hash.t; + delegate : Signature.Public_key_hash.t; + destination : Signature.Public_key_hash.t; + } + -> Kind.drain_delegate contents (* Failing_noop: An operation never considered by the state machine and which will always fail at [apply]. This allows end-users to sign arbitrary messages which have no computational semantics. *) @@ -348,6 +362,11 @@ and _ manager_operation = | Set_deposits_limit : Tez_repr.t option -> Kind.set_deposits_limit manager_operation + (* [Update_consensus_key (cycle, pk)] updates the consensus key of + the signing delegate to [pk] starting at [cycle]. *) + | Update_consensus_key : + Cycle_repr.t * Signature.Public_key.t + -> Kind.update_consensus_key manager_operation (* [Tx_rollup_origination] allows an implicit contract to originate a new transactional rollup. *) | Tx_rollup_origination : Kind.tx_rollup_origination manager_operation @@ -576,6 +595,8 @@ module Encoding : sig val ballot_case : Kind.ballot case + val drain_delegate_case : Kind.drain_delegate case + val failing_noop_case : Kind.failing_noop case val reveal_case : Kind.reveal Kind.manager case @@ -586,6 +607,8 @@ module Encoding : sig val delegation_case : Kind.delegation Kind.manager case + val update_consensus_key_case : Kind.update_consensus_key Kind.manager case + val register_global_constant_case : Kind.register_global_constant Kind.manager case @@ -649,6 +672,10 @@ module Encoding : sig val delegation_case : Kind.delegation case + val update_consensus_key_tag : int + + val update_consensus_key_case : Kind.update_consensus_key case + val register_global_constant_case : Kind.register_global_constant case val set_deposits_limit_case : Kind.set_deposits_limit case diff --git a/src/proto_alpha/lib_protocol/parameters_repr.ml b/src/proto_alpha/lib_protocol/parameters_repr.ml index 3ab238b23d485cbbd3fde4d61e912c767ac80157..13604ffdc600a09776d0be837ec410587da145e2 100644 --- a/src/proto_alpha/lib_protocol/parameters_repr.ml +++ b/src/proto_alpha/lib_protocol/parameters_repr.ml @@ -29,6 +29,7 @@ type bootstrap_account = { public_key : Signature.Public_key.t option; amount : Tez_repr.t; delegate_to : Signature.Public_key_hash.t option; + consensus_key : Signature.Public_key.t option; } type bootstrap_contract = { @@ -60,30 +61,50 @@ let bootstrap_account_encoding = public_key = Some public_key; amount; delegate_to = None; + consensus_key = None; } -> assert ( Signature.Public_key_hash.equal (Signature.Public_key.hash public_key) public_key_hash) ; Some (public_key, amount) - | {public_key = None; _} | {delegate_to = Some _; _} -> None) + | {public_key = None; _} + | {delegate_to = Some _; _} + | {consensus_key = Some _; _} -> + None) (fun (public_key, amount) -> { public_key = Some public_key; public_key_hash = Signature.Public_key.hash public_key; amount; delegate_to = None; + consensus_key = None; }); case (Tag 1) ~title:"Public_key_unknown" (tup2 Signature.Public_key_hash.encoding Tez_repr.encoding) (function - | {public_key_hash; public_key = None; amount; delegate_to = None} -> + | { + public_key_hash; + public_key = None; + amount; + delegate_to = None; + consensus_key = None; + } -> Some (public_key_hash, amount) - | {public_key = Some _; _} | {delegate_to = Some _; _} -> None) + | {public_key = Some _; _} + | {delegate_to = Some _; _} + | {consensus_key = Some _; _} -> + None) (fun (public_key_hash, amount) -> - {public_key = None; public_key_hash; amount; delegate_to = None}); + { + public_key = None; + public_key_hash; + amount; + delegate_to = None; + consensus_key = None; + }); case (Tag 2) ~title:"Public_key_known_with_delegate" @@ -97,19 +118,24 @@ let bootstrap_account_encoding = public_key = Some public_key; amount; delegate_to = Some delegate; + consensus_key = None; } -> assert ( Signature.Public_key_hash.equal (Signature.Public_key.hash public_key) public_key_hash) ; Some (public_key, amount, delegate) - | {public_key = None; _} | {delegate_to = None; _} -> None) + | {public_key = None; _} + | {delegate_to = None; _} + | {consensus_key = Some _; _} -> + None) (fun (public_key, amount, delegate) -> { public_key = Some public_key; public_key_hash = Signature.Public_key.hash public_key; amount; delegate_to = Some delegate; + consensus_key = None; }); case (Tag 3) @@ -124,15 +150,52 @@ let bootstrap_account_encoding = public_key = None; amount; delegate_to = Some delegate; + consensus_key = None; } -> Some (public_key_hash, amount, delegate) - | {public_key = Some _; _} | {delegate_to = None; _} -> None) + | {public_key = Some _; _} + | {delegate_to = None; _} + | {consensus_key = Some _; _} -> + None) (fun (public_key_hash, amount, delegate) -> { public_key = None; public_key_hash; amount; delegate_to = Some delegate; + consensus_key = None; + }); + case + (Tag 4) + ~title:"Public_key_known_with_consensus_key" + (tup3 + Signature.Public_key.encoding + Tez_repr.encoding + Signature.Public_key.encoding) + (function + | { + public_key_hash; + public_key = Some public_key; + amount; + delegate_to = None; + consensus_key = Some consensus_key; + } -> + assert ( + Signature.Public_key_hash.equal + (Signature.Public_key.hash public_key) + public_key_hash) ; + Some (public_key, amount, consensus_key) + | {public_key = None; _} + | {delegate_to = Some _; _} + | {consensus_key = None; _} -> + None) + (fun (public_key, amount, consensus_key) -> + { + public_key = Some public_key; + public_key_hash = Signature.Public_key.hash public_key; + amount; + delegate_to = None; + consensus_key = Some consensus_key; }); ] diff --git a/src/proto_alpha/lib_protocol/parameters_repr.mli b/src/proto_alpha/lib_protocol/parameters_repr.mli index 844e54ba3753ff58c7275b8488c08c881042d703..97bad09f1ccbd3704b1a49070eb9add1674c6576 100644 --- a/src/proto_alpha/lib_protocol/parameters_repr.mli +++ b/src/proto_alpha/lib_protocol/parameters_repr.mli @@ -33,6 +33,7 @@ type bootstrap_account = { public_key : Signature.Public_key.t option; amount : Tez_repr.t; delegate_to : Signature.Public_key_hash.t option; + consensus_key : Signature.Public_key.t option; } (** An originated contract initially existing on a chain since genesis. *) @@ -53,6 +54,8 @@ type t = { no_reward_cycles : int option; } +val bootstrap_account_encoding : bootstrap_account Data_encoding.t + val encoding : t Data_encoding.t val check_params : t -> unit tzresult diff --git a/src/proto_alpha/lib_protocol/raw_context.ml b/src/proto_alpha/lib_protocol/raw_context.ml index f1341fb2d2d8c3e4b171a42436ac92843ee6ac28..4c0712fd9a928dae3215af527d358efd109fd0a4 100644 --- a/src/proto_alpha/lib_protocol/raw_context.ml +++ b/src/proto_alpha/lib_protocol/raw_context.ml @@ -73,6 +73,29 @@ module Sc_rollup_address_map_builder = *) +type consensus_pk = { + delegate : Signature.Public_key_hash.t; + consensus_pk : Signature.Public_key.t; + consensus_pkh : Signature.Public_key_hash.t; +} + +let consensus_pk_encoding = + let open Data_encoding in + conv + (fun {delegate; consensus_pk; consensus_pkh} -> + if Signature.Public_key_hash.equal consensus_pkh delegate then + (consensus_pk, None) + else (consensus_pk, Some delegate)) + (fun (consensus_pk, delegate) -> + let consensus_pkh = Signature.Public_key.hash consensus_pk in + let delegate = + match delegate with None -> consensus_pkh | Some del -> del + in + {delegate; consensus_pk; consensus_pkh}) + (obj2 + (req "consensus_pk" Signature.Public_key.encoding) + (opt "delegate" Signature.Public_key_hash.encoding)) + module Raw_consensus = struct (** Consensus operations are indexed by their [initial slots]. Given a delegate, the [initial slot] is the lowest slot assigned to @@ -81,16 +104,12 @@ module Raw_consensus = struct type t = { current_endorsement_power : int; (** Number of endorsement slots recorded for the current block. *) - allowed_endorsements : - (Signature.Public_key.t * Signature.Public_key_hash.t * int) - Slot_repr.Map.t; + allowed_endorsements : (consensus_pk * int) Slot_repr.Map.t; (** Endorsements rights for the current block. Only an endorsement for the lowest slot in the block can be recorded. The map associates to each initial slot the [pkh] associated to this slot with its power. *) - allowed_preendorsements : - (Signature.Public_key.t * Signature.Public_key_hash.t * int) - Slot_repr.Map.t; + allowed_preendorsements : (consensus_pk * int) Slot_repr.Map.t; (** Preendorsements rights for the current block. Only a preendorsement for the lowest slot in the block can be recorded. The map associates to each initial slot the [pkh] associated to this @@ -238,10 +257,7 @@ type back = { unlimited_operation_gas : bool; consensus : Raw_consensus.t; non_consensus_operations_rev : Operation_hash.t list; - sampler_state : - (Seed_repr.seed - * (Signature.Public_key.t * Signature.Public_key_hash.t) Sampler.t) - Cycle_repr.Map.t; + sampler_state : (Seed_repr.seed * consensus_pk Sampler.t) Cycle_repr.Map.t; stake_distribution_for_current_cycle : Tez_repr.t Signature.Public_key_hash.Map.t option; tx_rollup_current_messages : @@ -1216,6 +1232,12 @@ let record_non_consensus_operation_hash ctxt operation_hash = let non_consensus_operations ctxt = List.rev (non_consensus_operations_rev ctxt) +module Migration_from_Jakarta = struct + let reset_samplers ctxt = + let ctxt = update_sampler_state ctxt Cycle_repr.Map.empty in + ok ctxt +end + let init_sampler_for_cycle ctxt cycle seed state = let map = sampler_state ctxt in if Cycle_repr.Map.mem cycle map then error (Sampler_already_set cycle) @@ -1254,6 +1276,17 @@ module Internal_for_tests = struct let new_level = Level_repr.Internal_for_tests.add_level ctxt.back.level l in let new_back = {ctxt.back with level = new_level} in {ctxt with back = new_back} + + let add_cycles ctxt l = + let blocks_per_cycle = Int32.to_int (constants ctxt).blocks_per_cycle in + let new_level = + Level_repr.Internal_for_tests.add_cycles + ~blocks_per_cycle + ctxt.back.level + l + in + let new_back = {ctxt.back with level = new_level} in + {ctxt with back = new_back} end module type CONSENSUS = sig @@ -1267,20 +1300,18 @@ module type CONSENSUS = sig type round - val allowed_endorsements : - t -> (Signature.Public_key.t * Signature.Public_key_hash.t * int) slot_map + type consensus_pk + + val allowed_endorsements : t -> (consensus_pk * int) slot_map - val allowed_preendorsements : - t -> (Signature.Public_key.t * Signature.Public_key_hash.t * int) slot_map + val allowed_preendorsements : t -> (consensus_pk * int) slot_map val current_endorsement_power : t -> int val initialize_consensus_operation : t -> - allowed_endorsements: - (Signature.Public_key.t * Signature.Public_key_hash.t * int) slot_map -> - allowed_preendorsements: - (Signature.Public_key.t * Signature.Public_key_hash.t * int) slot_map -> + allowed_endorsements:(consensus_pk * int) slot_map -> + allowed_preendorsements:(consensus_pk * int) slot_map -> t val record_grand_parent_endorsement : @@ -1314,7 +1345,8 @@ module Consensus : and type slot := Slot_repr.t and type 'a slot_map := 'a Slot_repr.Map.t and type slot_set := Slot_repr.Set.t - and type round := Round_repr.t = struct + and type round := Round_repr.t + and type consensus_pk := consensus_pk = struct let[@inline] allowed_endorsements ctxt = ctxt.back.consensus.allowed_endorsements diff --git a/src/proto_alpha/lib_protocol/raw_context.mli b/src/proto_alpha/lib_protocol/raw_context.mli index 913ccadb75d13fe97b7c0c7a4ff87200fac09993..2fd0c99bab0d73bdbc78aeb566366e200f71faa1 100644 --- a/src/proto_alpha/lib_protocol/raw_context.mli +++ b/src/proto_alpha/lib_protocol/raw_context.mli @@ -217,14 +217,18 @@ val record_non_consensus_operation_hash : t -> Operation_hash.t -> t val non_consensus_operations : t -> Operation_hash.t list +type consensus_pk = { + delegate : Signature.Public_key_hash.t; + consensus_pk : Signature.Public_key.t; + consensus_pkh : Signature.Public_key_hash.t; +} + +val consensus_pk_encoding : consensus_pk Data_encoding.t + (** [init_sampler_for_cycle ctxt cycle seed state] caches the seeded stake sampler (a.k.a. [seed, state]) for [cycle] in memory for quick access. *) val init_sampler_for_cycle : - t -> - Cycle_repr.t -> - Seed_repr.seed -> - (Signature.public_key * Signature.public_key_hash) Sampler.t -> - t tzresult + t -> Cycle_repr.t -> Seed_repr.seed -> consensus_pk Sampler.t -> t tzresult (** [sampler_for_cycle ~read ctxt cycle] returns the seeded stake sampler for [cycle]. The sampler is read in memory if @@ -233,19 +237,10 @@ val init_sampler_for_cycle : the [read] function and then cached in [ctxt] like [init_sampler_for_cycle]. *) val sampler_for_cycle : - read: - (t -> - (Seed_repr.seed - * (Signature.public_key * Signature.public_key_hash) Sampler.t) - tzresult - Lwt.t) -> + read:(t -> (Seed_repr.seed * consensus_pk Sampler.t) tzresult Lwt.t) -> t -> Cycle_repr.t -> - (t - * Seed_repr.seed - * (Signature.public_key * Signature.public_key_hash) Sampler.t) - tzresult - Lwt.t + (t * Seed_repr.seed * consensus_pk Sampler.t) tzresult Lwt.t (* The stake distribution is stored both in [t] and in the cache. It may be sufficient to only store it in the cache. *) @@ -257,6 +252,8 @@ val init_stake_distribution_for_current_cycle : module Internal_for_tests : sig val add_level : t -> int -> t + + val add_cycles : t -> int -> t end module type CONSENSUS = sig @@ -270,17 +267,17 @@ module type CONSENSUS = sig type round + type consensus_pk + (** Returns a map where each endorser's pkh is associated to the list of its endorsing slots (in decreasing order) for a given level. *) - val allowed_endorsements : - t -> (Signature.Public_key.t * Signature.Public_key_hash.t * int) slot_map + val allowed_endorsements : t -> (consensus_pk * int) slot_map (** Returns a map where each endorser's pkh is associated to the list of its endorsing slots (in decreasing order) for a given level. *) - val allowed_preendorsements : - t -> (Signature.Public_key.t * Signature.Public_key_hash.t * int) slot_map + val allowed_preendorsements : t -> (consensus_pk * int) slot_map (** [endorsement power ctx] returns the endorsement power of the current block. *) @@ -291,10 +288,8 @@ module type CONSENSUS = sig any consensus operation. *) val initialize_consensus_operation : t -> - allowed_endorsements: - (Signature.Public_key.t * Signature.Public_key_hash.t * int) slot_map -> - allowed_preendorsements: - (Signature.Public_key.t * Signature.Public_key_hash.t * int) slot_map -> + allowed_endorsements:(consensus_pk * int) slot_map -> + allowed_preendorsements:(consensus_pk * int) slot_map -> t (** [record_grand_parent_endorsement ctx pkh] records an @@ -356,6 +351,7 @@ module Consensus : and type 'a slot_map := 'a Slot_repr.Map.t and type slot_set := Slot_repr.Set.t and type round := Round_repr.t + and type consensus_pk := consensus_pk module Tx_rollup : sig val add_message : @@ -370,3 +366,7 @@ module Sc_rollup_in_memory_inbox : sig val set_current_messages : t -> Sc_rollup_repr.t -> Context.tree -> t tzresult end + +module Migration_from_Jakarta : sig + val reset_samplers : t -> t tzresult +end diff --git a/src/proto_alpha/lib_protocol/stake_storage.ml b/src/proto_alpha/lib_protocol/stake_storage.ml index 9c34808d628ec435f0c40ad8eaf8bb75c06c449f..c668430b87bd548048158e855457cad0522f9191 100644 --- a/src/proto_alpha/lib_protocol/stake_storage.ml +++ b/src/proto_alpha/lib_protocol/stake_storage.ml @@ -107,16 +107,21 @@ let add_stake ctxt delegate amount = roll now). *) return ctxt -let deactivate_only_call_from_delegate_storage ctxt delegate = +let set_inactive ctxt delegate = + Delegate_activation_storage.set_inactive ctxt delegate >>= fun ctxt -> Storage.Stake.Active_delegate_with_one_roll.remove ctxt delegate -let activate_only_call_from_delegate_storage ctxt delegate = - get_initialized_stake ctxt delegate >>=? fun (staking_balance, ctxt) -> - let tokens_per_roll = Constants_storage.tokens_per_roll ctxt in - if Tez_repr.(staking_balance >= tokens_per_roll) then - Storage.Stake.Active_delegate_with_one_roll.add ctxt delegate () - >>= fun ctxt -> return ctxt - else return ctxt +let set_active ctxt delegate = + Delegate_activation_storage.set_active ctxt delegate + >>=? fun (ctxt, inactive) -> + if not inactive then return ctxt + else + get_initialized_stake ctxt delegate >>=? fun (staking_balance, ctxt) -> + let tokens_per_roll = Constants_storage.tokens_per_roll ctxt in + if Tez_repr.(staking_balance >= tokens_per_roll) then + Storage.Stake.Active_delegate_with_one_roll.add ctxt delegate () + >>= fun ctxt -> return ctxt + else return ctxt let snapshot ctxt = Storage.Stake.Last_snapshot.get ctxt >>=? fun index -> diff --git a/src/proto_alpha/lib_protocol/stake_storage.mli b/src/proto_alpha/lib_protocol/stake_storage.mli index 894f683cd5798953fad5533810ac5bd07be79677..e416939734614c06aeaad09f0d77a0031357f3c1 100644 --- a/src/proto_alpha/lib_protocol/stake_storage.mli +++ b/src/proto_alpha/lib_protocol/stake_storage.mli @@ -38,10 +38,10 @@ val add_stake : Tez_repr.t -> Raw_context.t tzresult Lwt.t -val deactivate_only_call_from_delegate_storage : +val set_inactive : Raw_context.t -> Signature.Public_key_hash.t -> Raw_context.t Lwt.t -val activate_only_call_from_delegate_storage : +val set_active : Raw_context.t -> Signature.Public_key_hash.t -> Raw_context.t tzresult Lwt.t val get_staking_balance : diff --git a/src/proto_alpha/lib_protocol/storage.ml b/src/proto_alpha/lib_protocol/storage.ml index 46c9414315162509bd2e0b612e530dfcc62b82f3..66292f67b43fd0647ecfc97e1837fe9f7d201e32 100644 --- a/src/proto_alpha/lib_protocol/storage.ml +++ b/src/proto_alpha/lib_protocol/storage.ml @@ -190,6 +190,7 @@ module Contract = struct module Spendable_balance = Indexed_context.Make_map + (Registered) (struct let name = ["balance"] end) @@ -197,6 +198,7 @@ module Contract = struct module Missed_endorsements = Indexed_context.Make_map + (Registered) (struct let name = ["missed_endorsements"] end) @@ -204,13 +206,32 @@ module Contract = struct module Manager = Indexed_context.Make_map + (Registered) (struct let name = ["manager"] end) (Manager_repr) + module Consensus_key = + Indexed_context.Make_map + (Registered) + (struct + let name = ["consensus_key"; "active"] + end) + (Signature.Public_key) + + module Pending_consensus_keys = + Make_indexed_data_storage + (Make_subcontext (Registered) (Indexed_context.Raw_context) + (struct + let name = ["consensus_key"; "pendings"] + end)) + (Make_index (Cycle_repr.Index)) + (Signature.Public_key) + module Delegate = Indexed_context.Make_map + (Registered) (struct let name = ["delegate"] end) @@ -225,6 +246,7 @@ module Contract = struct module Delegate_last_cycle_before_deactivation = Indexed_context.Make_map + (Registered) (struct (* FIXME? Change the key name to reflect the functor's name *) let name = ["delegate_desactivation"] @@ -241,6 +263,7 @@ module Contract = struct module Counter = Indexed_context.Make_map + (Registered) (struct let name = ["counter"] end) @@ -254,8 +277,7 @@ module Contract = struct and type value = Script_repr.lazy_expr and type t := Raw_context.t = struct module I = - Indexed_context.Make_carbonated_map - (N) + Indexed_context.Make_carbonated_map (Registered) (N) (struct type t = Script_repr.lazy_expr @@ -324,6 +346,7 @@ module Contract = struct module Paid_storage_space = Indexed_context.Make_map + (Registered) (struct let name = ["paid_bytes"] end) @@ -331,6 +354,7 @@ module Contract = struct module Used_storage_space = Indexed_context.Make_map + (Registered) (struct let name = ["used_bytes"] end) @@ -338,6 +362,7 @@ module Contract = struct module Frozen_deposits = Indexed_context.Make_map + (Registered) (struct let name = ["frozen_deposits"] end) @@ -345,6 +370,7 @@ module Contract = struct module Frozen_deposits_limit = Indexed_context.Make_map + (Registered) (struct let name = ["frozen_deposits_limit"] end) @@ -360,6 +386,7 @@ module Contract = struct module Frozen_bonds = Bond_id_index.Make_carbonated_map + (Registered) (struct let name = ["frozen_bonds"] end) @@ -369,6 +396,7 @@ module Contract = struct module Total_frozen_bonds = Indexed_context.Make_map + (Registered) (struct let name = ["total_frozen_bonds"] end) @@ -453,6 +481,7 @@ module Big_map = struct module Total_bytes = Indexed_context.Make_map + (Registered) (struct let name = ["total_bytes"] end) @@ -460,6 +489,7 @@ module Big_map = struct module Key_type = Indexed_context.Make_map + (Registered) (struct let name = ["key_type"] end) @@ -471,6 +501,7 @@ module Big_map = struct module Value_type = Indexed_context.Make_map + (Registered) (struct let name = ["value_type"] end) @@ -582,6 +613,7 @@ module Sapling = struct module Total_bytes = Indexed_context.Make_map + (Registered) (struct let name = ["total_bytes"] end) @@ -905,6 +937,14 @@ module Delegates = end)) (Public_key_hash_index) +module Consensus_keys = + Make_data_set_storage + (Make_subcontext (Registered) (Raw_context) + (struct + let name = ["consensus_keys"] + end)) + (Public_key_hash_index) + (** Per cycle storage *) type slashed_level = {for_double_endorsing : bool; for_double_baking : bool} @@ -942,6 +982,7 @@ module Cycle = struct module Selected_stake_distribution = Indexed_context.Make_map + (Registered) (struct let name = ["selected_stake_distribution"] end) @@ -958,27 +999,43 @@ module Cycle = struct module Total_active_stake = Indexed_context.Make_map + (Registered) (struct let name = ["total_active_stake"] end) (Tez_repr) - let public_key_with_ghost_hash_encoding = - Data_encoding.conv - fst - (fun x -> (x, Signature.Public_key.hash x)) - Signature.Public_key.encoding + module Migration_from_Jakarta = struct + let public_key_with_ghost_hash_encoding = + Data_encoding.conv + fst + (fun x -> (x, Signature.Public_key.hash x)) + Signature.Public_key.encoding + + module Delegate_sampler_state = + Indexed_context.Make_map + (Ghost) + (struct + let name = ["delegate_sampler_state"] + end) + (struct + type t = + (Signature.Public_key.t * Signature.Public_key_hash.t) Sampler.t + + let encoding = Sampler.encoding public_key_with_ghost_hash_encoding + end) + end module Delegate_sampler_state = Indexed_context.Make_map + (Registered) (struct let name = ["delegate_sampler_state"] end) (struct - type t = - (Signature.Public_key.t * Signature.Public_key_hash.t) Sampler.t + type t = Raw_context.consensus_pk Sampler.t - let encoding = Sampler.encoding public_key_with_ghost_hash_encoding + let encoding = Sampler.encoding Raw_context.consensus_pk_encoding end) type unrevealed_nonce = { @@ -1025,6 +1082,7 @@ module Cycle = struct module Seed = Indexed_context.Make_map + (Registered) (struct let name = ["random_seed"] end) @@ -1417,6 +1475,7 @@ module Tx_rollup = struct module State = Indexed_context.Make_carbonated_map + (Registered) (struct let name = ["state"] end) @@ -1432,6 +1491,7 @@ module Tx_rollup = struct module Inbox = Level_context.Make_carbonated_map + (Registered) (struct let name = ["inbox"] end) @@ -1443,6 +1503,7 @@ module Tx_rollup = struct module Revealed_withdrawals = Level_context.Make_carbonated_map + (Registered) (struct let name = ["withdrawals"] end) @@ -1450,6 +1511,7 @@ module Tx_rollup = struct module Commitment = Level_context.Make_carbonated_map + (Registered) (struct let name = ["commitment"] end) @@ -1465,6 +1527,7 @@ module Tx_rollup = struct module Commitment_bond = Bond_indexed_context.Make_carbonated_map + (Registered) (struct let name = ["commitment"] end) @@ -1492,6 +1555,7 @@ module Sc_rollup = struct module PVM_kind = Indexed_context.Make_map + (Registered) (struct let name = ["kind"] end) @@ -1503,6 +1567,7 @@ module Sc_rollup = struct module Boot_sector = Indexed_context.Make_map + (Registered) (struct let name = ["boot_sector"] end) @@ -1514,6 +1579,7 @@ module Sc_rollup = struct module Parameters_type = Indexed_context.Make_carbonated_map + (Registered) (struct let name = ["parameters_type"] end) @@ -1525,6 +1591,7 @@ module Sc_rollup = struct module Initial_level = Indexed_context.Make_map + (Registered) (struct let name = ["initial_level"] end) @@ -1536,6 +1603,7 @@ module Sc_rollup = struct module Inbox = Indexed_context.Make_carbonated_map + (Registered) (struct let name = ["inbox"] end) @@ -1547,6 +1615,7 @@ module Sc_rollup = struct module Last_cemented_commitment = Indexed_context.Make_carbonated_map + (Registered) (struct let name = ["last_cemented_commitment"] end) @@ -1571,6 +1640,7 @@ module Sc_rollup = struct module Staker_count = Indexed_context.Make_carbonated_map + (Registered) (struct let name = ["staker_count"] end) @@ -1714,8 +1784,14 @@ module Sc_rollup = struct module Applied_outbox_messages = Level_index_context.Make_carbonated_map + (Registered) (struct let name = ["applied_outbox_messages"] end) (Bitset_and_level) end + +module Migration_from_Jakarta = struct + module Delegate_sampler_state = + Cycle.Migration_from_Jakarta.Delegate_sampler_state +end diff --git a/src/proto_alpha/lib_protocol/storage.mli b/src/proto_alpha/lib_protocol/storage.mli index 667740f8fe60d24841ab31a06375c058c0c69be6..6f216f99a6ca4f55c24b3fcc5320b80e866271af 100644 --- a/src/proto_alpha/lib_protocol/storage.mli +++ b/src/proto_alpha/lib_protocol/storage.mli @@ -104,6 +104,20 @@ module Contract : sig and type value = Manager_repr.t and type t := Raw_context.t + (** The active consensus key of a delegate *) + module Consensus_key : + Indexed_data_storage + with type key = Contract_repr.t + and type value = Signature.Public_key.t + and type t := Raw_context.t + + (** The pending consensus key of a delegate *) + module Pending_consensus_keys : + Indexed_data_storage + with type key = Cycle_repr.t + and type value = Signature.Public_key.t + and type t := Raw_context.t * Contract_repr.t + (** The delegate of a contract, if any. *) module Delegate : Indexed_data_storage @@ -345,6 +359,12 @@ module Delegates : with type t := Raw_context.t and type elt = Signature.Public_key_hash.t +(** Set of all active consensus keys in cycle `current + preserved_cycles + 1` *) +module Consensus_keys : + Data_set_storage + with type t := Raw_context.t + and type elt = Signature.Public_key_hash.t + type slashed_level = {for_double_endorsing : bool; for_double_baking : bool} (** Set used to avoid slashing multiple times the same event *) @@ -397,8 +417,7 @@ module Total_active_stake : module Delegate_sampler_state : Indexed_data_storage with type key = Cycle_repr.t - and type value = - (Signature.Public_key.t * Signature.Public_key_hash.t) Sampler.t + and type value = Raw_context.consensus_pk Sampler.t and type t := Raw_context.t (** Votes *) @@ -824,3 +843,12 @@ module Sc_rollup : sig and type key = int32 and type value = Raw_level_repr.t * Bitset.t end + +module Migration_from_Jakarta : sig + module Delegate_sampler_state : + Indexed_data_storage + with type key = Cycle_repr.t + and type value = + (Signature.Public_key.t * Signature.Public_key_hash.t) Sampler.t + and type t := Raw_context.t +end diff --git a/src/proto_alpha/lib_protocol/storage_functors.ml b/src/proto_alpha/lib_protocol/storage_functors.ml index d32d3f00c45626102bd9c53593d79b5911708f3f..8652c9598192c5499c721c7f58b670f050ba850e 100644 --- a/src/proto_alpha/lib_protocol/storage_functors.ml +++ b/src/proto_alpha/lib_protocol/storage_functors.ml @@ -882,7 +882,7 @@ module Make_indexed_subcontext (C : Raw_context.T) (I : INDEX) : [@@coq_axiom_with_reason "stack overflow in Coq"] end - module Make_map (N : NAME) (V : VALUE) : + module Make_map (R : REGISTER) (N : NAME) (V : VALUE) : Indexed_data_storage with type t = t and type key = key and type value = V.t = struct type t = C.t @@ -966,16 +966,20 @@ module Make_indexed_subcontext (C : Raw_context.T) (I : INDEX) : let () = let open Storage_description in let unpack = unpack I.args in + let description = + if R.ghost then Storage_description.create () + else Raw_context.description + in register_value ~get:(fun c -> let c, k = unpack c in find c k) - (register_named_subcontext Raw_context.description N.name) + (register_named_subcontext description N.name) V.encoding [@@coq_axiom_with_reason "stack overflow in Coq"] end - module Make_carbonated_map (N : NAME) (V : VALUE) : + module Make_carbonated_map (R : REGISTER) (N : NAME) (V : VALUE) : Non_iterable_indexed_carbonated_data_storage with type t = t and type key = key @@ -1082,11 +1086,15 @@ module Make_indexed_subcontext (C : Raw_context.T) (I : INDEX) : let () = let open Storage_description in let unpack = unpack I.args in + let description = + if R.ghost then Storage_description.create () + else Raw_context.description + in register_value ~get:(fun c -> let c, k = unpack c in find c k >|=? fun (_, v) -> v) - (register_named_subcontext Raw_context.description N.name) + (register_named_subcontext description N.name) V.encoding [@@coq_axiom_with_reason "stack overflow in Coq"] end diff --git a/src/proto_alpha/lib_protocol/storage_sigs.ml b/src/proto_alpha/lib_protocol/storage_sigs.ml index ad16d90af8555bdb258883746b3d1bb33b7d8436..b38b18ae7c085604d4161eddc0da46add5a8022b 100644 --- a/src/proto_alpha/lib_protocol/storage_sigs.ml +++ b/src/proto_alpha/lib_protocol/storage_sigs.ml @@ -410,10 +410,10 @@ module type Indexed_raw_context = sig module Make_set (_ : REGISTER) (_ : NAME) : Data_set_storage with type t = t and type elt = key - module Make_map (_ : NAME) (V : VALUE) : + module Make_map (_ : REGISTER) (_ : NAME) (V : VALUE) : Indexed_data_storage with type t = t and type key = key and type value = V.t - module Make_carbonated_map (_ : NAME) (V : VALUE) : + module Make_carbonated_map (_ : REGISTER) (_ : NAME) (V : VALUE) : Non_iterable_indexed_carbonated_data_storage with type t = t and type key = key diff --git a/src/proto_alpha/lib_protocol/test/helpers/block.ml b/src/proto_alpha/lib_protocol/test/helpers/block.ml index 21b2a26c38b070d0ce7d5f3675e2808926811fb2..7502ba0197d5f8763f7ede3bb9c5f78a33e1e03d 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/block.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/block.ml @@ -90,9 +90,12 @@ let get_next_baker_excluding excludes block = let {Plugin.RPC.Baking_rights.delegate = pkh; timestamp; round; _} = WithExceptions.Option.get ~loc:__LOC__ @@ List.find - (fun {Plugin.RPC.Baking_rights.delegate; _} -> + (fun {Plugin.RPC.Baking_rights.consensus_key; _} -> not - (List.mem ~equal:Signature.Public_key_hash.equal delegate excludes)) + (List.mem + ~equal:Signature.Public_key_hash.equal + consensus_key + excludes)) bakers in (pkh, round, WithExceptions.Option.to_exn ~none:(Failure "") timestamp) @@ -187,7 +190,8 @@ module Forge = struct | _ -> assert false in let predecessor_round = Fitness.round pred_fitness in - dispatch_policy policy pred >>=? fun (pkh, round, expected_timestamp) -> + dispatch_policy policy pred + >>=? fun (delegate, round, expected_timestamp) -> let timestamp = Option.value ~default:expected_timestamp timestamp in let level = Int32.succ pred.header.shell.level in Raw_level.of_int32 level |> Environment.wrap_tzresult >>?= fun raw_level -> @@ -234,7 +238,7 @@ module Forge = struct ~payload_round () in - {baker = pkh; shell; contents} + {baker = delegate; shell; contents} (* compatibility only, needed by incremental *) let contents ?(proof_of_work_nonce = default_proof_of_work_nonce) @@ -276,7 +280,8 @@ let prepare_main_init_params ?bootstrap_contracts commitments constants let bootstrap_accounts = List.map (fun (Account.{pk; pkh; _}, amount, delegate_to) -> - Default_parameters.make_bootstrap_account (pkh, pk, amount, delegate_to)) + Default_parameters.make_bootstrap_account + (pkh, pk, amount, delegate_to, None)) initial_accounts in let parameters = @@ -742,9 +747,9 @@ let bake_n_with_all_balance_updates ?(baking_mode = Application) ?policy fun (Successful_manager_result r) -> match r with | Reveal_result _ | Delegation_result _ - | Set_deposits_limit_result _ | Tx_rollup_origination_result _ - | Tx_rollup_submit_batch_result _ | Tx_rollup_commit_result _ - | Tx_rollup_return_bond_result _ + | Update_consensus_key_result _ | Set_deposits_limit_result _ + | Tx_rollup_origination_result _ | Tx_rollup_submit_batch_result _ + | Tx_rollup_commit_result _ | Tx_rollup_return_bond_result _ | Tx_rollup_finalize_commitment_result _ | Tx_rollup_remove_commitment_result _ | Tx_rollup_rejection_result _ | Transfer_ticket_result _ @@ -780,6 +785,7 @@ let bake_n_with_origination_results ?(baking_mode = Application) ?policy n b = function | Successful_manager_result (Reveal_result _) | Successful_manager_result (Delegation_result _) + | Successful_manager_result (Update_consensus_key_result _) | Successful_manager_result (Transaction_result _) | Successful_manager_result (Register_global_constant_result _) | Successful_manager_result (Set_deposits_limit_result _) diff --git a/src/proto_alpha/lib_protocol/test/helpers/context.ml b/src/proto_alpha/lib_protocol/test/helpers/context.ml index c5f7874c415c3546b7add44c289c7db417d695c3..b99d8cf0405300e4527b3a10d0f138315e5d5d02 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/context.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/context.ml @@ -330,6 +330,8 @@ module Delegate = struct deactivated : bool; grace_period : Cycle.t; voting_info : Alpha_context.Vote.delegate_info; + active_consensus_key : Signature.Public_key_hash.t; + pending_consensus_keys : (Cycle.t * Signature.Public_key_hash.t) list; } let info ctxt pkh = Delegate_services.info rpc_ctxt ctxt pkh @@ -352,6 +354,8 @@ module Delegate = struct let voting_info ctxt d = Alpha_services.Delegate.voting_info rpc_ctxt ctxt d + let consensus_key ctxt pkh = Delegate_services.consensus_key rpc_ctxt ctxt pkh + let participation ctxt pkh = Delegate_services.participation rpc_ctxt ctxt pkh end @@ -456,7 +460,7 @@ let init_with_constants_gen tup constants = List.map (fun (acc, tez, delegate_to) -> Default_parameters.make_bootstrap_account - (acc.Account.pkh, acc.Account.pk, tez, delegate_to)) + (acc.Account.pkh, acc.Account.pk, tez, delegate_to, None)) accounts in let parameters = @@ -479,7 +483,8 @@ let default_raw_context () = let bootstrap_accounts = List.map (fun (Account.{pk; pkh; _}, amount, delegate_to) -> - Default_parameters.make_bootstrap_account (pkh, pk, amount, delegate_to)) + Default_parameters.make_bootstrap_account + (pkh, pk, amount, delegate_to, None)) initial_accounts in Block.prepare_initial_context_params initial_accounts diff --git a/src/proto_alpha/lib_protocol/test/helpers/context.mli b/src/proto_alpha/lib_protocol/test/helpers/context.mli index 38f49f94fe3d9f3c3c74d7aea9383ad4248e8845..9378ee1c0b3ef2db676772b8f086234e82a1fa4c 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/context.mli +++ b/src/proto_alpha/lib_protocol/test/helpers/context.mli @@ -164,6 +164,8 @@ module Delegate : sig deactivated : bool; grace_period : Cycle.t; voting_info : Vote.delegate_info; + active_consensus_key : Signature.Public_key_hash.t; + pending_consensus_keys : (Cycle.t * Signature.Public_key_hash.t) list; } val info : t -> public_key_hash -> Delegate_services.info tzresult Lwt.t @@ -185,6 +187,11 @@ module Delegate : sig val voting_info : t -> public_key_hash -> Vote.delegate_info tzresult Lwt.t + val consensus_key : + t -> + public_key_hash -> + (public_key_hash * (Cycle.t * public_key_hash) list) tzresult Lwt.t + val participation : t -> public_key_hash -> Delegate.participation_info tzresult Lwt.t end diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_baking.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_baking.ml index 6a6bd357fc3c4463046660fb5fc64796f71d421d..f7179edab1b309d2c60a7c871c9bf98c5d78df1d 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_baking.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_baking.ml @@ -344,7 +344,7 @@ let test_committee_sampling () = List.map (fun (acc, tez, delegate_to) -> Default_parameters.make_bootstrap_account - (acc.Account.pkh, acc.Account.pk, tez, delegate_to)) + (acc.Account.pkh, acc.Account.pk, tez, delegate_to, None)) accounts in let consensus_committee_size = max_round in diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_delegation.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_delegation.ml index 41299b62d68a0170bd09405b2acfde9edc7231c9..1aa2a163f01fecd92b7f61ca06a8916d31031d65 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_delegation.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_delegation.ml @@ -56,7 +56,7 @@ let expect_error err = function let expect_alpha_error err = expect_error (Environment.Ecoproto_error err) let expect_no_change_registered_delegate_pkh pkh = function - | Environment.Ecoproto_error (Delegate_storage.No_deletion pkh0) :: _ + | Environment.Ecoproto_error (Delegate_storage.Contract.No_deletion pkh0) :: _ when pkh0 = pkh -> return_unit | _ -> failwith "Delegate can not be deleted and operation should fail." @@ -203,7 +203,8 @@ let bootstrap_manager_already_registered_delegate ~fee () = else Incremental.add_operation ~expect_failure:(function - | Environment.Ecoproto_error Delegate_storage.Active_delegate :: _ -> + | Environment.Ecoproto_error Delegate_storage.Contract.Active_delegate + :: _ -> return_unit | _ -> failwith "Delegate is already active and operation should fail.") i @@ -440,7 +441,8 @@ let tests_bootstrap_contracts = two possibilities of 1a for non-credited contracts. *) let expect_unregistered_key pkh = function - | Environment.Ecoproto_error (Delegate_storage.Unregistered_delegate pkh0) + | Environment.Ecoproto_error + (Delegate_storage.Contract.Unregistered_delegate pkh0) :: _ when pkh = pkh0 -> return_unit diff --git a/src/proto_alpha/lib_protocol/test/integration/test_frozen_bonds.ml b/src/proto_alpha/lib_protocol/test/integration/test_frozen_bonds.ml index 27f5394c700a2b0276a33e8f3782f8558e6e49d8..2237adcb14077206814e36a551f37e927b07d586 100644 --- a/src/proto_alpha/lib_protocol/test/integration/test_frozen_bonds.ml +++ b/src/proto_alpha/lib_protocol/test/integration/test_frozen_bonds.ml @@ -93,7 +93,7 @@ let init_test ~user_is_delegate = (* Configure delegate, as a delegate by self-delegation, for which revealing its manager key is a prerequisite. *) Contract.reveal_manager_key ctxt delegate delegate_pk >>>=? fun ctxt -> - Delegate.set ctxt delegate_contract (Some delegate) >>>=? fun ctxt -> + Contract.Delegate.set ctxt delegate_contract (Some delegate) >>>=? fun ctxt -> return (ctxt, user_contract, user_account, delegate) (** Tested scenario : @@ -111,7 +111,7 @@ let test_delegate_then_freeze_deposit () = (* Fetch user's initial balance before freeze. *) Token.balance ctxt user_account >>>=? fun (ctxt, user_balance) -> (* Let user delegate to "delegate". *) - Delegate.set ctxt user_contract (Some delegate) >>>=? fun ctxt -> + Contract.Delegate.set ctxt user_contract (Some delegate) >>>=? fun ctxt -> (* Fetch staking balance after delegation and before freeze. *) Delegate.staking_balance ctxt delegate >>>=? fun staking_balance -> (* Freeze a tx-rollup deposit. *) @@ -126,7 +126,7 @@ let test_delegate_then_freeze_deposit () = (* Ensure staking balance did not change. *) Assert.equal_tez ~loc:__LOC__ staking_balance' staking_balance >>=? fun () -> (* Remove delegation. *) - Delegate.set ctxt user_contract None >>>=? fun ctxt -> + Contract.Delegate.set ctxt user_contract None >>>=? fun ctxt -> (* Fetch staking balance after delegation removal. *) Delegate.staking_balance ctxt delegate >>>=? fun staking_balance'' -> (* Ensure staking balance decreased by user's initial balance. *) @@ -173,7 +173,7 @@ let test_freeze_deposit_then_delegate () = Now, fetch staking balance before delegation and after freeze. *) Delegate.staking_balance ctxt delegate >>>=? fun staking_balance -> (* Let user delegate to "delegate". *) - Delegate.set ctxt user_contract (Some delegate) >>>=? fun ctxt -> + Contract.Delegate.set ctxt user_contract (Some delegate) >>>=? fun ctxt -> (* Fetch staking balance after delegation. *) Delegate.staking_balance ctxt delegate >>>=? fun staking_balance' -> (* ensure staking balance increased by the user's balance. *) @@ -191,7 +191,7 @@ let test_freeze_deposit_then_delegate () = Assert.equal_tez ~loc:__LOC__ staking_balance'' staking_balance' >>=? fun () -> (* Remove delegation. *) - Delegate.set ctxt user_contract None >>>=? fun ctxt -> + Contract.Delegate.set ctxt user_contract None >>>=? fun ctxt -> (* Fetch staking balance. *) Delegate.staking_balance ctxt delegate >>>=? fun staking_balance''' -> (* Check that staking balance has decreased by the user's initial balance. *) @@ -329,7 +329,8 @@ let test_scenario scenario = (* Configure delegate, as a delegate by self-delegation, for which revealing its manager key is a prerequisite. *) Contract.reveal_manager_key ctxt delegate2 delegate_pk2 >>>=? fun ctxt -> - Delegate.set ctxt delegate_contract2 (Some delegate2) >>>=? fun ctxt -> + Contract.Delegate.set ctxt delegate_contract2 (Some delegate2) + >>>=? fun ctxt -> let tx_rollup1, nonce = mk_tx_rollup () in let tx_rollup2, _ = mk_tx_rollup ~nonce () in let bond_id1 = Bond_id.Tx_rollup_bond_id tx_rollup1 in @@ -344,7 +345,7 @@ let test_scenario scenario = Contract.get_balance_and_frozen_bonds ctxt user_contract >>>=? fun user_balance -> (* Let user delegate to "delegate". *) - Delegate.set ctxt user_contract (Some delegate) >>>=? fun ctxt -> + Contract.Delegate.set ctxt user_contract (Some delegate) >>>=? fun ctxt -> (* Fetch staking balance after delegation *) Delegate.staking_balance ctxt delegate >>>=? fun staking_balance' -> Assert.equal_tez @@ -418,7 +419,7 @@ let test_scenario scenario = (* Fetch user's initial balance before undelegate. *) Token.balance ctxt user_account >>>=? fun (_, user_balance) -> (* Remove delegation. *) - Delegate.set ctxt user_contract None >>>=? fun ctxt -> + Contract.Delegate.set ctxt user_contract None >>>=? fun ctxt -> (* Fetch staking balance after delegation removal. *) Delegate.staking_balance ctxt delegate >>>=? fun staking_balance' -> (* Ensure staking balance decreased by delegation amount *) diff --git a/src/proto_alpha/lib_protocol/test/integration/test_token.ml b/src/proto_alpha/lib_protocol/test/integration/test_token.ml index d124511689198ad537a81a35befe187ddc2f17b5..20a147c187b11d3f8af8134b80401137e25da853 100644 --- a/src/proto_alpha/lib_protocol/test/integration/test_token.ml +++ b/src/proto_alpha/lib_protocol/test/integration/test_token.ml @@ -505,13 +505,13 @@ let build_test_cases () = (* Configure baker1, and baker2 as delegates by self-delegation, for which revealing their manager key is a prerequisite. *) wrap (Contract.reveal_manager_key ctxt baker1 baker1_pk) >>=? fun ctxt -> - wrap (Delegate.set ctxt (Contract.Implicit baker1) (Some baker1)) + wrap (Contract.Delegate.set ctxt (Contract.Implicit baker1) (Some baker1)) >>=? fun ctxt -> wrap (Contract.reveal_manager_key ctxt baker2 baker2_pk) >>=? fun ctxt -> - wrap (Delegate.set ctxt (Contract.Implicit baker2) (Some baker2)) + wrap (Contract.Delegate.set ctxt (Contract.Implicit baker2) (Some baker2)) (* Let user1 delegate to baker2. *) >>=? fun ctxt -> - wrap (Delegate.set ctxt (Contract.Implicit user1) (Some baker2)) + wrap (Contract.Delegate.set ctxt (Contract.Implicit user1) (Some baker2)) >>=? fun ctxt -> let tx_rollup1 = mk_rollup () in let bond_id1 = Bond_id.Tx_rollup_bond_id tx_rollup1 in diff --git a/src/proto_alpha/lib_protocol/test/unit/main.ml b/src/proto_alpha/lib_protocol/test/unit/main.ml index 8e1f03660c1257ef48dbc8aab8aa728a9e106467..42bc5a90aa7514331d5e85942c69a9b99e088e9d 100644 --- a/src/proto_alpha/lib_protocol/test/unit/main.ml +++ b/src/proto_alpha/lib_protocol/test/unit/main.ml @@ -79,5 +79,6 @@ let () = Unit_test.spec "sc rollup management protocol" Test_sc_rollup_management_protocol.tests; + Unit_test.spec "Delegate_consensus_key.ml" Test_consensus_key.tests; ] |> Lwt_main.run diff --git a/src/proto_alpha/lib_protocol/test/unit/test_consensus_key.ml b/src/proto_alpha/lib_protocol/test/unit/test_consensus_key.ml new file mode 100644 index 0000000000000000000000000000000000000000..8df0ca5fabe315531bfdbb138489b697d3d9b6f0 --- /dev/null +++ b/src/proto_alpha/lib_protocol/test/unit/test_consensus_key.ml @@ -0,0 +1,269 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(** Testing + ------- + Component: Protocol (delegate_consensus_key) + Invocation: dune exec src/proto_alpha/lib_protocol/test/unit/main.exe \ + -- test "^\[Unit\] Delegate_consensus_key.ml" + Subject: Functions from the module `Delegate_consensus_key` +*) + +open Protocol + +let create () = + let open Lwt_result_syntax in + let accounts = Account.generate_accounts 2 in + let a1, a2 = + match accounts with + | [(a1, _, _); (a2, _, _)] -> (a1, a2) + | _ -> assert false + in + let* ctxt = Block.alpha_context accounts in + return (Alpha_context.Internal_for_tests.to_raw ctxt, a1, a2) + +module Consensus_key = struct + let active_key ctxt pkh = + Delegate_consensus_key.active_key ctxt pkh >|= Environment.wrap_tzresult + + let active_pubkey ctxt pkh = + Delegate_consensus_key.active_pubkey ctxt pkh >|= Environment.wrap_tzresult + + let active_pubkey_for_cycle ctxt pkh cycle = + Delegate_consensus_key.active_pubkey_for_cycle + ctxt + pkh + (Cycle_repr.of_int32_exn (Int32.of_int cycle)) + >|= Environment.wrap_tzresult + + let pending_updates ctxt pkh = + Delegate_consensus_key.pending_updates ctxt pkh + >|= Environment.wrap_tzresult + + let register_update ctxt pkh cycle pk = + Delegate_consensus_key.register_update + ctxt + pkh + (Cycle_repr.of_int32_exn (Int32.of_int cycle)) + pk + >|= Environment.wrap_tzresult + + let activate ctxt ~new_cycle = + Delegate_consensus_key.activate ctxt ~new_cycle + >|= Environment.wrap_tzresult +end + +module Assert = struct + include Assert + + let equal_pkh ~__LOC__ a b = + Assert.equal + ~loc:__LOC__ + Signature.Public_key_hash.equal + "pkh" + Signature.Public_key_hash.pp + a + b + + let equal_pk ~__LOC__ a b = + Assert.equal + ~loc:__LOC__ + Signature.Public_key.equal + "pk" + Signature.Public_key.pp + a + b + + let active_keys ~__LOC__ ctxt delegate l = + List.iter_es + (fun (c, pk) -> + let open Lwt_result_syntax in + let* active_pk = + Consensus_key.active_pubkey_for_cycle ctxt delegate c + in + equal_pk ~__LOC__ active_pk.consensus_pk pk) + l +end + +let rec add_cycles ctxt n = + if n <= 0 then return ctxt + else + let open Lwt_result_syntax in + let current_level = Raw_context.current_level ctxt in + let new_cycle = Cycle_repr.succ current_level.cycle in + let* ctxt = Consensus_key.activate ctxt ~new_cycle in + let ctxt = Raw_context.Internal_for_tests.add_cycles ctxt 1 in + add_cycles ctxt (n - 1) + +let test_consensus_key_storage () = + let open Lwt_result_syntax in + let* ctxt, del1, del2 = create () in + let a1 = Account.new_account () in + let a2 = Account.new_account () in + let preserved_cycles = Constants_storage.preserved_cycles ctxt in + let* () = Assert.equal_int ~loc:__LOC__ preserved_cycles 3 in + let* () = + let* active_pkh = Consensus_key.active_key ctxt del1.pkh in + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh del1.pkh + in + let* () = + let* active_pk = Consensus_key.active_pubkey ctxt del1.pkh in + Assert.equal_pk ~__LOC__ active_pk.consensus_pk del1.pk + in + let* () = + let* active_pk = Consensus_key.active_pubkey_for_cycle ctxt del1.pkh 3 in + Assert.equal_pk ~__LOC__ active_pk.consensus_pk del1.pk + in + let* () = + let*! err = Consensus_key.register_update ctxt del1.pkh 4 del2.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_active -> true + | _ -> false) + in + let* () = + let*! err = Consensus_key.register_update ctxt del1.pkh 3 a1.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_cycle c -> + c = Cycle_repr.of_int32_exn 4l + | _ -> false) + in + let* ctxt = Consensus_key.register_update ctxt del1.pkh 4 a1.pk in + let* () = + let*! err = Consensus_key.register_update ctxt del1.pkh 4 a1.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> + c = Cycle_repr.of_int32_exn 4l + | _ -> false) + in + let* () = + let*! err = Consensus_key.register_update ctxt del2.pkh 4 a1.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_active -> true + | _ -> false) + in + let* ctxt = Consensus_key.register_update ctxt del2.pkh 4 del1.pk in + let* () = + let*! err = Consensus_key.register_update ctxt del1.pkh 5 a1.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_cycle c -> + c = Cycle_repr.of_int32_exn 4l + | _ -> false) + in + let* () = + Assert.active_keys + ~__LOC__ + ctxt + del1.pkh + [ + (0, del1.pk); + (1, del1.pk); + (2, del1.pk); + (2, del1.pk); + (3, del1.pk); + (4, a1.pk); + (5, a1.pk); + ] + in + let* ctxt = add_cycles ctxt 1 in + let* () = + let* active_pkh = Consensus_key.active_key ctxt del1.pkh in + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh del1.pkh + in + let* () = + let*! err = Consensus_key.register_update ctxt del1.pkh 4 a1.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_cycle c -> + c = Cycle_repr.of_int32_exn 5l + | _ -> false) + in + let* () = + let*! err = Consensus_key.register_update ctxt del1.pkh 5 a1.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> + c = Cycle_repr.of_int32_exn 4l + | _ -> false) + in + let* ctxt = Consensus_key.register_update ctxt del1.pkh 5 a2.pk in + let* ctxt = Consensus_key.register_update ctxt del2.pkh 5 a1.pk in + let* ctxt = Consensus_key.register_update ctxt del2.pkh 5 del2.pk in + let* () = + Assert.active_keys + ~__LOC__ + ctxt + del1.pkh + [ + (1, del1.pk); + (2, del1.pk); + (2, del1.pk); + (3, del1.pk); + (4, a1.pk); + (5, a2.pk); + (6, a2.pk); + ] + in + let* ctxt = add_cycles ctxt 2 in + let* () = + let* active_pkh = Consensus_key.active_key ctxt del1.pkh in + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh del1.pkh + in + let* () = + let*! err = Consensus_key.register_update ctxt del1.pkh 7 a2.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> + c = Cycle_repr.of_int32_exn 5l + | _ -> false) + in + let* ctxt = Consensus_key.register_update ctxt del1.pkh 7 a1.pk in + let* () = + Assert.active_keys + ~__LOC__ + ctxt + del1.pkh + [(3, del1.pk); (4, a1.pk); (5, a2.pk); (6, a2.pk); (7, a1.pk); (8, a1.pk)] + in + let* ctxt = add_cycles ctxt 1 in + let* () = + let* active_pkh = Consensus_key.active_key ctxt del1.pkh in + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh a1.pkh + in + let* ctxt = add_cycles ctxt 1 in + let* () = + let* active_pkh = Consensus_key.active_key ctxt del1.pkh in + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh a2.pkh + in + let* ctxt = add_cycles ctxt 1 in + let* () = + let* active_pkh = Consensus_key.active_key ctxt del1.pkh in + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh a2.pkh + in + let* ctxt = add_cycles ctxt 1 in + let* () = + let* active_pkh = Consensus_key.active_key ctxt del1.pkh in + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh a1.pkh + in + return () + +let tests = + [Tztest.tztest "consensus_key_storage" `Quick test_consensus_key_storage] diff --git a/tezt/_regressions/rpc/alpha.client.delegates.out b/tezt/_regressions/rpc/alpha.client.delegates.out index 5b87aa0650a66b326124a82c9b324244a5e67ee6..648f09344be05c307ee5d6eb937436ec46abe067 100644 --- a/tezt/_regressions/rpc/alpha.client.delegates.out +++ b/tezt/_regressions/rpc/alpha.client.delegates.out @@ -22,7 +22,8 @@ rpc/alpha.client.delegates.out "frozen_deposits": "200000000000", "staking_balance": "4000000000000", "delegated_contracts": [ "[PUBLIC_KEY_HASH]" ], "delegated_balance": "0", "deactivated": false, "grace_period": 5, - "voting_power": "4000000000000", "remaining_proposals": 20 } + "voting_power": "4000000000000", "remaining_proposals": 20, + "active_consensus_key": "[PUBLIC_KEY_HASH]" } ./tezos-client rpc get '/chains/main/blocks/head/context/delegates/[PUBLIC_KEY_HASH]/full_balance' "4000000000000" diff --git a/tezt/_regressions/rpc/alpha.client.mempool.out b/tezt/_regressions/rpc/alpha.client.mempool.out index 252bdc0ec972de1884e57906ff15b99be64ea9f7..e7c7756202cf26fadd151ea12ec24f72332fdcac 100644 --- a/tezt/_regressions/rpc/alpha.client.mempool.out +++ b/tezt/_regressions/rpc/alpha.client.mempool.out @@ -154,13 +154,15 @@ curl -s 'http://localhost:[PORT]/chains/main/mempool/monitor_operations?applied= [{"hash":"[OPERATION_HASH]","protocol":"ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK","branch":"[BRANCH_HASH]","contents":[{"kind":"transaction","source":"[PUBLIC_KEY_HASH]","fee":"1000","counter":"1","gas_limit":"1040","storage_limit":"257","amount":"1000000","destination":"[PUBLIC_KEY_HASH]"}],"signature":"[SIGNATURE]"},{"hash":"[OPERATION_HASH]","protocol":"ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK","branch":"[BRANCH_HASH]","contents":[{"kind":"transaction","source":"[PUBLIC_KEY_HASH]","fee":"0","counter":"1","gas_limit":"1040","storage_limit":"257","amount":"1000000","destination":"[PUBLIC_KEY_HASH]"}],"signature":"[SIGNATURE]","error":[{"kind":"permanent","id":"proto.alpha.prefilter.fees_too_low"}]},{"hash":"[OPERATION_HASH]","protocol":"ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK","branch":"[BRANCH_HASH]","contents":[{"kind":"transaction","source":"[PUBLIC_KEY_HASH]","fee":"1000","counter":"1","gas_limit":"1040","storage_limit":"257","amount":"1000000","destination":"[PUBLIC_KEY_HASH]"}],"signature":"[SIGNATURE]","error":[{"kind":"branch","id":"proto.alpha.contract.counter_in_the_past","contract":"[PUBLIC_KEY_HASH]","expected":"2","found":"1"}]},{"hash":"[OPERATION_HASH]","protocol":"ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK","branch":"[BRANCH_HASH]","contents":[{"kind":"transaction","source":"[PUBLIC_KEY_HASH]","fee":"1000","counter":"5","gas_limit":"1040","storage_limit":"257","amount":"1000000","destination":"[PUBLIC_KEY_HASH]"}],"signature":"[SIGNATURE]","error":[{"kind":"temporary","id":"proto.alpha.contract.counter_in_the_future","contract":"[PUBLIC_KEY_HASH]","expected":"1","found":"5"}]}] ./tezos-client rpc get /chains/main/mempool/filter -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } ./tezos-client rpc get '/chains/main/mempool/filter?include_default=true' -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } @@ -180,19 +182,22 @@ curl -s 'http://localhost:[PORT]/chains/main/mempool/monitor_operations?applied= ], "allow_script_failure": false }' -{ "minimal_fees": "50", "minimal_nanotez_per_gas_unit": [ "201", "5" ], +{ "minimal_fees": "50", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "201", "5" ], "minimal_nanotez_per_byte": [ "56", "3" ], "allow_script_failure": false, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } ./tezos-client rpc get /chains/main/mempool/filter -{ "minimal_fees": "50", "minimal_nanotez_per_gas_unit": [ "201", "5" ], +{ "minimal_fees": "50", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "201", "5" ], "minimal_nanotez_per_byte": [ "56", "3" ], "allow_script_failure": false, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } ./tezos-client rpc get '/chains/main/mempool/filter?include_default=true' -{ "minimal_fees": "50", "minimal_nanotez_per_gas_unit": [ "201", "5" ], +{ "minimal_fees": "50", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "201", "5" ], "minimal_nanotez_per_byte": [ "56", "3" ], "allow_script_failure": false, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } @@ -205,19 +210,22 @@ curl -s 'http://localhost:[PORT]/chains/main/mempool/monitor_operations?applied= "minimal_fees": "200", "allow_script_failure": true }' -{ "minimal_fees": "200", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "200", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } ./tezos-client rpc get /chains/main/mempool/filter -{ "minimal_fees": "200", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "200", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } ./tezos-client rpc get '/chains/main/mempool/filter?include_default=true' -{ "minimal_fees": "200", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "200", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } @@ -226,19 +234,22 @@ curl -s 'http://localhost:[PORT]/chains/main/mempool/monitor_operations?applied= { "minimal_fees": "200" } ./tezos-client rpc post /chains/main/mempool/filter with '{}' -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } ./tezos-client rpc get /chains/main/mempool/filter -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } ./tezos-client rpc get '/chains/main/mempool/filter?include_default=true' -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } @@ -1683,6 +1694,80 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "additionalProperties": false }, + { + "title": "Update_consensus_key", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "update_consensus_key" + ] + }, + "source": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "fee": { + "$ref": "#/definitions/alpha.mutez" + }, + "counter": { + "$ref": "#/definitions/positive_bignum" + }, + "gas_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "storage_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "cycle": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + "pk": { + "$ref": "#/definitions/Signature.Public_key" + } + }, + "required": [ + "pk", + "cycle", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, + { + "title": "Drain_delegate", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "drain_delegate" + ] + }, + "consensus_key": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "delegate": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "destination": { + "$ref": "#/definitions/Signature.Public_key_hash" + } + }, + "required": [ + "destination", + "delegate", + "consensus_key", + "kind" + ], + "additionalProperties": false + }, { "title": "Failing_noop", "type": "object", @@ -4335,6 +4420,60 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "name": "Double_preendorsement_evidence" }, + { + "tag": 8, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "consensus_key", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "delegate", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "destination", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + } + ], + "name": "Drain_delegate" + }, { "tag": 17, "fields": [ @@ -5075,6 +5214,103 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "name": "Set_deposits_limit" }, + { + "tag": 113, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "source", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "fee", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "counter", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "gas_limit", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "storage_limit", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "cycle", + "layout": { + "size": "Int32", + "kind": "Int" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "pk", + "layout": { + "name": "public_key", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + } + ], + "name": "Update_consensus_key" + }, { "tag": 150, "fields": [ @@ -12150,6 +12386,80 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "additionalProperties": false }, + { + "title": "Update_consensus_key", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "update_consensus_key" + ] + }, + "source": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "fee": { + "$ref": "#/definitions/alpha.mutez" + }, + "counter": { + "$ref": "#/definitions/positive_bignum" + }, + "gas_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "storage_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "cycle": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + "pk": { + "$ref": "#/definitions/Signature.Public_key" + } + }, + "required": [ + "pk", + "cycle", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, + { + "title": "Drain_delegate", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "drain_delegate" + ] + }, + "consensus_key": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "delegate": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "destination": { + "$ref": "#/definitions/Signature.Public_key_hash" + } + }, + "required": [ + "destination", + "delegate", + "consensus_key", + "kind" + ], + "additionalProperties": false + }, { "title": "Failing_noop", "type": "object", diff --git a/tezt/_regressions/rpc/alpha.client.others.out b/tezt/_regressions/rpc/alpha.client.others.out index 6e692cbcd10a16375c21d940a6944349d2e26d52..867b16dcca1ac5f72f721fa2bcd9c7cba1c84ae1 100644 --- a/tezt/_regressions/rpc/alpha.client.others.out +++ b/tezt/_regressions/rpc/alpha.client.others.out @@ -54,15 +54,20 @@ rpc/alpha.client.others.out ./tezos-client rpc get /chains/main/blocks/head/helpers/baking_rights [ { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 0, "estimated_time": "[TIMESTAMP]" }, + "round": 0, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 1, "estimated_time": "[TIMESTAMP]" }, + "round": 1, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 2, "estimated_time": "[TIMESTAMP]" }, + "round": 2, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 3, "estimated_time": "[TIMESTAMP]" }, + "round": 3, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 10, "estimated_time": "[TIMESTAMP]" } ] + "round": 10, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" } ] ./tezos-client rpc get '/chains/main/blocks/head/helpers/current_level?offset=0' { "level": 1, "level_position": 0, "cycle": 0, "cycle_position": 0, @@ -72,15 +77,20 @@ rpc/alpha.client.others.out [ { "level": 1, "delegates": [ { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 11, "endorsing_power": 50 }, + "first_slot": 11, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 4, "endorsing_power": 47 }, + "first_slot": 4, "endorsing_power": 47, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 2, "endorsing_power": 46 }, + "first_slot": 2, "endorsing_power": 46, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 1, "endorsing_power": 55 }, + "first_slot": 1, "endorsing_power": 55, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 0, "endorsing_power": 58 } ] } ] + "first_slot": 0, "endorsing_power": 58, + "consensus_key": "[PUBLIC_KEY_HASH]" } ] } ] ./tezos-client rpc get /chains/main/blocks/head/helpers/levels_in_current_cycle { "first": 1, "last": 8 } diff --git a/tezt/_regressions/rpc/alpha.light.delegates.out b/tezt/_regressions/rpc/alpha.light.delegates.out index 86f0c849ccff7933a9476d928051ff402cd00030..87ef3f752b0f0f5381254921c6ef6439d8112dc8 100644 --- a/tezt/_regressions/rpc/alpha.light.delegates.out +++ b/tezt/_regressions/rpc/alpha.light.delegates.out @@ -24,7 +24,8 @@ protocol of light mode unspecified, using the node's protocol: ProtoGenesisGenes "frozen_deposits": "200000000000", "staking_balance": "4000000000000", "delegated_contracts": [ "[PUBLIC_KEY_HASH]" ], "delegated_balance": "0", "deactivated": false, "grace_period": 5, - "voting_power": "4000000000000", "remaining_proposals": 20 } + "voting_power": "4000000000000", "remaining_proposals": 20, + "active_consensus_key": "[PUBLIC_KEY_HASH]" } protocol of light mode unspecified, using the node's protocol: ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im ./tezos-client --mode light rpc get '/chains/main/blocks/head/context/delegates/[PUBLIC_KEY_HASH]/full_balance' diff --git a/tezt/_regressions/rpc/alpha.light.others.out b/tezt/_regressions/rpc/alpha.light.others.out index 61402074c195e6db67c4ffb4bf65995a06a244b0..208d97cc3102df369d82db4b01cc935123893b21 100644 --- a/tezt/_regressions/rpc/alpha.light.others.out +++ b/tezt/_regressions/rpc/alpha.light.others.out @@ -55,15 +55,20 @@ protocol of light mode unspecified, using the node's protocol: ProtoGenesisGenes ./tezos-client --mode light rpc get /chains/main/blocks/head/helpers/baking_rights [ { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 0, "estimated_time": "[TIMESTAMP]" }, + "round": 0, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 1, "estimated_time": "[TIMESTAMP]" }, + "round": 1, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 2, "estimated_time": "[TIMESTAMP]" }, + "round": 2, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 3, "estimated_time": "[TIMESTAMP]" }, + "round": 3, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 10, "estimated_time": "[TIMESTAMP]" } ] + "round": 10, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" } ] protocol of light mode unspecified, using the node's protocol: ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im ./tezos-client --mode light rpc get '/chains/main/blocks/head/helpers/current_level?offset=0' @@ -75,15 +80,20 @@ protocol of light mode unspecified, using the node's protocol: ProtoGenesisGenes [ { "level": 1, "delegates": [ { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 11, "endorsing_power": 50 }, + "first_slot": 11, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 4, "endorsing_power": 47 }, + "first_slot": 4, "endorsing_power": 47, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 2, "endorsing_power": 46 }, + "first_slot": 2, "endorsing_power": 46, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 1, "endorsing_power": 55 }, + "first_slot": 1, "endorsing_power": 55, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 0, "endorsing_power": 58 } ] } ] + "first_slot": 0, "endorsing_power": 58, + "consensus_key": "[PUBLIC_KEY_HASH]" } ] } ] protocol of light mode unspecified, using the node's protocol: ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im ./tezos-client --mode light rpc get /chains/main/blocks/head/helpers/levels_in_current_cycle diff --git a/tezt/_regressions/rpc/alpha.proxy.delegates.out b/tezt/_regressions/rpc/alpha.proxy.delegates.out index 172a65ed7b8fbf59684c0cc2874abea13a78c9bf..f484c5fe3315e6e033b5a97aceab6917046cae65 100644 --- a/tezt/_regressions/rpc/alpha.proxy.delegates.out +++ b/tezt/_regressions/rpc/alpha.proxy.delegates.out @@ -24,7 +24,8 @@ protocol of proxy unspecified, using the node's protocol: ProtoGenesisGenesisGen "frozen_deposits": "200000000000", "staking_balance": "4000000000000", "delegated_contracts": [ "[PUBLIC_KEY_HASH]" ], "delegated_balance": "0", "deactivated": false, "grace_period": 5, - "voting_power": "4000000000000", "remaining_proposals": 20 } + "voting_power": "4000000000000", "remaining_proposals": 20, + "active_consensus_key": "[PUBLIC_KEY_HASH]" } protocol of proxy unspecified, using the node's protocol: ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im ./tezos-client --mode proxy rpc get '/chains/main/blocks/head/context/delegates/[PUBLIC_KEY_HASH]/full_balance' diff --git a/tezt/_regressions/rpc/alpha.proxy.mempool.out b/tezt/_regressions/rpc/alpha.proxy.mempool.out index c0f381b6079f080c8217384c0f1a097d37e8c4ca..6ebc9200d13f8dcb9c9add35a364ae4e17cbcfce 100644 --- a/tezt/_regressions/rpc/alpha.proxy.mempool.out +++ b/tezt/_regressions/rpc/alpha.proxy.mempool.out @@ -160,14 +160,16 @@ curl -s 'http://localhost:[PORT]/chains/main/mempool/monitor_operations?applied= [{"hash":"[OPERATION_HASH]","protocol":"ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK","branch":"[BRANCH_HASH]","contents":[{"kind":"transaction","source":"[PUBLIC_KEY_HASH]","fee":"1000","counter":"1","gas_limit":"1040","storage_limit":"257","amount":"1000000","destination":"[PUBLIC_KEY_HASH]"}],"signature":"[SIGNATURE]"},{"hash":"[OPERATION_HASH]","protocol":"ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK","branch":"[BRANCH_HASH]","contents":[{"kind":"transaction","source":"[PUBLIC_KEY_HASH]","fee":"0","counter":"1","gas_limit":"1040","storage_limit":"257","amount":"1000000","destination":"[PUBLIC_KEY_HASH]"}],"signature":"[SIGNATURE]","error":[{"kind":"permanent","id":"proto.alpha.prefilter.fees_too_low"}]},{"hash":"[OPERATION_HASH]","protocol":"ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK","branch":"[BRANCH_HASH]","contents":[{"kind":"transaction","source":"[PUBLIC_KEY_HASH]","fee":"1000","counter":"1","gas_limit":"1040","storage_limit":"257","amount":"1000000","destination":"[PUBLIC_KEY_HASH]"}],"signature":"[SIGNATURE]","error":[{"kind":"branch","id":"proto.alpha.contract.counter_in_the_past","contract":"[PUBLIC_KEY_HASH]","expected":"2","found":"1"}]},{"hash":"[OPERATION_HASH]","protocol":"ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK","branch":"[BRANCH_HASH]","contents":[{"kind":"transaction","source":"[PUBLIC_KEY_HASH]","fee":"1000","counter":"5","gas_limit":"1040","storage_limit":"257","amount":"1000000","destination":"[PUBLIC_KEY_HASH]"}],"signature":"[SIGNATURE]","error":[{"kind":"temporary","id":"proto.alpha.contract.counter_in_the_future","contract":"[PUBLIC_KEY_HASH]","expected":"1","found":"5"}]}] ./tezos-client --mode proxy rpc get /chains/main/mempool/filter -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK ./tezos-client --mode proxy rpc get '/chains/main/mempool/filter?include_default=true' -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } @@ -189,21 +191,24 @@ protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaAL ], "allow_script_failure": false }' -{ "minimal_fees": "50", "minimal_nanotez_per_gas_unit": [ "201", "5" ], +{ "minimal_fees": "50", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "201", "5" ], "minimal_nanotez_per_byte": [ "56", "3" ], "allow_script_failure": false, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK ./tezos-client --mode proxy rpc get /chains/main/mempool/filter -{ "minimal_fees": "50", "minimal_nanotez_per_gas_unit": [ "201", "5" ], +{ "minimal_fees": "50", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "201", "5" ], "minimal_nanotez_per_byte": [ "56", "3" ], "allow_script_failure": false, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK ./tezos-client --mode proxy rpc get '/chains/main/mempool/filter?include_default=true' -{ "minimal_fees": "50", "minimal_nanotez_per_gas_unit": [ "201", "5" ], +{ "minimal_fees": "50", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "201", "5" ], "minimal_nanotez_per_byte": [ "56", "3" ], "allow_script_failure": false, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } @@ -218,21 +223,24 @@ protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaAL "minimal_fees": "200", "allow_script_failure": true }' -{ "minimal_fees": "200", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "200", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK ./tezos-client --mode proxy rpc get /chains/main/mempool/filter -{ "minimal_fees": "200", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "200", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK ./tezos-client --mode proxy rpc get '/chains/main/mempool/filter?include_default=true' -{ "minimal_fees": "200", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "200", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } @@ -243,21 +251,24 @@ protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaAL protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK ./tezos-client --mode proxy rpc post /chains/main/mempool/filter with '{}' -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK ./tezos-client --mode proxy rpc get /chains/main/mempool/filter -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK ./tezos-client --mode proxy rpc get '/chains/main/mempool/filter?include_default=true' -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } @@ -1704,6 +1715,80 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "additionalProperties": false }, + { + "title": "Update_consensus_key", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "update_consensus_key" + ] + }, + "source": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "fee": { + "$ref": "#/definitions/alpha.mutez" + }, + "counter": { + "$ref": "#/definitions/positive_bignum" + }, + "gas_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "storage_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "cycle": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + "pk": { + "$ref": "#/definitions/Signature.Public_key" + } + }, + "required": [ + "pk", + "cycle", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, + { + "title": "Drain_delegate", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "drain_delegate" + ] + }, + "consensus_key": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "delegate": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "destination": { + "$ref": "#/definitions/Signature.Public_key_hash" + } + }, + "required": [ + "destination", + "delegate", + "consensus_key", + "kind" + ], + "additionalProperties": false + }, { "title": "Failing_noop", "type": "object", @@ -4356,6 +4441,60 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "name": "Double_preendorsement_evidence" }, + { + "tag": 8, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "consensus_key", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "delegate", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "destination", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + } + ], + "name": "Drain_delegate" + }, { "tag": 17, "fields": [ @@ -5096,6 +5235,103 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "name": "Set_deposits_limit" }, + { + "tag": 113, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "source", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "fee", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "counter", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "gas_limit", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "storage_limit", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "cycle", + "layout": { + "size": "Int32", + "kind": "Int" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "pk", + "layout": { + "name": "public_key", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + } + ], + "name": "Update_consensus_key" + }, { "tag": 150, "fields": [ @@ -12171,6 +12407,80 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "additionalProperties": false }, + { + "title": "Update_consensus_key", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "update_consensus_key" + ] + }, + "source": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "fee": { + "$ref": "#/definitions/alpha.mutez" + }, + "counter": { + "$ref": "#/definitions/positive_bignum" + }, + "gas_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "storage_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "cycle": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + "pk": { + "$ref": "#/definitions/Signature.Public_key" + } + }, + "required": [ + "pk", + "cycle", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, + { + "title": "Drain_delegate", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "drain_delegate" + ] + }, + "consensus_key": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "delegate": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "destination": { + "$ref": "#/definitions/Signature.Public_key_hash" + } + }, + "required": [ + "destination", + "delegate", + "consensus_key", + "kind" + ], + "additionalProperties": false + }, { "title": "Failing_noop", "type": "object", diff --git a/tezt/_regressions/rpc/alpha.proxy.others.out b/tezt/_regressions/rpc/alpha.proxy.others.out index 7d26b7089f0333e42aff2ce512021352aa1e1df3..82d17b80a52e1699cc0fee4554d2116991ea0318 100644 --- a/tezt/_regressions/rpc/alpha.proxy.others.out +++ b/tezt/_regressions/rpc/alpha.proxy.others.out @@ -55,15 +55,20 @@ protocol of proxy unspecified, using the node's protocol: ProtoGenesisGenesisGen ./tezos-client --mode proxy rpc get /chains/main/blocks/head/helpers/baking_rights [ { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 0, "estimated_time": "[TIMESTAMP]" }, + "round": 0, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 1, "estimated_time": "[TIMESTAMP]" }, + "round": 1, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 2, "estimated_time": "[TIMESTAMP]" }, + "round": 2, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 3, "estimated_time": "[TIMESTAMP]" }, + "round": 3, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 10, "estimated_time": "[TIMESTAMP]" } ] + "round": 10, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" } ] protocol of proxy unspecified, using the node's protocol: ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im ./tezos-client --mode proxy rpc get '/chains/main/blocks/head/helpers/current_level?offset=0' @@ -75,15 +80,20 @@ protocol of proxy unspecified, using the node's protocol: ProtoGenesisGenesisGen [ { "level": 1, "delegates": [ { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 11, "endorsing_power": 50 }, + "first_slot": 11, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 4, "endorsing_power": 47 }, + "first_slot": 4, "endorsing_power": 47, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 2, "endorsing_power": 46 }, + "first_slot": 2, "endorsing_power": 46, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 1, "endorsing_power": 55 }, + "first_slot": 1, "endorsing_power": 55, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 0, "endorsing_power": 58 } ] } ] + "first_slot": 0, "endorsing_power": 58, + "consensus_key": "[PUBLIC_KEY_HASH]" } ] } ] protocol of proxy unspecified, using the node's protocol: ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im ./tezos-client --mode proxy rpc get /chains/main/blocks/head/helpers/levels_in_current_cycle diff --git a/tezt/_regressions/rpc/alpha.proxy_server_data_dir.delegates.out b/tezt/_regressions/rpc/alpha.proxy_server_data_dir.delegates.out index 82f40f30c09c4c867cd84afcd9cd6474eaf1de89..8bd81a4eb05ac734b004d55ca93a77aef7963993 100644 --- a/tezt/_regressions/rpc/alpha.proxy_server_data_dir.delegates.out +++ b/tezt/_regressions/rpc/alpha.proxy_server_data_dir.delegates.out @@ -22,7 +22,8 @@ rpc/alpha.proxy_server_data_dir.delegates.out "frozen_deposits": "200000000000", "staking_balance": "4000000000000", "delegated_contracts": [ "[PUBLIC_KEY_HASH]" ], "delegated_balance": "0", "deactivated": false, "grace_period": 5, - "voting_power": "4000000000000", "remaining_proposals": 20 } + "voting_power": "4000000000000", "remaining_proposals": 20, + "active_consensus_key": "[PUBLIC_KEY_HASH]" } ./tezos-client rpc get '/chains/main/blocks/head/context/delegates/[PUBLIC_KEY_HASH]/full_balance' "4000000000000" diff --git a/tezt/_regressions/rpc/alpha.proxy_server_data_dir.others.out b/tezt/_regressions/rpc/alpha.proxy_server_data_dir.others.out index 12f77d24e987a0276887b20f63a96845cc3ed808..1f8608a42e78f190a37ecf239df6f6ff1b617b76 100644 --- a/tezt/_regressions/rpc/alpha.proxy_server_data_dir.others.out +++ b/tezt/_regressions/rpc/alpha.proxy_server_data_dir.others.out @@ -54,15 +54,20 @@ rpc/alpha.proxy_server_data_dir.others.out ./tezos-client rpc get /chains/main/blocks/head/helpers/baking_rights [ { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 0, "estimated_time": "[TIMESTAMP]" }, + "round": 0, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 1, "estimated_time": "[TIMESTAMP]" }, + "round": 1, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 2, "estimated_time": "[TIMESTAMP]" }, + "round": 2, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 3, "estimated_time": "[TIMESTAMP]" }, + "round": 3, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 4, "estimated_time": "[TIMESTAMP]" } ] + "round": 4, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" } ] ./tezos-client rpc get '/chains/main/blocks/head/helpers/current_level?offset=0' { "level": 2, "level_position": 1, "cycle": 0, "cycle_position": 1, @@ -72,15 +77,20 @@ rpc/alpha.proxy_server_data_dir.others.out [ { "level": 2, "delegates": [ { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 10, "endorsing_power": 50 }, + "first_slot": 10, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 3, "endorsing_power": 50 }, + "first_slot": 3, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 2, "endorsing_power": 65 }, + "first_slot": 2, "endorsing_power": 65, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 1, "endorsing_power": 50 }, + "first_slot": 1, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 0, "endorsing_power": 41 } ] } ] + "first_slot": 0, "endorsing_power": 41, + "consensus_key": "[PUBLIC_KEY_HASH]" } ] } ] ./tezos-client rpc get /chains/main/blocks/head/helpers/levels_in_current_cycle { "first": 1, "last": 8 } diff --git a/tezt/_regressions/rpc/alpha.proxy_server_rpc.delegates.out b/tezt/_regressions/rpc/alpha.proxy_server_rpc.delegates.out index bf6ef936d9ad787f9f99a30b160626badc6175e4..f2537562625c430d5dfc5a8e727ee359347ee106 100644 --- a/tezt/_regressions/rpc/alpha.proxy_server_rpc.delegates.out +++ b/tezt/_regressions/rpc/alpha.proxy_server_rpc.delegates.out @@ -22,7 +22,8 @@ rpc/alpha.proxy_server_rpc.delegates.out "frozen_deposits": "200000000000", "staking_balance": "4000000000000", "delegated_contracts": [ "[PUBLIC_KEY_HASH]" ], "delegated_balance": "0", "deactivated": false, "grace_period": 5, - "voting_power": "4000000000000", "remaining_proposals": 20 } + "voting_power": "4000000000000", "remaining_proposals": 20, + "active_consensus_key": "[PUBLIC_KEY_HASH]" } ./tezos-client rpc get '/chains/main/blocks/head/context/delegates/[PUBLIC_KEY_HASH]/full_balance' "4000000000000" diff --git a/tezt/_regressions/rpc/alpha.proxy_server_rpc.others.out b/tezt/_regressions/rpc/alpha.proxy_server_rpc.others.out index 869343b796cc776736144cf20de72f00ee66be84..f660bc2c4f30b7c1201a03b3ec6d6acb9d9cf0f1 100644 --- a/tezt/_regressions/rpc/alpha.proxy_server_rpc.others.out +++ b/tezt/_regressions/rpc/alpha.proxy_server_rpc.others.out @@ -54,15 +54,20 @@ rpc/alpha.proxy_server_rpc.others.out ./tezos-client rpc get /chains/main/blocks/head/helpers/baking_rights [ { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 0, "estimated_time": "[TIMESTAMP]" }, + "round": 0, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 1, "estimated_time": "[TIMESTAMP]" }, + "round": 1, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 2, "estimated_time": "[TIMESTAMP]" }, + "round": 2, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 3, "estimated_time": "[TIMESTAMP]" }, + "round": 3, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 4, "estimated_time": "[TIMESTAMP]" } ] + "round": 4, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" } ] ./tezos-client rpc get '/chains/main/blocks/head/helpers/current_level?offset=0' { "level": 2, "level_position": 1, "cycle": 0, "cycle_position": 1, @@ -72,15 +77,20 @@ rpc/alpha.proxy_server_rpc.others.out [ { "level": 2, "delegates": [ { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 10, "endorsing_power": 50 }, + "first_slot": 10, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 3, "endorsing_power": 50 }, + "first_slot": 3, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 2, "endorsing_power": 65 }, + "first_slot": 2, "endorsing_power": 65, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 1, "endorsing_power": 50 }, + "first_slot": 1, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 0, "endorsing_power": 41 } ] } ] + "first_slot": 0, "endorsing_power": 41, + "consensus_key": "[PUBLIC_KEY_HASH]" } ] } ] ./tezos-client rpc get /chains/main/blocks/head/helpers/levels_in_current_cycle { "first": 1, "last": 8 } diff --git a/tezt/lib_tezos/client.ml b/tezt/lib_tezos/client.ml index 2d3a8a43fb309dbada61d7ce78a623cadd2f48d4..d045a6348f7f20c36331bc385e36595e263f0a0f 100644 --- a/tezt/lib_tezos/client.ml +++ b/tezt/lib_tezos/client.ml @@ -492,7 +492,7 @@ let spawn_bake_for ?endpoint ?protocol ?(keys = [Constant.bootstrap1.alias]) let bake_for ?endpoint ?protocol ?keys ?minimal_fees ?minimal_nanotez_per_gas_unit ?minimal_nanotez_per_byte ?minimal_timestamp - ?mempool ?ignore_node_mempool ?force ?context_path client = + ?mempool ?ignore_node_mempool ?force ?context_path ?expect_failure client = spawn_bake_for ?endpoint ?keys @@ -506,7 +506,7 @@ let bake_for ?endpoint ?protocol ?keys ?minimal_fees ?context_path ?protocol client - |> Process.check + |> Process.check ?expect_failure let node_of_endpoint = function Node n -> Some n | Proxy_server _ -> None @@ -795,7 +795,7 @@ let get_delegate ?endpoint ~src client = Lwt.return (output =~* rex "(tz[a-zA-Z0-9]+) \\(.*\\)") let set_delegate ?endpoint ?(wait = "none") ?fee ?fee_cap - ?(force_low_fee = false) ~src ~delegate client = + ?(force_low_fee = false) ?expect_failure ~src ~delegate client = let value = spawn_command ?endpoint @@ -806,7 +806,7 @@ let set_delegate ?endpoint ?(wait = "none") ?fee ?fee_cap @ optional_arg "fee-cap" Tez.to_string fee_cap @ if force_low_fee then ["--force-low-fee"] else []) in - {value; run = Process.check} + {value; run = Process.check ?expect_failure} let reveal ?endpoint ?(wait = "none") ?fee ?fee_cap ?(force_low_fee = false) ~src client = @@ -826,10 +826,11 @@ let spawn_withdraw_delegate ?endpoint ?(wait = "none") ~src client = spawn_command ?endpoint client - (["--wait"; wait] @ ["withdraw"; "delegate"; "for"; src]) + (["--wait"; wait] @ ["withdraw"; "delegate"; "from"; src]) -let withdraw_delegate ?endpoint ?wait ~src client = - spawn_withdraw_delegate ?endpoint ?wait ~src client |> Process.check +let withdraw_delegate ?endpoint ?wait ?expect_failure ~src client = + spawn_withdraw_delegate ?endpoint ?wait ~src client + |> Process.check ?expect_failure let spawn_get_balance_for ?endpoint ~account client = spawn_command ?endpoint client ["get"; "balance"; "for"; account] @@ -901,6 +902,33 @@ let unset_deposits_limit ?hooks ?endpoint ?(wait = "none") ~src client = (["--wait"; wait] @ ["unset"; "deposits"; "limit"; "for"; src]) |> Process.check_and_read_stdout +let update_consensus_key ?hooks ?endpoint ?(wait = "none") ?burn_cap + ?expect_failure ~src ~pk client = + spawn_command + ?hooks + ?endpoint + client + (["--wait"; wait] + @ ["set"; "consensus"; "key"; "for"; src; "to"; pk] + @ optional_arg "burn-cap" Tez.to_string burn_cap) + |> Process.check ?expect_failure + +let drain_delegate ?hooks ?endpoint ?(wait = "none") ?expect_failure ~delegate + ~consensus_key ?destination client = + let destination = + match destination with + | None -> [] + | Some destination -> [destination; "with"] + in + spawn_command + ?hooks + ?endpoint + client + (["--wait"; wait] + @ ["drain"; "delegate"; delegate; "to"] + @ destination @ [consensus_key]) + |> Process.check ?expect_failure + let spawn_originate_contract ?hooks ?log_output ?endpoint ?(wait = "none") ?init ?burn_cap ~alias ~amount ~src ~prg client = spawn_command @@ -1836,12 +1864,17 @@ let init_with_protocol ?path ?admin_path ?name ?color ?base_dir ?event_level let* _ = Node.wait_for_level node 1 in return (node, client) -let spawn_register_key owner client = +let spawn_register_key ?consensus owner client = spawn_command client - ["--wait"; "none"; "register"; "key"; owner; "as"; "delegate"] + (["--wait"; "none"; "register"; "key"; owner; "as"; "delegate"] + @ + match consensus with + | None -> [] + | Some pk -> ["with"; "consensus"; "key"; pk]) -let register_key owner client = spawn_register_key owner client |> Process.check +let register_key ?consensus owner client = + spawn_register_key ?consensus owner client |> Process.check let contract_storage ?unparsing_mode address client = spawn_command diff --git a/tezt/lib_tezos/client.mli b/tezt/lib_tezos/client.mli index 236d049fed5f57d1c53b1bb1f7f5a2ed80f16edf..7abaf1ec7bc1db3f19f24a1ad5953c4df6ccb48a 100644 --- a/tezt/lib_tezos/client.mli +++ b/tezt/lib_tezos/client.mli @@ -363,6 +363,7 @@ val bake_for : ?ignore_node_mempool:bool -> ?force:bool -> ?context_path:string -> + ?expect_failure:bool -> t -> unit Lwt.t @@ -628,6 +629,7 @@ val set_delegate : ?fee:Tez.t -> ?fee_cap:Tez.t -> ?force_low_fee:bool -> + ?expect_failure:bool -> src:string -> delegate:string -> t -> @@ -646,7 +648,12 @@ val reveal : (** Run [tezos-client withdraw delegate from ]. *) val withdraw_delegate : - ?endpoint:endpoint -> ?wait:string -> src:string -> t -> unit Lwt.t + ?endpoint:endpoint -> + ?wait:string -> + ?expect_failure:bool -> + src:string -> + t -> + unit Lwt.t (** Same as [withdraw_delegate], but do not wait for the process to exit. *) val spawn_withdraw_delegate : @@ -729,6 +736,30 @@ val unset_deposits_limit : t -> string Lwt.t +(** Run [tezos-client use as consensus key for delegate ] *) +val update_consensus_key : + ?hooks:Process.hooks -> + ?endpoint:endpoint -> + ?wait:string -> + ?burn_cap:Tez.t -> + ?expect_failure:bool -> + src:string -> + pk:string -> + t -> + unit Lwt.t + +(** Run [tezos-client drain delegate with consensus key ] *) +val drain_delegate : + ?hooks:Process.hooks -> + ?endpoint:endpoint -> + ?wait:string -> + ?expect_failure:bool -> + delegate:string -> + consensus_key:string -> + ?destination:string -> + t -> + unit Lwt.t + (* TODO: https://gitlab.com/tezos/tezos/-/issues/2336 [amount] should be named [transferring] *) (* TODO: https://gitlab.com/tezos/tezos/-/issues/2336 @@ -1380,10 +1411,10 @@ val spawn_command : Process.t (** Register public key for given account with given client. *) -val spawn_register_key : string -> t -> Process.t +val spawn_register_key : ?consensus:string -> string -> t -> Process.t (** Register public key for given account with given client. *) -val register_key : string -> t -> unit Lwt.t +val register_key : ?consensus:string -> string -> t -> unit Lwt.t (** Get contract storage for a contract. Returns a Micheline expression representing the storage as a string. *) diff --git a/tezt/tests/consensus_key.ml b/tezt/tests/consensus_key.ml new file mode 100644 index 0000000000000000000000000000000000000000..6ee1ffc510450b89a0911a6972e764656d727ba0 --- /dev/null +++ b/tezt/tests/consensus_key.ml @@ -0,0 +1,362 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 G.B. Fefe *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(* Testing + ------- + Component: Baker + Invocation: dune exec tezt/tests/main.exe -- --file consensus_key.ml + Subject: Test the operation `Update_consensus_key` and its effects + on the baker. +*) + +module Helpers = struct + type level = { + level : int; + level_position : int; + cycle : int; + cycle_position : int; + expected_commitment : bool; + } + + let level_type : level Check.typ = + Check.convert + (fun {level; level_position; cycle; cycle_position; expected_commitment} -> + (level, level_position, cycle, cycle_position, expected_commitment)) + Check.(tuple5 int int int int bool) + + let decode_level json = + let level = JSON.(json |-> "level" |> as_int) in + let level_position = JSON.(json |-> "level_position" |> as_int) in + let cycle = JSON.(json |-> "cycle" |> as_int) in + let cycle_position = JSON.(json |-> "cycle_position" |> as_int) in + let expected_commitment = + JSON.(json |-> "expected_commitment" |> as_bool) + in + {level; level_position; cycle; cycle_position; expected_commitment} + + let get_current_level client = + let* json = RPC.get_current_level client in + return (decode_level json) + + let check_current_level client expected_level = + let* level = get_current_level client in + Check.((level = expected_level) level_type) + ~error_msg:"expected current_period = %R, got %L" ; + unit + + let bake_and_wait_block node client = + let* level_json = RPC.get_current_level client in + let level = JSON.(level_json |-> "level" |> as_int) in + let* () = + Client.bake_for ~context_path:(Node.data_dir node // "context") client + in + let* _i = Node.wait_for_level node (level + 1) in + Lwt.return_unit +end + +open Helpers + +let test_update_consensus_key = + Protocol.register_test + ~__FILE__ + ~title:"update consensus key" + ~tags:["consensus_key"] + @@ fun protocol -> + let parameters = + (* we update paramaters for faster testing: no need to wait + 5 cycles for the consensus key to activate. *) + [(["blocks_per_cycle"], Some "4"); (["preserved_cycles"], Some "0")] + in + let* parameter_file = + Protocol.write_parameter_file ~base:(Right (protocol, None)) parameters + in + let* _, client = + Client.init_with_protocol ~parameter_file ~protocol `Client () + in + let* key_a = Client.gen_and_show_keys client in + let* key_b = Client.gen_and_show_keys client in + let* key_c = Client.gen_and_show_keys client in + let* destination = Client.gen_and_show_keys client in + + let* () = + Client.transfer + ~burn_cap:Tez.one + ~amount:(Tez.of_int 1_000_000) + ~giver:Constant.bootstrap1.alias + ~receiver:key_b.alias + client + in + let* () = + Client.transfer + ~burn_cap:Tez.one + ~amount:(Tez.of_int 1_000_000) + ~giver:Constant.bootstrap2.alias + ~receiver:key_c.alias + client + in + let* () = + Client.transfer + ~burn_cap:Tez.one + ~amount:(Tez.of_int 1) + ~giver:Constant.bootstrap4.alias + ~receiver:destination.alias + client + in + let* () = Client.bake_for_and_wait client in + + (* Trying multiple invalid updates: + - changing the consensus key of an unregistered delegate + - changing the consensus key to its actual value + - changing the consensus key to an active consensus key (bootstrap) + *) + let* () = + Client.update_consensus_key + ~expect_failure:true + ~src:key_b.alias + ~pk:Constant.bootstrap1.alias + client + in + let* () = + Client.update_consensus_key + ~expect_failure:true + ~src:Constant.bootstrap1.alias + ~pk:Constant.bootstrap1.alias + client + in + let* () = + Client.update_consensus_key + ~expect_failure:true + ~src:Constant.bootstrap2.alias + ~pk:Constant.bootstrap1.alias + client + in + + (* Trying a valid consensus key update. *) + let* () = + Client.update_consensus_key + ~src:Constant.bootstrap1.alias + ~pk:key_a.alias + client + in + let* () = Client.bake_for_and_wait client in + + (* Invalid update: + - changing the consensus key to an active consensus key (set) *) + let* () = + Client.update_consensus_key + ~expect_failure:true + ~src:Constant.bootstrap2.alias + ~pk:key_a.alias + client + in + + (* Register a delegate with a consensus key. *) + let* () = Client.register_key ~consensus:key_c.alias key_b.alias client in + + (* Bake until the end of the cycle... *) + let* () = Client.bake_for_and_wait client in + let* () = + check_current_level + client + { + level = 4; + level_position = 3; + cycle = 0; + cycle_position = 3; + expected_commitment = true; + } + in + + (* Bootstrap1 should not be able to bake anymore... *) + let* () = + Client.bake_for + ~expect_failure:true + ~keys:[Constant.bootstrap1.alias] + client + in + + (* ... meanwhile `key_a` and `key_c` are able to bake. *) + let* () = Client.bake_for_and_wait ~keys:[key_a.alias] client in + let* () = Client.bake_for_and_wait ~keys:[key_c.alias] client in + + (* Switch back to the initial consensus key. *) + let* () = + Client.update_consensus_key + ~src:Constant.bootstrap1.alias + ~pk:Constant.bootstrap1.alias + client + in + let* () = + Client.update_consensus_key ~src:key_b.alias ~pk:key_b.alias client + in + + (* Bake until the end of the cycle. *) + let* () = Client.bake_for_and_wait ~keys:[key_a.alias] client in + let* () = Client.bake_for_and_wait ~keys:[key_a.alias] client in + let* () = + check_current_level + client + { + level = 8; + level_position = 7; + cycle = 1; + cycle_position = 3; + expected_commitment = true; + } + in + + (* We are not able to bake with `key_a` anymore... *) + let* () = Client.bake_for ~expect_failure:true ~keys:[key_a.alias] client in + + (* ... but are able to bake again with `bootstrap1` and `key_b`... *) + let* () = Client.bake_for_and_wait ~keys:[Constant.bootstrap1.alias] client in + let* () = Client.bake_for_and_wait ~keys:[key_b.alias] client in + + let* () = + Client.update_consensus_key + ~src:Constant.bootstrap3.alias + ~pk:key_a.alias + client + in + let* () = + Client.update_consensus_key + ~src:Constant.bootstrap4.alias + ~pk:key_c.alias + client + in + + (* Bake until the end of the cycle. *) + let* () = Client.bake_for_and_wait ~keys:[Constant.bootstrap1.alias] client in + let* () = Client.bake_for_and_wait ~keys:[Constant.bootstrap1.alias] client in + let* () = + check_current_level + client + { + level = 12; + level_position = 11; + cycle = 2; + cycle_position = 3; + expected_commitment = true; + } + in + + (* Invalid drain: unregistered delegate. *) + let* () = + Client.drain_delegate + ~expect_failure:true + ~delegate:destination.alias + ~consensus_key:destination.alias + ~destination:destination.alias + client + in + + (* Invalid drain: bootstrap2 is has no custom consensus key. *) + let* () = + Client.drain_delegate + ~expect_failure:true + ~delegate:Constant.bootstrap2.alias + ~consensus_key:Constant.bootstrap2.alias + ~destination:destination.alias + client + in + + (* Invalid drain: bootstrap2 is not the consensus key for bootstrap1. *) + let* () = + Client.drain_delegate + ~expect_failure:true + ~delegate:Constant.bootstrap1.alias + ~consensus_key:Constant.bootstrap2.alias + ~destination:destination.alias + client + in + + (* Invalid drain: cannot drain to itself. *) + let* () = + Client.drain_delegate + ~expect_failure:true + ~delegate:Constant.bootstrap4.alias + ~consensus_key:key_c.alias + ~destination:Constant.bootstrap4.alias + client + in + + let* old_balance = Client.get_balance_for ~account:destination.alias client in + let* () = + Client.drain_delegate + ~delegate:Constant.bootstrap4.alias + ~consensus_key:key_c.alias + ~destination:destination.alias + client + in + let* () = Client.bake_for_and_wait ~keys:[Constant.bootstrap1.alias] client in + + (* The manager account has been drained... *) + let* b = Client.get_balance_for ~account:Constant.bootstrap4.alias client in + Check.((Tez.to_mutez b = 0) int) ~error_msg:"Manager balance is not empty" ; + + let* new_balance = Client.get_balance_for ~account:destination.alias client in + Check.((Tez.to_mutez old_balance < Tez.to_mutez new_balance) int) + ~error_msg:"Destination account has not been credited" ; + + (* Check if drain is prefered over a operation from the delegate... *) + let* old_balance5 = + Client.get_balance_for ~account:Constant.bootstrap5.alias client + in + let* () = + Client.transfer + ~burn_cap:Tez.one + ~amount:(Tez.of_int 1) + ~giver:Constant.bootstrap3.alias + ~receiver:Constant.bootstrap5.alias + client + in + + let* old_balance = Client.get_balance_for ~account:destination.alias client in + let* () = + Client.drain_delegate + ~delegate:Constant.bootstrap3.alias + ~consensus_key:key_a.alias + ~destination:destination.alias + client + in + let* () = Client.bake_for_and_wait ~keys:[Constant.bootstrap1.alias] client in + + (* The manager account has been drained... *) + let* b = Client.get_balance_for ~account:Constant.bootstrap3.alias client in + Check.((Tez.to_mutez b = 0) int) ~error_msg:"Manager balance is not empty" ; + + let* new_balance5 = + Client.get_balance_for ~account:Constant.bootstrap5.alias client + in + Check.((Tez.to_mutez new_balance5 = Tez.to_mutez old_balance5) int) + ~error_msg:"Manager operation was included" ; + + let* new_balance = Client.get_balance_for ~account:destination.alias client in + Check.((Tez.to_mutez old_balance < Tez.to_mutez new_balance) int) + ~error_msg:"Destination account has not been credited" ; + + unit + +let register ~protocols = test_update_consensus_key protocols diff --git a/tezt/tests/main.ml b/tezt/tests/main.ml index 16b77d632e1ea047c700d31950a3c2c5cb7afa63..eed1452d76c3938297c123ccd42567cc9b5302cd 100644 --- a/tezt/tests/main.ml +++ b/tezt/tests/main.ml @@ -126,6 +126,7 @@ let () = Sapling.register ~protocols:[Alpha] ; Client_run_view.register ~protocols:[Alpha] ; Multinode_snapshot.register ~protocols:[Alpha] ; + Consensus_key.register ~protocols:[Alpha] ; Config.register () ; (* Test.run () should be the last statement, don't register afterwards! *) Test.run ()