2019-09-05 17:21:01 +04:00
|
|
|
(*****************************************************************************)
|
|
|
|
(* *)
|
|
|
|
(* Open Source License *)
|
|
|
|
(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
|
|
|
(* *)
|
|
|
|
(* 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. *)
|
|
|
|
(* *)
|
|
|
|
(*****************************************************************************)
|
|
|
|
|
|
|
|
open Alpha_context
|
|
|
|
open Misc
|
|
|
|
|
|
|
|
type error += Invalid_fitness_gap of int64 * int64 (* `Permanent *)
|
2020-02-12 20:40:17 +04:00
|
|
|
|
|
|
|
type error += Timestamp_too_early of Timestamp.t * Timestamp.t
|
|
|
|
|
|
|
|
(* `Permanent *)
|
|
|
|
|
2019-09-05 17:21:01 +04:00
|
|
|
type error += Unexpected_endorsement (* `Permanent *)
|
2020-02-12 20:40:17 +04:00
|
|
|
|
|
|
|
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 *)
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let () =
|
|
|
|
register_error_kind
|
|
|
|
`Permanent
|
|
|
|
~id:"baking.timestamp_too_early"
|
|
|
|
~title:"Block forged too early"
|
2020-02-12 20:40:17 +04:00
|
|
|
~description:
|
|
|
|
"The block timestamp is before the first slot for this baker at this \
|
|
|
|
level"
|
2019-09-05 17:21:01 +04:00
|
|
|
~pp:(fun ppf (r, p) ->
|
2020-02-12 20:40:17 +04:00
|
|
|
Format.fprintf
|
|
|
|
ppf
|
|
|
|
"Block forged too early (%a is before %a)"
|
|
|
|
Time.pp_hum
|
|
|
|
p
|
|
|
|
Time.pp_hum
|
|
|
|
r)
|
|
|
|
Data_encoding.(
|
|
|
|
obj2 (req "minimum" Time.encoding) (req "provided" Time.encoding))
|
|
|
|
(function Timestamp_too_early (r, p) -> Some (r, p) | _ -> None)
|
2019-09-05 17:21:01 +04:00
|
|
|
(fun (r, p) -> Timestamp_too_early (r, p)) ;
|
|
|
|
register_error_kind
|
|
|
|
`Permanent
|
|
|
|
~id:"baking.invalid_fitness_gap"
|
|
|
|
~title:"Invalid fitness gap"
|
|
|
|
~description:"The gap of fitness is out of bounds"
|
|
|
|
~pp:(fun ppf (m, g) ->
|
2020-02-12 20:40:17 +04:00
|
|
|
Format.fprintf ppf "The gap of fitness %Ld is not between 0 and %Ld" g m)
|
|
|
|
Data_encoding.(obj2 (req "maximum" int64) (req "provided" int64))
|
|
|
|
(function Invalid_fitness_gap (m, g) -> Some (m, g) | _ -> None)
|
2019-09-05 17:21:01 +04:00
|
|
|
(fun (m, g) -> Invalid_fitness_gap (m, g)) ;
|
|
|
|
register_error_kind
|
|
|
|
`Permanent
|
|
|
|
~id:"baking.invalid_block_signature"
|
|
|
|
~title:"Invalid block signature"
|
2020-02-12 20:40:17 +04:00
|
|
|
~description:"A block was not signed with the expected private key."
|
2019-09-05 17:21:01 +04:00
|
|
|
~pp:(fun ppf (block, pkh) ->
|
2020-02-12 20:40:17 +04:00
|
|
|
Format.fprintf
|
|
|
|
ppf
|
|
|
|
"Invalid signature for block %a. Expected: %a."
|
|
|
|
Block_hash.pp_short
|
|
|
|
block
|
|
|
|
Signature.Public_key_hash.pp_short
|
|
|
|
pkh)
|
|
|
|
Data_encoding.(
|
|
|
|
obj2
|
|
|
|
(req "block" Block_hash.encoding)
|
|
|
|
(req "expected" Signature.Public_key_hash.encoding))
|
|
|
|
(function
|
|
|
|
| Invalid_block_signature (block, pkh) -> Some (block, pkh) | _ -> None)
|
|
|
|
(fun (block, pkh) -> Invalid_block_signature (block, pkh)) ;
|
2019-09-05 17:21:01 +04:00
|
|
|
register_error_kind
|
|
|
|
`Permanent
|
|
|
|
~id:"baking.invalid_signature"
|
|
|
|
~title:"Invalid block signature"
|
|
|
|
~description:"The block's signature is invalid"
|
2020-02-12 20:40:17 +04:00
|
|
|
~pp:(fun ppf () -> Format.fprintf ppf "Invalid block signature")
|
2019-09-05 17:21:01 +04:00
|
|
|
Data_encoding.empty
|
|
|
|
(function Invalid_signature -> Some () | _ -> None)
|
|
|
|
(fun () -> Invalid_signature) ;
|
|
|
|
register_error_kind
|
|
|
|
`Permanent
|
|
|
|
~id:"baking.insufficient_proof_of_work"
|
|
|
|
~title:"Insufficient block proof-of-work stamp"
|
|
|
|
~description:"The block's proof-of-work stamp is insufficient"
|
2020-02-12 20:40:17 +04:00
|
|
|
~pp:(fun ppf () -> Format.fprintf ppf "Insufficient proof-of-work stamp")
|
2019-09-05 17:21:01 +04:00
|
|
|
Data_encoding.empty
|
|
|
|
(function Invalid_stamp -> Some () | _ -> None)
|
|
|
|
(fun () -> Invalid_stamp) ;
|
|
|
|
register_error_kind
|
|
|
|
`Permanent
|
|
|
|
~id:"baking.unexpected_endorsement"
|
|
|
|
~title:"Endorsement from unexpected delegate"
|
2020-02-12 20:40:17 +04:00
|
|
|
~description:
|
|
|
|
"The operation is signed by a delegate without endorsement rights."
|
2019-09-05 17:21:01 +04:00
|
|
|
~pp:(fun ppf () ->
|
2020-02-12 20:40:17 +04:00
|
|
|
Format.fprintf
|
|
|
|
ppf
|
|
|
|
"The endorsement is signed by a delegate without endorsement rights.")
|
2019-09-05 17:21:01 +04:00
|
|
|
Data_encoding.unit
|
|
|
|
(function Unexpected_endorsement -> Some () | _ -> None)
|
|
|
|
(fun () -> Unexpected_endorsement)
|
|
|
|
|
|
|
|
let minimal_time c priority pred_timestamp =
|
|
|
|
let priority = Int32.of_int priority in
|
|
|
|
let rec cumsum_time_between_blocks acc durations p =
|
2020-02-12 20:40:17 +04:00
|
|
|
if Compare.Int32.( <= ) p 0l then ok acc
|
|
|
|
else
|
|
|
|
match durations with
|
|
|
|
| [] ->
|
|
|
|
cumsum_time_between_blocks acc [Period.one_minute] p
|
|
|
|
| [last] ->
|
|
|
|
Period.mult p last >>? fun period -> Timestamp.(acc +? period)
|
2019-09-05 17:21:01 +04:00
|
|
|
| first :: durations ->
|
2020-02-12 20:40:17 +04:00
|
|
|
Timestamp.(acc +? first)
|
|
|
|
>>? fun acc ->
|
2019-09-05 17:21:01 +04:00
|
|
|
let p = Int32.pred p in
|
2020-02-12 20:40:17 +04:00
|
|
|
cumsum_time_between_blocks acc durations p
|
|
|
|
in
|
2019-09-05 17:21:01 +04:00
|
|
|
Lwt.return
|
|
|
|
(cumsum_time_between_blocks
|
2020-02-12 20:40:17 +04:00
|
|
|
pred_timestamp
|
|
|
|
(Constants.time_between_blocks c)
|
|
|
|
(Int32.succ priority))
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let earlier_predecessor_timestamp ctxt level =
|
|
|
|
let current = Level.current ctxt in
|
|
|
|
let current_timestamp = Timestamp.current ctxt in
|
|
|
|
let gap = Level.diff level current in
|
|
|
|
let step = List.hd (Constants.time_between_blocks ctxt) in
|
|
|
|
if Compare.Int32.(gap < 1l) then
|
|
|
|
failwith "Baking.earlier_block_timestamp: past block."
|
|
|
|
else
|
2020-02-12 20:40:17 +04:00
|
|
|
Lwt.return (Period.mult (Int32.pred gap) step)
|
|
|
|
>>=? fun delay ->
|
|
|
|
Lwt.return Timestamp.(current_timestamp +? delay)
|
|
|
|
>>=? fun result -> return result
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let check_timestamp c priority pred_timestamp =
|
2020-02-12 20:40:17 +04:00
|
|
|
minimal_time c priority pred_timestamp
|
|
|
|
>>=? fun minimal_time ->
|
2019-09-05 17:21:01 +04:00
|
|
|
let timestamp = Alpha_context.Timestamp.current c in
|
2019-10-17 13:45:27 +04:00
|
|
|
Lwt.return
|
2020-02-12 20:40:17 +04:00
|
|
|
(record_trace
|
|
|
|
(Timestamp_too_early (minimal_time, timestamp))
|
2019-10-17 13:45:27 +04:00
|
|
|
Timestamp.(timestamp -? minimal_time))
|
2019-09-05 17:21:01 +04:00
|
|
|
|
2020-02-12 20:40:17 +04:00
|
|
|
let check_baking_rights c {Block_header.priority; _} pred_timestamp =
|
2019-09-05 17:21:01 +04:00
|
|
|
let level = Level.current c in
|
2020-02-12 20:40:17 +04:00
|
|
|
Roll.baking_rights_owner c level ~priority
|
|
|
|
>>=? fun delegate ->
|
|
|
|
check_timestamp c priority pred_timestamp
|
|
|
|
>>=? fun block_delay -> return (delegate, block_delay)
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
type error += Incorrect_priority (* `Permanent *)
|
2020-02-12 20:40:17 +04:00
|
|
|
|
2019-10-17 13:45:27 +04:00
|
|
|
type error += Incorrect_number_of_endorsements (* `Permanent *)
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let () =
|
|
|
|
register_error_kind
|
|
|
|
`Permanent
|
|
|
|
~id:"incorrect_priority"
|
|
|
|
~title:"Incorrect priority"
|
|
|
|
~description:"Block priority must be non-negative."
|
|
|
|
~pp:(fun ppf () ->
|
2020-02-12 20:40:17 +04:00
|
|
|
Format.fprintf ppf "The block priority must be non-negative.")
|
2019-09-05 17:21:01 +04:00
|
|
|
Data_encoding.unit
|
|
|
|
(function Incorrect_priority -> Some () | _ -> None)
|
|
|
|
(fun () -> Incorrect_priority)
|
|
|
|
|
2019-10-17 13:45:27 +04:00
|
|
|
let () =
|
2020-02-12 20:40:17 +04:00
|
|
|
let description =
|
|
|
|
"The number of endorsements must be non-negative and at most the \
|
|
|
|
endosers_per_block constant."
|
|
|
|
in
|
2019-10-17 13:45:27 +04:00
|
|
|
register_error_kind
|
|
|
|
`Permanent
|
|
|
|
~id:"incorrect_number_of_endorsements"
|
|
|
|
~title:"Incorrect number of endorsements"
|
|
|
|
~description
|
|
|
|
~pp:(fun ppf () -> Format.fprintf ppf "%s" description)
|
|
|
|
Data_encoding.unit
|
|
|
|
(function Incorrect_number_of_endorsements -> Some () | _ -> None)
|
|
|
|
(fun () -> Incorrect_number_of_endorsements)
|
|
|
|
|
2020-02-12 20:40:17 +04:00
|
|
|
let rec reward_for_priority reward_per_prio prio =
|
|
|
|
match reward_per_prio with
|
|
|
|
| [] ->
|
|
|
|
(* Empty reward list in parameters means no rewards *)
|
|
|
|
Tez.zero
|
|
|
|
| [last] ->
|
|
|
|
last
|
|
|
|
| first :: rest ->
|
|
|
|
if Compare.Int.(prio <= 0) then first
|
|
|
|
else reward_for_priority rest (pred prio)
|
|
|
|
|
|
|
|
let baking_reward ctxt ~block_priority ~included_endorsements =
|
|
|
|
fail_unless Compare.Int.(block_priority >= 0) Incorrect_priority
|
|
|
|
>>=? fun () ->
|
|
|
|
fail_unless
|
|
|
|
Compare.Int.(
|
|
|
|
included_endorsements >= 0
|
|
|
|
&& included_endorsements <= Constants.endorsers_per_block ctxt)
|
|
|
|
Incorrect_number_of_endorsements
|
|
|
|
>>=? fun () ->
|
|
|
|
let reward_per_endorsement =
|
|
|
|
reward_for_priority
|
|
|
|
(Constants.baking_reward_per_endorsement ctxt)
|
|
|
|
block_priority
|
|
|
|
in
|
|
|
|
Lwt.return Tez.(reward_per_endorsement *? Int64.of_int included_endorsements)
|
|
|
|
|
|
|
|
let endorsing_reward ctxt ~block_priority num_slots =
|
|
|
|
fail_unless Compare.Int.(block_priority >= 0) Incorrect_priority
|
|
|
|
>>=? fun () ->
|
|
|
|
let reward_per_endorsement =
|
|
|
|
reward_for_priority (Constants.endorsement_reward ctxt) block_priority
|
|
|
|
in
|
|
|
|
Lwt.return Tez.(reward_per_endorsement *? Int64.of_int num_slots)
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let baking_priorities c level =
|
|
|
|
let rec f priority =
|
2020-02-12 20:40:17 +04:00
|
|
|
Roll.baking_rights_owner c level ~priority
|
|
|
|
>>=? fun delegate -> return (LCons (delegate, fun () -> f (succ priority)))
|
2019-09-05 17:21:01 +04:00
|
|
|
in
|
|
|
|
f 0
|
|
|
|
|
2020-02-12 20:40:17 +04:00
|
|
|
let endorsement_rights ctxt level =
|
2019-09-05 17:21:01 +04:00
|
|
|
fold_left_s
|
|
|
|
(fun acc slot ->
|
2020-02-12 20:40:17 +04:00
|
|
|
Roll.endorsement_rights_owner ctxt level ~slot
|
|
|
|
>>=? fun pk ->
|
|
|
|
let pkh = Signature.Public_key.hash pk in
|
|
|
|
let right =
|
|
|
|
match Signature.Public_key_hash.Map.find_opt pkh acc with
|
|
|
|
| None ->
|
|
|
|
(pk, [slot], false)
|
|
|
|
| Some (pk, slots, used) ->
|
|
|
|
(pk, slot :: slots, used)
|
|
|
|
in
|
|
|
|
return (Signature.Public_key_hash.Map.add pkh right acc))
|
2019-09-05 17:21:01 +04:00
|
|
|
Signature.Public_key_hash.Map.empty
|
2020-02-12 20:40:17 +04:00
|
|
|
(0 --> (Constants.endorsers_per_block ctxt - 1))
|
2019-09-05 17:21:01 +04:00
|
|
|
|
2020-02-12 20:40:17 +04:00
|
|
|
let check_endorsement_rights ctxt chain_id (op : Kind.endorsement Operation.t)
|
|
|
|
=
|
2019-09-05 17:21:01 +04:00
|
|
|
let current_level = Level.current ctxt in
|
2020-02-12 20:40:17 +04:00
|
|
|
let (Single (Endorsement {level; _})) = op.protocol_data.contents in
|
|
|
|
( if Raw_level.(succ level = current_level.level) then
|
|
|
|
return (Alpha_context.allowed_endorsements ctxt)
|
|
|
|
else endorsement_rights ctxt (Level.from_raw ctxt level) )
|
|
|
|
>>=? fun endorsements ->
|
2019-09-05 17:21:01 +04:00
|
|
|
match
|
|
|
|
Signature.Public_key_hash.Map.fold (* no find_first *)
|
|
|
|
(fun pkh (pk, slots, used) acc ->
|
2020-02-12 20:40:17 +04:00
|
|
|
match Operation.check_signature_sync pk chain_id op with
|
|
|
|
| Error _ ->
|
|
|
|
acc
|
|
|
|
| Ok () ->
|
|
|
|
Some (pkh, slots, used))
|
|
|
|
endorsements
|
|
|
|
None
|
2019-09-05 17:21:01 +04:00
|
|
|
with
|
2020-02-12 20:40:17 +04:00
|
|
|
| None ->
|
|
|
|
fail Unexpected_endorsement
|
|
|
|
| Some v ->
|
|
|
|
return v
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let select_delegate delegate delegate_list max_priority =
|
|
|
|
let rec loop acc l n =
|
2020-02-12 20:40:17 +04:00
|
|
|
if Compare.Int.(n >= max_priority) then return (List.rev acc)
|
2019-09-05 17:21:01 +04:00
|
|
|
else
|
2020-02-12 20:40:17 +04:00
|
|
|
let (LCons (pk, t)) = l in
|
2019-09-05 17:21:01 +04:00
|
|
|
let acc =
|
2020-02-12 20:40:17 +04:00
|
|
|
if
|
|
|
|
Signature.Public_key_hash.equal
|
|
|
|
delegate
|
|
|
|
(Signature.Public_key.hash pk)
|
2019-09-05 17:21:01 +04:00
|
|
|
then n :: acc
|
2020-02-12 20:40:17 +04:00
|
|
|
else acc
|
|
|
|
in
|
|
|
|
t () >>=? fun t -> loop acc t (succ n)
|
2019-09-05 17:21:01 +04:00
|
|
|
in
|
|
|
|
loop [] delegate_list 0
|
|
|
|
|
2020-02-12 20:40:17 +04:00
|
|
|
let first_baking_priorities ctxt ?(max_priority = 32) delegate level =
|
|
|
|
baking_priorities ctxt level
|
|
|
|
>>=? fun delegate_list -> select_delegate delegate delegate_list max_priority
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let check_hash hash stamp_threshold =
|
|
|
|
let bytes = Block_hash.to_bytes hash in
|
|
|
|
let word = MBytes.get_int64 bytes 0 in
|
|
|
|
Compare.Uint64.(word <= stamp_threshold)
|
|
|
|
|
|
|
|
let check_header_proof_of_work_stamp shell contents stamp_threshold =
|
|
|
|
let hash =
|
|
|
|
Block_header.hash
|
2020-02-12 20:40:17 +04:00
|
|
|
{shell; protocol_data = {contents; signature = Signature.zero}}
|
|
|
|
in
|
2019-09-05 17:21:01 +04:00
|
|
|
check_hash hash stamp_threshold
|
|
|
|
|
|
|
|
let check_proof_of_work_stamp ctxt block =
|
|
|
|
let proof_of_work_threshold = Constants.proof_of_work_threshold ctxt in
|
2020-02-12 20:40:17 +04:00
|
|
|
if
|
|
|
|
check_header_proof_of_work_stamp
|
2019-09-05 17:21:01 +04:00
|
|
|
block.Block_header.shell
|
|
|
|
block.protocol_data.contents
|
2020-02-12 20:40:17 +04:00
|
|
|
proof_of_work_threshold
|
|
|
|
then return_unit
|
|
|
|
else fail Invalid_stamp
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let check_signature block chain_id key =
|
|
|
|
let check_signature key
|
2020-02-12 20:40:17 +04:00
|
|
|
{Block_header.shell; protocol_data = {contents; signature}} =
|
2019-09-05 17:21:01 +04:00
|
|
|
let unsigned_header =
|
|
|
|
Data_encoding.Binary.to_bytes_exn
|
|
|
|
Block_header.unsigned_encoding
|
2020-02-12 20:40:17 +04:00
|
|
|
(shell, contents)
|
|
|
|
in
|
|
|
|
Signature.check
|
|
|
|
~watermark:(Block_header chain_id)
|
|
|
|
key
|
|
|
|
signature
|
|
|
|
unsigned_header
|
|
|
|
in
|
|
|
|
if check_signature key block then return_unit
|
2019-09-05 17:21:01 +04:00
|
|
|
else
|
2020-02-12 20:40:17 +04:00
|
|
|
fail
|
|
|
|
(Invalid_block_signature
|
|
|
|
(Block_header.hash block, Signature.Public_key.hash key))
|
2019-09-05 17:21:01 +04:00
|
|
|
|
2019-10-17 13:45:27 +04:00
|
|
|
let max_fitness_gap _ctxt = 1L
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let check_fitness_gap ctxt (block : Block_header.t) =
|
|
|
|
let current_fitness = Fitness.current ctxt in
|
2020-02-12 20:40:17 +04:00
|
|
|
Lwt.return (Fitness.to_int64 block.shell.fitness)
|
|
|
|
>>=? fun announced_fitness ->
|
2019-09-05 17:21:01 +04:00
|
|
|
let gap = Int64.sub announced_fitness current_fitness in
|
|
|
|
if Compare.Int64.(gap <= 0L || max_fitness_gap ctxt < gap) then
|
|
|
|
fail (Invalid_fitness_gap (max_fitness_gap ctxt, gap))
|
2020-02-12 20:40:17 +04:00
|
|
|
else return_unit
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let last_of_a_cycle ctxt l =
|
2020-02-12 20:40:17 +04:00
|
|
|
Compare.Int32.(
|
|
|
|
Int32.succ l.Level.cycle_position = Constants.blocks_per_cycle ctxt)
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let dawn_of_a_new_cycle ctxt =
|
|
|
|
let level = Level.current ctxt in
|
2020-02-12 20:40:17 +04:00
|
|
|
if last_of_a_cycle ctxt level then return_some level.cycle else return_none
|
2019-10-17 13:45:27 +04:00
|
|
|
|
|
|
|
let minimum_allowed_endorsements ctxt ~block_delay =
|
|
|
|
let minimum = Constants.initial_endorsers ctxt in
|
|
|
|
let delay_per_missing_endorsement =
|
|
|
|
Int64.to_int
|
2020-02-12 20:40:17 +04:00
|
|
|
(Period.to_seconds (Constants.delay_per_missing_endorsement ctxt))
|
2019-10-17 13:45:27 +04:00
|
|
|
in
|
|
|
|
let reduced_time_constraint =
|
|
|
|
let delay = Int64.to_int (Period.to_seconds block_delay) in
|
2020-02-12 20:40:17 +04:00
|
|
|
if Compare.Int.(delay_per_missing_endorsement = 0) then delay
|
|
|
|
else delay / delay_per_missing_endorsement
|
2019-10-17 13:45:27 +04:00
|
|
|
in
|
|
|
|
Compare.Int.max 0 (minimum - reduced_time_constraint)
|
|
|
|
|
|
|
|
let minimal_valid_time ctxt ~priority ~endorsing_power =
|
|
|
|
let predecessor_timestamp = Timestamp.current ctxt in
|
2020-02-12 20:40:17 +04:00
|
|
|
minimal_time ctxt priority predecessor_timestamp
|
|
|
|
>>=? fun minimal_time ->
|
2019-10-17 13:45:27 +04:00
|
|
|
let minimal_required_endorsements = Constants.initial_endorsers ctxt in
|
|
|
|
let delay_per_missing_endorsement =
|
|
|
|
Constants.delay_per_missing_endorsement ctxt
|
|
|
|
in
|
|
|
|
let missing_endorsements =
|
2020-02-12 20:40:17 +04:00
|
|
|
Compare.Int.max 0 (minimal_required_endorsements - endorsing_power)
|
|
|
|
in
|
|
|
|
match
|
|
|
|
Period.mult
|
|
|
|
(Int32.of_int missing_endorsements)
|
|
|
|
delay_per_missing_endorsement
|
|
|
|
with
|
2019-10-17 13:45:27 +04:00
|
|
|
| Ok delay ->
|
|
|
|
return (Time.add minimal_time (Period.to_seconds delay))
|
2020-02-12 20:40:17 +04:00
|
|
|
| Error _ as err ->
|
|
|
|
Lwt.return err
|