Alpha: reintroduce duplicate endorsement error

This commit is contained in:
Marco Stronati 2018-06-16 18:19:53 +02:00 committed by Grégoire Henry
parent 1ab9a7a72c
commit b5c275c5fa
10 changed files with 94 additions and 60 deletions

View File

@ -928,7 +928,7 @@ module Operation : sig
type error += Invalid_signature (* `Permanent *) type error += Invalid_signature (* `Permanent *)
val check_signature: public_key -> _ operation -> unit tzresult Lwt.t val check_signature: public_key -> _ operation -> unit tzresult Lwt.t
val raw_check_signature: public_key -> _ operation -> unit tzresult val check_signature_sync: public_key -> _ operation -> unit tzresult
val internal_operation_encoding: packed_internal_operation Data_encoding.t val internal_operation_encoding: packed_internal_operation Data_encoding.t
@ -1059,10 +1059,10 @@ val record_endorsement:
context -> Signature.Public_key_hash.t -> context context -> Signature.Public_key_hash.t -> context
val allowed_endorsements: val allowed_endorsements:
context -> context ->
(Signature.Public_key.t * int list) Signature.Public_key_hash.Map.t (Signature.Public_key.t * int list * bool) Signature.Public_key_hash.Map.t
val init_endorsements: val init_endorsements:
context -> context ->
(Signature.Public_key.t * int list) Signature.Public_key_hash.Map.t -> (Signature.Public_key.t * int list * bool) Signature.Public_key_hash.Map.t ->
context context
val reset_internal_nonce: context -> context val reset_internal_nonce: context -> context

View File

