Alpha: reintroduce duplicate endorsement error
This commit is contained in:
parent
1ab9a7a72c
commit
b5c275c5fa
@ -928,7 +928,7 @@ module Operation : sig
|
||||
type error += Invalid_signature (* `Permanent *)
|
||||
|
||||
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
|
||||
|
||||
@ -1059,10 +1059,10 @@ val record_endorsement:
|
||||
context -> Signature.Public_key_hash.t -> context
|
||||
val allowed_endorsements:
|
||||
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:
|
||||
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
|
||||
|
||||
val reset_internal_nonce: context -> context
|
||||
|
@ -13,6 +13,7 @@ open Alpha_context
|
||||
|
||||
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 += Duplicate_endorsement of Signature.Public_key_hash.t (* `Branch *)
|
||||
type error += Invalid_endorsement_level
|
||||
type error += Invalid_commitment of { expected: bool }
|
||||
type error += Internal_operation_replay of packed_internal_operation
|
||||
@ -70,6 +71,17 @@ let () =
|
||||
(req "provided" Voting_period.encoding))
|
||||
(function Wrong_voting_period (e, p) -> Some (e, p) | _ -> None)
|
||||
(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
|
||||
`Temporary
|
||||
~id:"operation.invalid_endorsement_level"
|
||||
@ -597,25 +609,31 @@ let apply_contents_list
|
||||
: (context * kind contents_result_list) tzresult Lwt.t =
|
||||
match contents_list with
|
||||
| Single (Endorsement { block ; level }) ->
|
||||
let current_level = (Level.current ctxt).level in
|
||||
fail_unless
|
||||
(Block_hash.equal block pred_block)
|
||||
(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
|
||||
Raw_level.(succ level = current_level)
|
||||
Invalid_endorsement_level >>=? fun () ->
|
||||
Baking.check_endorsement_rights ctxt operation >>=? fun (delegate, slots) ->
|
||||
let ctxt = record_endorsement ctxt delegate in
|
||||
let gap = List.length slots in
|
||||
let ctxt = Fitness.increase ~gap ctxt in
|
||||
Lwt.return
|
||||
Tez.(Constants.endorsement_security_deposit ctxt *?
|
||||
Int64.of_int gap) >>=? fun deposit ->
|
||||
add_deposit ctxt delegate deposit >>=? fun ctxt ->
|
||||
Global.get_last_block_priority ctxt >>=? fun block_priority ->
|
||||
Baking.endorsement_reward ctxt ~block_priority gap >>=? fun reward ->
|
||||
Delegate.freeze_rewards ctxt delegate reward >>=? fun ctxt ->
|
||||
return (ctxt, Single_result (Endorsement_result (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 gap = List.length slots in
|
||||
let ctxt = Fitness.increase ~gap ctxt in
|
||||
Lwt.return
|
||||
Tez.(Constants.endorsement_security_deposit ctxt *?
|
||||
Int64.of_int gap) >>=? fun deposit ->
|
||||
add_deposit ctxt delegate deposit >>=? fun ctxt ->
|
||||
Global.get_last_block_priority ctxt >>=? fun block_priority ->
|
||||
Baking.endorsement_reward ctxt ~block_priority gap >>=? fun reward ->
|
||||
Delegate.freeze_rewards ctxt delegate reward >>=? fun ctxt ->
|
||||
return (ctxt, Single_result (Endorsement_result (delegate, slots)))
|
||||
| Single (Seed_nonce_revelation { level ; nonce }) ->
|
||||
let level = Level.from_raw ctxt level in
|
||||
Nonce.reveal ctxt level nonce >>=? fun ctxt ->
|
||||
@ -639,8 +657,8 @@ let apply_contents_list
|
||||
(Outdated_double_endorsement_evidence
|
||||
{ level = level.level ;
|
||||
last = oldest_level }) >>=? fun () ->
|
||||
Baking.check_endorsement_rights ctxt op1 >>=? fun (delegate1, _) ->
|
||||
Baking.check_endorsement_rights ctxt op2 >>=? fun (delegate2, _) ->
|
||||
Baking.check_endorsement_rights ctxt op1 >>=? fun (delegate1, _, _) ->
|
||||
Baking.check_endorsement_rights ctxt op2 >>=? fun (delegate2, _, _) ->
|
||||
fail_unless
|
||||
(Signature.Public_key_hash.equal delegate1 delegate2)
|
||||
(Inconsistent_double_endorsement_evidence
|
||||
|
@ -386,7 +386,7 @@ module Encoding = struct
|
||||
encoding =
|
||||
(obj2
|
||||
(req "delegate" Signature.Public_key_hash.encoding)
|
||||
(req "slots" (list uint8))) ;
|
||||
(req "slots" (list uint8)));
|
||||
select =
|
||||
(function
|
||||
| Contents_result (Endorsement_result _ as op) -> Some op
|
||||
|
@ -13,7 +13,7 @@ open Misc
|
||||
|
||||
type error += Invalid_fitness_gap of int64 * int64 (* `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_signature (* `Permanent *)
|
||||
type error += Invalid_stamp (* `Permanent *)
|
||||
@ -158,11 +158,11 @@ let endorsement_rights c level =
|
||||
(fun acc slot ->
|
||||
Roll.endorsement_rights_owner c level ~slot >>=? fun pk ->
|
||||
let pkh = Signature.Public_key.hash pk in
|
||||
let slots =
|
||||
let right =
|
||||
match Signature.Public_key_hash.Map.find_opt pkh acc with
|
||||
| None -> (pk, [slot])
|
||||
| Some (pk, slots) -> (pk, slot :: slots) in
|
||||
return (Signature.Public_key_hash.Map.add pkh slots acc))
|
||||
| None -> (pk, [slot], false)
|
||||
| Some (pk, slots, used) -> (pk, slot :: slots, used) in
|
||||
return (Signature.Public_key_hash.Map.add pkh right acc))
|
||||
Signature.Public_key_hash.Map.empty
|
||||
(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)
|
||||
else
|
||||
endorsement_rights ctxt (Level.from_raw ctxt level)
|
||||
end >>=? fun map ->
|
||||
end >>=? fun endorsements ->
|
||||
match
|
||||
Signature.Public_key_hash.Map.find_first_opt
|
||||
(fun pkh ->
|
||||
let pk, _ = Signature.Public_key_hash.Map.find pkh map in
|
||||
match Operation.raw_check_signature pk op with
|
||||
| Error _ -> false
|
||||
| Ok () -> true)
|
||||
map
|
||||
Signature.Public_key_hash.Map.fold (* no find_first *)
|
||||
(fun pkh (pk, slots, used) acc ->
|
||||
match Operation.check_signature_sync pk op with
|
||||
| Error _ -> acc
|
||||
| Ok () -> Some (pkh, slots, used))
|
||||
endorsements None
|
||||
with
|
||||
| None ->
|
||||
fail Unexpected_endorsement
|
||||
| Some (pkh, (_pk, slots)) ->
|
||||
return (pkh, slots)
|
||||
| None -> fail Unexpected_endorsement
|
||||
| Some v -> return v
|
||||
|
||||
let select_delegate delegate delegate_list max_priority =
|
||||
let rec loop acc l n =
|
||||
|
@ -31,18 +31,19 @@ val check_baking_rights:
|
||||
context -> Block_header.contents -> Time.t ->
|
||||
public_key tzresult Lwt.t
|
||||
|
||||
(** [check_endorsements_rights c slots]:
|
||||
* verifies that the endorsement slots are valid ;
|
||||
* verifies that the endorsement slots correspond to the same delegate at the current level;
|
||||
*)
|
||||
val check_endorsement_rights:
|
||||
context -> Kind.endorsement Operation.t ->
|
||||
(public_key_hash * int list) tzresult Lwt.t
|
||||
|
||||
(** For a given level computes who has the right to
|
||||
include an endorsement in the next block.
|
||||
The result can be stored in Alpha_context.allowed_endorsements *)
|
||||
val endorsement_rights:
|
||||
context ->
|
||||
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. *)
|
||||
val endorsement_reward: context -> block_priority:int -> int -> Tez.t tzresult Lwt.t
|
||||
|
@ -460,7 +460,7 @@ module Endorsing_rights = struct
|
||||
RPC_service.get_service
|
||||
~description:
|
||||
"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\
|
||||
Parameters `level` and `cycle` can be used to specify 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 ->
|
||||
return
|
||||
(Signature.Public_key_hash.Map.fold
|
||||
(fun delegate (_, slots) acc -> {
|
||||
(fun delegate (_, slots, _) acc -> {
|
||||
level = level.level ; delegate ; slots ; estimated_time
|
||||
} :: acc)
|
||||
rights [])
|
||||
@ -529,4 +529,3 @@ let baking_rights ctxt max_priority =
|
||||
List.map
|
||||
(fun { Baking_rights.delegate ; timestamp ; _ } ->
|
||||
(delegate, timestamp)) l)
|
||||
|
||||
|
@ -646,7 +646,7 @@ let () =
|
||||
(function Missing_signature -> Some () | _ -> None)
|
||||
(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 unsigned_operation =
|
||||
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
|
||||
|
||||
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 (o : _ operation) =
|
||||
|
@ -165,7 +165,7 @@ type error += Invalid_signature (* `Permanent *)
|
||||
|
||||
val check_signature:
|
||||
Signature.Public_key.t -> _ operation -> unit tzresult Lwt.t
|
||||
val raw_check_signature:
|
||||
val check_signature_sync:
|
||||
Signature.Public_key.t -> _ operation -> unit tzresult
|
||||
|
||||
|
||||
|
@ -18,7 +18,7 @@ type t = {
|
||||
fitness: Int64.t ;
|
||||
deposits: Tez_repr.t Signature.Public_key_hash.Map.t ;
|
||||
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 ;
|
||||
rewards: Tez_repr.t ;
|
||||
block_gas: Z.t ;
|
||||
@ -41,11 +41,23 @@ let constants ctxt = ctxt.constants
|
||||
let recover ctxt = ctxt.context
|
||||
|
||||
let record_endorsement ctxt k =
|
||||
{ ctxt with
|
||||
allowed_endorsements =
|
||||
Signature.Public_key_hash.Map.remove k ctxt.allowed_endorsements }
|
||||
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
|
||||
allowed_endorsements =
|
||||
Signature.Public_key_hash.Map.add k (d,s,true) 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 =
|
||||
ctxt.allowed_endorsements
|
||||
|
||||
|
@ -205,12 +205,19 @@ val record_internal_nonce: context -> int -> context
|
||||
(** Check is the internal operation nonce has been taken. *)
|
||||
val internal_nonce_already_recorded: context -> int -> bool
|
||||
|
||||
val record_endorsement:
|
||||
context -> Signature.Public_key_hash.t -> context
|
||||
(** Returns a map where to each endorser's pkh is associated the list of its
|
||||
endorsing slots (in decreasing order) for a given level. *)
|
||||
val allowed_endorsements:
|
||||
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:
|
||||
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
|
||||
|
||||
(** Marks an endorsment in the map as used. *)
|
||||
val record_endorsement:
|
||||
context -> Signature.Public_key_hash.t -> context
|
||||
|
Loading…
Reference in New Issue
Block a user