diff --git a/src/proto_alpha/lib_protocol/src/alpha_context.mli b/src/proto_alpha/lib_protocol/src/alpha_context.mli index 1f297a7fb..227a07f6d 100644 --- a/src/proto_alpha/lib_protocol/src/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/src/alpha_context.mli @@ -346,6 +346,7 @@ module Constants : sig nonce_length : int ; max_revelations_per_block : int ; max_operation_data_length : int ; + max_proposals_per_delegate : int ; } val fixed_encoding: fixed Data_encoding.t val fixed: fixed @@ -354,6 +355,7 @@ module Constants : sig val nonce_length: int val max_revelations_per_block: int val max_operation_data_length: int + val max_proposals_per_delegate: int (** Constants parameterized by context *) type parametric = { @@ -731,11 +733,14 @@ module Vote : sig val record_proposal: context -> Protocol_hash.t -> public_key_hash -> - context Lwt.t + context tzresult Lwt.t val get_proposals: context -> int32 Protocol_hash.Map.t tzresult Lwt.t val clear_proposals: context -> context Lwt.t + val recorded_proposal_count_for_delegate: + context -> public_key_hash -> int tzresult Lwt.t + val listings_encoding : (Signature.Public_key_hash.t * int32) list Data_encoding.t val freeze_listings: context -> context tzresult Lwt.t val clear_listings: context -> context tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/src/amendment.ml b/src/proto_alpha/lib_protocol/src/amendment.ml index 6b833ca3b..b42b901ba 100644 --- a/src/proto_alpha/lib_protocol/src/amendment.ml +++ b/src/proto_alpha/lib_protocol/src/amendment.ml @@ -121,6 +121,8 @@ type error += (* `Branch *) | Invalid_proposal | Unexpected_proposal | Unauthorized_proposal + | Too_many_proposals + | Empty_proposal | Unexpected_ballot | Unauthorized_ballot @@ -175,17 +177,45 @@ let () = ~pp:(fun ppf () -> Format.fprintf ppf "Unauthorized ballot") empty (function Unauthorized_ballot -> Some () | _ -> None) - (fun () -> Unauthorized_ballot) + (fun () -> Unauthorized_ballot) ; + (* Too many proposals *) + register_error_kind + `Branch + ~id:"too_many_proposals" + ~title:"Too many proposals" + ~description:"The delegate reached the maximum number of allowed proposals." + ~pp:(fun ppf () -> Format.fprintf ppf "Too many proposals") + empty + (function Too_many_proposals -> Some () | _ -> None) + (fun () -> Too_many_proposals) ; + (* Empty proposal *) + register_error_kind + `Branch + ~id:"empty_proposal" + ~title:"Empty proposal" + ~description:"Proposal lists cannot be empty." + ~pp:(fun ppf () -> Format.fprintf ppf "Empty proposal") + empty + (function Empty_proposal -> Some () | _ -> None) + (fun () -> Empty_proposal) let record_proposals ctxt delegate proposals = + begin match proposals with + | [] -> fail Empty_proposal + | _ :: _ -> return () + end >>=? fun () -> Vote.get_current_period_kind ctxt >>=? function | Proposal -> Vote.in_listings ctxt delegate >>= fun in_listings -> if in_listings then - Lwt_list.fold_left_s + fold_left_s (fun ctxt proposal -> Vote.record_proposal ctxt proposal delegate) - ctxt proposals >>= return + ctxt proposals >>=? fun ctxt -> + Vote.recorded_proposal_count_for_delegate ctxt delegate >>=? fun count -> + if Compare.Int.(count > Constants.max_proposals_per_delegate) then + fail Too_many_proposals + else return ctxt else fail Unauthorized_proposal | Testing_vote | Testing | Promotion_vote -> diff --git a/src/proto_alpha/lib_protocol/src/amendment.mli b/src/proto_alpha/lib_protocol/src/amendment.mli index f27116fc8..baffcff97 100644 --- a/src/proto_alpha/lib_protocol/src/amendment.mli +++ b/src/proto_alpha/lib_protocol/src/amendment.mli @@ -31,6 +31,8 @@ val may_start_new_voting_cycle: type error += | Unexpected_proposal | Unauthorized_proposal + | Too_many_proposals + | Empty_proposal val record_proposals: context -> diff --git a/src/proto_alpha/lib_protocol/src/constants_repr.ml b/src/proto_alpha/lib_protocol/src/constants_repr.ml index 9adfb1b4c..e583c80ec 100644 --- a/src/proto_alpha/lib_protocol/src/constants_repr.ml +++ b/src/proto_alpha/lib_protocol/src/constants_repr.ml @@ -27,13 +27,15 @@ let version_number = "\000" let proof_of_work_nonce_size = 8 let nonce_length = 32 let max_revelations_per_block = 32 -let max_operation_data_length = 16 * 1024 ; (* 16kB *) +let max_proposals_per_delegate = 20 +let max_operation_data_length = 16 * 1024 (* 16kB *) type fixed = { proof_of_work_nonce_size : int ; nonce_length : int ; max_revelations_per_block : int ; max_operation_data_length : int ; + max_proposals_per_delegate : int ; } let fixed_encoding = @@ -43,27 +45,32 @@ let fixed_encoding = (c.proof_of_work_nonce_size, c.nonce_length, c.max_revelations_per_block, - c.max_operation_data_length)) + c.max_operation_data_length, + c.max_proposals_per_delegate)) (fun (proof_of_work_nonce_size, nonce_length, max_revelations_per_block, - max_operation_data_length) -> + max_operation_data_length, + max_proposals_per_delegate) -> { proof_of_work_nonce_size ; nonce_length ; max_revelations_per_block ; max_operation_data_length ; + max_proposals_per_delegate ; } ) - (obj4 + (obj5 (req "proof_of_work_nonce_size" uint8) (req "nonce_length" uint8) (req "max_revelations_per_block" uint8) - (req "max_operation_data_length" int31)) + (req "max_operation_data_length" int31) + (req "max_proposals_per_delegate" uint8)) let fixed = { proof_of_work_nonce_size ; nonce_length ; max_revelations_per_block ; max_operation_data_length ; + max_proposals_per_delegate ; } type parametric = { diff --git a/src/proto_alpha/lib_protocol/src/storage.ml b/src/proto_alpha/lib_protocol/src/storage.ml index 2285c4d61..0eb14cc7c 100644 --- a/src/proto_alpha/lib_protocol/src/storage.ml +++ b/src/proto_alpha/lib_protocol/src/storage.ml @@ -159,6 +159,11 @@ module Contract = struct (struct let name = ["counter"] end) (Z) + module Proposals = + Indexed_context.Make_map + (struct let name = ["proposals"] end) + (Int) + (* Consume gas for serilization and deserialization of expr in this module *) module Make_carbonated_map_expr (N : Storage_sigs.NAME) = struct diff --git a/src/proto_alpha/lib_protocol/src/storage.mli b/src/proto_alpha/lib_protocol/src/storage.mli index 96b992346..74f07d300 100644 --- a/src/proto_alpha/lib_protocol/src/storage.mli +++ b/src/proto_alpha/lib_protocol/src/storage.mli @@ -178,6 +178,11 @@ module Contract : sig and type value = Z.t and type t := Raw_context.t + module Proposals : Indexed_data_storage + with type key = Contract_repr.t + and type value = int + and type t := Raw_context.t + module Code : Non_iterable_indexed_carbonated_data_storage with type key = Contract_repr.t and type value = Script_repr.lazy_expr diff --git a/src/proto_alpha/lib_protocol/src/vote_storage.ml b/src/proto_alpha/lib_protocol/src/vote_storage.ml index b8f9be0e3..bc0ec37ee 100644 --- a/src/proto_alpha/lib_protocol/src/vote_storage.ml +++ b/src/proto_alpha/lib_protocol/src/vote_storage.ml @@ -23,8 +23,18 @@ (* *) (*****************************************************************************) -let record_proposal ctxt delegate proposal = - Storage.Vote.Proposals.add ctxt (delegate, proposal) +let recorded_proposal_count_for_delegate ctxt proposer = + let delegate = Contract_repr.implicit_contract proposer in + Storage.Contract.Proposals.get_option ctxt delegate >>=? function + | None -> return 0 + | Some count -> return count + +let record_proposal ctxt proposal proposer = + recorded_proposal_count_for_delegate ctxt proposer >>=? fun count -> + let delegate = Contract_repr.implicit_contract proposer in + Storage.Contract.Proposals.init_set ctxt delegate (count + 1) >>= fun ctxt -> + Storage.Vote.Proposals.add ctxt (proposal, proposer) >>= fun ctxt -> + return ctxt let get_proposals ctxt = Storage.Vote.Proposals.fold ctxt @@ -42,6 +52,10 @@ let get_proposals ctxt = end) let clear_proposals ctxt = + Storage.Delegates.fold ctxt ~init:ctxt ~f:begin fun proposer ctxt -> + let delegate = Contract_repr.implicit_contract proposer in + Storage.Contract.Proposals.remove ctxt delegate + end >>= fun ctxt -> Storage.Vote.Proposals.clear ctxt type ballots = { diff --git a/src/proto_alpha/lib_protocol/src/vote_storage.mli b/src/proto_alpha/lib_protocol/src/vote_storage.mli index 9d1eb00c4..cefd0830f 100644 --- a/src/proto_alpha/lib_protocol/src/vote_storage.mli +++ b/src/proto_alpha/lib_protocol/src/vote_storage.mli @@ -23,9 +23,14 @@ (* *) (*****************************************************************************) +(** Records a proposal per delegate *) val record_proposal: Raw_context.t -> Protocol_hash.t -> Signature.Public_key_hash.t -> - Raw_context.t Lwt.t + Raw_context.t tzresult Lwt.t + +val recorded_proposal_count_for_delegate: + Raw_context.t -> Signature.Public_key_hash.t -> + int tzresult Lwt.t val get_proposals: Raw_context.t -> int32 Protocol_hash.Map.t tzresult Lwt.t