@ -13,6 +13,7 @@ open Alpha_context
type error += Wrong_voting_period of Voting_period.t * Voting_period.t (* `Temporary *) type error += Wrong_voting_period of Voting_period.t * Voting_period.t (* `Temporary *)
type error += Wrong_endorsement_predecessor of Block_hash.t * Block_hash.t (* `Temporary *) type error += Wrong_endorsement_predecessor of Block_hash.t * Block_hash.t (* `Temporary *)
type error += Duplicate_endorsement of Signature.Public_key_hash.t (* `Branch *)
type error += Invalid_endorsement_level type error += Invalid_endorsement_level
type error += Invalid_commitment of { expected: bool } type error += Invalid_commitment of { expected: bool }
type error += Internal_operation_replay of packed_internal_operation type error += Internal_operation_replay of packed_internal_operation
@ -70,6 +71,17 @@ let () =
(req "provided" Voting_period.encoding)) (req "provided" Voting_period.encoding))
(function Wrong_voting_period (e, p) -> Some (e, p) | _ -> None) (function Wrong_voting_period (e, p) -> Some (e, p) | _ -> None)
(fun (e, p) -> Wrong_voting_period (e, p)); (fun (e, p) -> Wrong_voting_period (e, p));
register_error_kind
`Branch
~id:"operation.duplicate_endorsement"
~title:"Duplicate endorsement"
~description:"Two endorsements received from same delegate"
~pp:(fun ppf k ->
Format.fprintf ppf "Duplicate endorsement from delegate %a (possible replay attack)."
Signature.Public_key_hash.pp_short k)
Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding))
(function Duplicate_endorsement k -> Some k | _ -> None)
(fun k -> Duplicate_endorsement k);
register_error_kind register_error_kind
`Temporary `Temporary
~id:"operation.invalid_endorsement_level" ~id:"operation.invalid_endorsement_level"
@ -597,14 +609,20 @@ let apply_contents_list
: (context * kind contents_result_list) tzresult Lwt.t = : (context * kind contents_result_list) tzresult Lwt.t =
match contents_list with match contents_list with
| Single (Endorsement { block ; level }) -> | Single (Endorsement { block ; level }) ->
let current_level = (Level.current ctxt).level in
fail_unless fail_unless
(Block_hash.equal block pred_block) (Block_hash.equal block pred_block)
(Wrong_endorsement_predecessor (pred_block, block)) >>=? fun () -> (Wrong_endorsement_predecessor (pred_block, block)) >>=? fun () ->
let block = operation.shell.branch in
fail_unless
(Block_hash.equal block pred_block)
(Wrong_endorsement_predecessor (pred_block, block)) >>=? fun () ->
let current_level = (Level.current ctxt).level in
fail_unless fail_unless
Raw_level.(succ level = current_level) Raw_level.(succ level = current_level)
Invalid_endorsement_level >>=? fun () -> Invalid_endorsement_level >>=? fun () ->
Baking.check_endorsement_rights ctxt operation >>=? fun (delegate, slots) -> Baking.check_endorsement_rights ctxt operation >>=? fun (delegate, slots, used) ->
if used then fail (Duplicate_endorsement delegate)
else
let ctxt = record_endorsement ctxt delegate in let ctxt = record_endorsement ctxt delegate in
let gap = List.length slots in let gap = List.length slots in
let ctxt = Fitness.increase ~gap ctxt in let ctxt = Fitness.increase ~gap ctxt in
@ -639,8 +657,8 @@ let apply_contents_list
(Outdated_double_endorsement_evidence (Outdated_double_endorsement_evidence
{ level = level.level ; { level = level.level ;
last = oldest_level }) >>=? fun () -> last = oldest_level }) >>=? fun () ->
Baking.check_endorsement_rights ctxt op1 >>=? fun (delegate1, _) -> Baking.check_endorsement_rights ctxt op1 >>=? fun (delegate1, _, _) ->
Baking.check_endorsement_rights ctxt op2 >>=? fun (delegate2, _) -> Baking.check_endorsement_rights ctxt op2 >>=? fun (delegate2, _, _) ->
fail_unless fail_unless
(Signature.Public_key_hash.equal delegate1 delegate2) (Signature.Public_key_hash.equal delegate1 delegate2)
(Inconsistent_double_endorsement_evidence (Inconsistent_double_endorsement_evidence

View File

@ -13,7 +13,7 @@ open Misc
type error += Invalid_fitness_gap of int64 * int64 (* `Permanent *) type error += Invalid_fitness_gap of int64 * int64 (* `Permanent *)
type error += Timestamp_too_early of Timestamp.t * Timestamp.t (* `Permanent *) type error += Timestamp_too_early of Timestamp.t * Timestamp.t (* `Permanent *)
type error += Unexpected_endorsement type error += Unexpected_endorsement (* `Permanent *)
type error += Invalid_block_signature of Block_hash.t * Signature.Public_key_hash.t (* `Permanent *) type error += Invalid_block_signature of Block_hash.t * Signature.Public_key_hash.t (* `Permanent *)
type error += Invalid_signature (* `Permanent *) type error += Invalid_signature (* `Permanent *)
type error += Invalid_stamp (* `Permanent *) type error += Invalid_stamp (* `Permanent *)
@ -158,11 +158,11 @@ let endorsement_rights c level =
(fun acc slot -> (fun acc slot ->
Roll.endorsement_rights_owner c level ~slot >>=? fun pk -> Roll.endorsement_rights_owner c level ~slot >>=? fun pk ->
let pkh = Signature.Public_key.hash pk in let pkh = Signature.Public_key.hash pk in
let slots = let right =
match Signature.Public_key_hash.Map.find_opt pkh acc with match Signature.Public_key_hash.Map.find_opt pkh acc with
| None -> (pk, [slot]) | None -> (pk, [slot], false)
| Some (pk, slots) -> (pk, slot :: slots) in | Some (pk, slots, used) -> (pk, slot :: slots, used) in
return (Signature.Public_key_hash.Map.add pkh slots acc)) return (Signature.Public_key_hash.Map.add pkh right acc))
Signature.Public_key_hash.Map.empty Signature.Public_key_hash.Map.empty
(0 --> (Constants.endorsers_per_block c - 1)) (0 --> (Constants.endorsers_per_block c - 1))
@ -174,20 +174,17 @@ let check_endorsement_rights ctxt (op : Kind.endorsement Operation.t) =
return (Alpha_context.allowed_endorsements ctxt) return (Alpha_context.allowed_endorsements ctxt)
else else
endorsement_rights ctxt (Level.from_raw ctxt level) endorsement_rights ctxt (Level.from_raw ctxt level)
end >>=? fun map -> end >>=? fun endorsements ->
match match
Signature.Public_key_hash.Map.find_first_opt Signature.Public_key_hash.Map.fold (* no find_first *)
(fun pkh -> (fun pkh (pk, slots, used) acc ->
let pk, _ = Signature.Public_key_hash.Map.find pkh map in match Operation.check_signature_sync pk op with
match Operation.raw_check_signature pk op with | Error _ -> acc
| Error _ -> false | Ok () -> Some (pkh, slots, used))
| Ok () -> true) endorsements None
map
with with
| None -> | None -> fail Unexpected_endorsement
fail Unexpected_endorsement | Some v -> return v
| Some (pkh, (_pk, slots)) ->
return (pkh, slots)
let select_delegate delegate delegate_list max_priority = let select_delegate delegate delegate_list max_priority =
let rec loop acc l n = let rec loop acc l n =

View File

@ -31,18 +31,19 @@ val check_baking_rights:
context -> Block_header.contents -> Time.t -> context -> Block_header.contents -> Time.t ->
public_key tzresult Lwt.t public_key tzresult Lwt.t
(** [check_endorsements_rights c slots]: (** For a given level computes who has the right to
* verifies that the endorsement slots are valid ; include an endorsement in the next block.
* verifies that the endorsement slots correspond to the same delegate at the current level; The result can be stored in Alpha_context.allowed_endorsements *)
*)
val check_endorsement_rights:
context -> Kind.endorsement Operation.t ->
(public_key_hash * int list) tzresult Lwt.t
val endorsement_rights: val endorsement_rights:
context -> context ->
Level.t -> Level.t ->
(public_key * int list) Signature.Public_key_hash.Map.t tzresult Lwt.t (public_key * int list * bool) Signature.Public_key_hash.Map.t tzresult Lwt.t
(** Check that the operation was signed by a delegate allowed
to endorse at the level specified by the endorsement. *)
val check_endorsement_rights:
context -> Kind.endorsement Operation.t ->
(public_key_hash * int list * bool) tzresult Lwt.t
(** Returns the endorsement reward calculated w.r.t a given priotiry. *) (** Returns the endorsement reward calculated w.r.t a given priotiry. *)
val endorsement_reward: context -> block_priority:int -> int -> Tez.t tzresult Lwt.t val endorsement_reward: context -> block_priority:int -> int -> Tez.t tzresult Lwt.t

View File

@ -460,7 +460,7 @@ module Endorsing_rights = struct
RPC_service.get_service RPC_service.get_service
~description: ~description:
"Retrieves the delegates allowed to endorse a block.\n\ "Retrieves the delegates allowed to endorse a block.\n\
By default, it gives the endorsement slots for bakers that \ By default, it gives the endorsement slots for delegates that \
have at least one in the next block.\n\ have at least one in the next block.\n\
Parameters `level` and `cycle` can be used to specify the \ Parameters `level` and `cycle` can be used to specify the \
(valid) level(s) in the past or future at which the \ (valid) level(s) in the past or future at which the \
@ -483,7 +483,7 @@ module Endorsing_rights = struct
Baking.endorsement_rights ctxt level >>=? fun rights -> Baking.endorsement_rights ctxt level >>=? fun rights ->
return return
(Signature.Public_key_hash.Map.fold (Signature.Public_key_hash.Map.fold
(fun delegate (_, slots) acc -> { (fun delegate (_, slots, _) acc -> {
level = level.level ; delegate ; slots ; estimated_time level = level.level ; delegate ; slots ; estimated_time
} :: acc) } :: acc)
rights []) rights [])
@ -529,4 +529,3 @@ let baking_rights ctxt max_priority =
List.map List.map
(fun { Baking_rights.delegate ; timestamp ; _ } -> (fun { Baking_rights.delegate ; timestamp ; _ } ->
(delegate, timestamp)) l) (delegate, timestamp)) l)

View File

@ -646,7 +646,7 @@ let () =
(function Missing_signature -> Some () | _ -> None) (function Missing_signature -> Some () | _ -> None)
(fun () -> Missing_signature) (fun () -> Missing_signature)
let raw_check_signature (type kind) key ({ shell ; protocol_data } : kind operation) = let check_signature_sync (type kind) key ({ shell ; protocol_data } : kind operation) =
let check ~watermark contents signature = let check ~watermark contents signature =
let unsigned_operation = let unsigned_operation =
Data_encoding.Binary.to_bytes_exn Data_encoding.Binary.to_bytes_exn
@ -668,7 +668,7 @@ let raw_check_signature (type kind) key ({ shell ; protocol_data } : kind operat
check ~watermark:Generic_operation (Contents_list contents) signature check ~watermark:Generic_operation (Contents_list contents) signature
let check_signature pk op = let check_signature pk op =
Lwt.return (raw_check_signature pk op) Lwt.return (check_signature_sync pk op)
let hash_raw = Operation.hash let hash_raw = Operation.hash
let hash (o : _ operation) = let hash (o : _ operation) =

View File

@ -165,7 +165,7 @@ type error += Invalid_signature (* `Permanent *)
val check_signature: val check_signature:
Signature.Public_key.t -> _ operation -> unit tzresult Lwt.t Signature.Public_key.t -> _ operation -> unit tzresult Lwt.t
val raw_check_signature: val check_signature_sync:
Signature.Public_key.t -> _ operation -> unit tzresult Signature.Public_key.t -> _ operation -> unit tzresult

View File

@ -18,7 +18,7 @@ type t = {
fitness: Int64.t ; fitness: Int64.t ;
deposits: Tez_repr.t Signature.Public_key_hash.Map.t ; deposits: Tez_repr.t Signature.Public_key_hash.Map.t ;
allowed_endorsements: allowed_endorsements:
(Signature.Public_key.t * int list) Signature.Public_key_hash.Map.t ; (Signature.Public_key.t * int list * bool) Signature.Public_key_hash.Map.t ;
fees: Tez_repr.t ; fees: Tez_repr.t ;
rewards: Tez_repr.t ; rewards: Tez_repr.t ;
block_gas: Z.t ; block_gas: Z.t ;
@ -41,11 +41,23 @@ let constants ctxt = ctxt.constants
let recover ctxt = ctxt.context let recover ctxt = ctxt.context
let record_endorsement ctxt k = let record_endorsement ctxt k =
match Signature.Public_key_hash.Map.find_opt k ctxt.allowed_endorsements with
| None -> assert false
| Some (_, _, true) -> assert false (* right already used *)
| Some (d, s, false) ->
{ ctxt with { ctxt with
allowed_endorsements = allowed_endorsements =
Signature.Public_key_hash.Map.remove k ctxt.allowed_endorsements } Signature.Public_key_hash.Map.add k (d,s,true) ctxt.allowed_endorsements }
let init_endorsements ctxt allowed_endorsements = let init_endorsements ctxt allowed_endorsements =
{ ctxt with allowed_endorsements } if Signature.Public_key_hash.Map.is_empty allowed_endorsements
then assert false (* can't initialize to empty *)
else begin
if Signature.Public_key_hash.Map.is_empty ctxt.allowed_endorsements
then { ctxt with allowed_endorsements }
else assert false (* can't initialize twice *)
end
let allowed_endorsements ctxt = let allowed_endorsements ctxt =
ctxt.allowed_endorsements ctxt.allowed_endorsements

View File

@ -205,12 +205,19 @@ val record_internal_nonce: context -> int -> context
(** Check is the internal operation nonce has been taken. *) (** Check is the internal operation nonce has been taken. *)
val internal_nonce_already_recorded: context -> int -> bool val internal_nonce_already_recorded: context -> int -> bool
val record_endorsement: (** Returns a map where to each endorser's pkh is associated the list of its
context -> Signature.Public_key_hash.t -> context endorsing slots (in decreasing order) for a given level. *)
val allowed_endorsements: val allowed_endorsements:
context -> context ->
(Signature.Public_key.t * int list) Signature.Public_key_hash.Map.t (Signature.Public_key.t * int list * bool) Signature.Public_key_hash.Map.t
(** Initializes the map of allowed endorsements, this function must only be
called once. *)
val init_endorsements: val init_endorsements:
context -> context ->
(Signature.Public_key.t * int list) Signature.Public_key_hash.Map.t -> (Signature.Public_key.t * int list * bool) Signature.Public_key_hash.Map.t ->
context context
(** Marks an endorsment in the map as used. *)
val record_endorsement:
context -> Signature.Public_key_hash.t -> context