(**************************************************************************) (* *) (* Copyright (c) 2014 - 2016. *) (* Dynamic Ledger Solutions, Inc. *) (* *) (* All rights reserved. No warranty, explicit or implicit, provided. *) (* *) (**************************************************************************) open Misc open Tezos_context type error += | Too_early of Timestamp.t * Timestamp.t | Invalid_level of Raw_level.t * Raw_level.t | Cannot_pay_mining_bond | Cannot_pay_endorsement_bond | Bad_slot | Bad_delegate let minimal_time c priority = Timestamp.get_current c >>=? fun prev_timestamp -> Lwt.return (Period.mult (Int32.succ priority) (Constants.time_between_slots c)) >>=? fun period -> Lwt.return Timestamp.(prev_timestamp +? period) let check_timestamp c priority timestamp = minimal_time c priority >>=? fun minimal_time -> fail_unless Timestamp.(minimal_time <= timestamp) (Too_early (minimal_time, timestamp)) let check_mining_rights c { Block.shell = { timestamp } ; proto = { mining_slot = (raw_level, priority) } } = Level.current c >>=? fun current_level -> fail_unless Raw_level.(raw_level = current_level.level) (Invalid_level (current_level.Level.level, raw_level)) >>=? fun () -> let level = Level.from_raw c raw_level in Roll.mining_rights_owner c level ~priority >>=? fun delegate -> check_timestamp c priority timestamp >>=? fun () -> return delegate let pay_mining_bond c { Block.proto = { mining_slot = (_raw_level, priority) } } id = if Compare.Int32.(priority >= Constants.first_free_mining_slot c) then return c else Contract.unconditional_spend c (Contract.default_contract id) Constants.mining_bond_cost |> trace Cannot_pay_mining_bond let pay_endorsement_bond c id = let bond = Constants.endorsement_bond_cost in Contract.unconditional_spend c (Contract.default_contract id) bond |> trace Cannot_pay_endorsement_bond >>=? fun c -> return (c, bond) let check_signing_rights c slot delegate = fail_unless Compare.Int.(slot <= Constants.max_signing_slot c) Bad_slot >>=? fun () -> Level.current c >>=? fun level -> Roll.endorsement_rights_owner c level ~slot >>=? fun owning_delegate -> fail_unless (Ed25519.Public_key_hash.equal owning_delegate delegate) Bad_delegate let paying_priorities c = 0l ---> Constants.first_free_mining_slot c let bond_and_reward = match Tez.(Constants.mining_bond_cost +? Constants.mining_reward) with | Ok v -> v | Error _ -> assert false let base_mining_reward c ~priority = if Compare.Int32.(priority < Constants.first_free_mining_slot c) then bond_and_reward else Constants.mining_reward type error += Incorect_priority let endorsement_reward ~block_priority:prio = if Compare.Int32.(prio >= 0l) then return Tez.(Constants.endorsement_reward / (Int64.(succ (of_int32 prio)))) else fail Incorect_priority let mining_priorities c level = let rec f priority = Roll.mining_rights_owner c level ~priority >>=? fun delegate -> return (LCons (delegate, (fun () -> f (Int32.succ priority)))) in f 0l let endorsement_priorities c level = let rec f slot = Roll.endorsement_rights_owner c level ~slot >>=? fun delegate -> return (LCons (delegate, (fun () -> f (succ slot)))) in f 0 let select_delegate delegate delegate_list max_priority = let rec loop acc l n = if Compare.Int32.(n >= max_priority) then return (List.rev acc) else let LCons (pkh, t) = l in let acc = if Ed25519.Public_key_hash.equal delegate pkh then n :: acc else acc in t () >>=? fun t -> loop acc t (Int32.succ n) in loop [] delegate_list 0l let first_mining_priorities ctxt ?(max_priority = Constants.first_free_mining_slot ctxt) delegate level = mining_priorities ctxt level >>=? fun delegate_list -> select_delegate delegate delegate_list max_priority let first_endorsement_slots ctxt ?(max_priority = Int32.of_int (Constants.max_signing_slot ctxt)) delegate level = endorsement_priorities ctxt level >>=? fun delegate_list -> select_delegate delegate delegate_list max_priority let check_hash hash stamp_threshold = let bytes = Block_hash.to_bytes hash in let len = MBytes.length bytes * 8 in try for i = len - 1 downto (len - stamp_threshold) do if MBytes.get_bool bytes i then raise Exit done; true with Exit -> false let check_header_hash {Block.shell;proto;signature} stamp_threshold = let hash = Block_hash.hash_bytes [ Data_encoding.Binary.to_bytes (Data_encoding.tup2 Block.unsigned_header_encoding Ed25519.signature_encoding) ((shell, proto), signature)] in check_hash hash stamp_threshold type error += | Invalid_signature | Invalid_stamp let check_proof_of_work_stamp ctxt block_header = let proof_of_work_threshold = Constants.proof_of_work_threshold ctxt in if check_header_hash block_header proof_of_work_threshold then return () else fail Invalid_stamp let check_signature ctxt block_header id = Public_key.get ctxt id >>=? fun key -> let check_signature key { Block.proto ; shell ; signature } = let unsigned_header = Block.forge_header shell proto in Ed25519.check_signature key signature unsigned_header in if check_signature key block_header then return () else fail Invalid_signature let max_fitness_gap ctxt = let slots = Int64.of_int (Constants.max_signing_slot ctxt + 1) in Int64.add slots 1L type error += Invalid_fitness_gap let check_fitness_gap ctxt (block_header : Block.header) = Fitness.raw_get ctxt >>=? fun current_fitness -> Fitness.raw_read block_header.shell.fitness >>=? fun announced_fitness -> let gap = Int64.sub announced_fitness current_fitness in if Compare.Int64.(gap <= 0L || max_fitness_gap ctxt < gap) then fail Invalid_fitness_gap else return () let first_of_a_cycle l = Compare.Int32.(l.Level.cycle_position = 0l) let dawn_of_a_new_cycle ctxt = Level.current ctxt >>=? fun level -> if first_of_a_cycle level then return (Some level.cycle) else return None