Raw File
validator.ml
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com>     *)
(* Copyright (c) 2018-2021 Nomadic Labs, <contact@nomadic-labs.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.                                                 *)
(*                                                                           *)
(*****************************************************************************)

type t = {
  state : Store.t;
  db : Distributed_db.t;
  block_validator : Block_validator.t;
  chain_validator_limits : Chain_validator.limits;
  peer_validator_limits : Peer_validator.limits;
  block_validator_limits : Block_validator.limits;
  prevalidator_limits : Prevalidator.limits;
  start_testchain : bool;
  valid_block_input : Store.Block.t Lwt_watcher.input;
  chains_input : (Chain_id.t * bool) Lwt_watcher.input;
  active_chains : Chain_validator.t Chain_id.Table.t;
}

let create state db peer_validator_limits block_validator_limits
    block_validator_kind prevalidator_limits chain_validator_limits
    ~start_testchain =
  let open Lwt_result_syntax in
  let*! block_validator =
    Block_validator.create
      block_validator_limits
      db
      block_validator_kind
      ~start_testchain
  in
  let valid_block_input = Lwt_watcher.create_input () in
  let chains_input = Lwt_watcher.create_input () in
  return
    {
      state;
      db;
      start_testchain;
      block_validator;
      block_validator_limits;
      prevalidator_limits;
      peer_validator_limits;
      chain_validator_limits;
      valid_block_input;
      chains_input;
      active_chains = Chain_id.Table.create 7;
    }

let activate v ~start_prevalidator ~validator_process chain_store =
  let open Lwt_syntax in
  let chain_id = Store.Chain.chain_id chain_store in
  let* () = Validator_event.(emit activate_chain) chain_id in
  match Chain_id.Table.find v.active_chains chain_id with
  | Some chain -> return_ok chain
  | None ->
      Chain_validator.create
        ~start_prevalidator
        ~start_testchain:v.start_testchain
        ~active_chains:v.active_chains
        ~block_validator_process:validator_process
        v.peer_validator_limits
        v.prevalidator_limits
        v.block_validator
        v.valid_block_input
        v.chains_input
        v.db
        chain_store
        v.chain_validator_limits

let get {active_chains; _} chain_id =
  let open Result_syntax in
  match Chain_id.Table.find active_chains chain_id with
  | Some nv -> return nv
  | None -> tzfail (Validation_errors.Inactive_chain chain_id)

let get_active_chains {active_chains; _} =
  let l = Chain_id.Table.fold (fun c _ acc -> c :: acc) active_chains [] in
  List.rev l

let read_block store h =
  let open Lwt_option_syntax in
  let*! chain_stores = Store.all_chain_stores store in
  List.find_map_s
    (fun chain_store ->
      let* b = Store.Block.read_block_opt chain_store h in
      let id = Store.Chain.chain_id chain_store in
      return (id, b))
    chain_stores

let read_block_header db h =
  let open Lwt_option_syntax in
  let* chain_id, block = read_block (Distributed_db.store db) h in
  let header = Store.Block.header block in
  return (chain_id, header)

let validate_block v ?(force = false) ?chain_id bytes operations =
  let open Lwt_result_syntax in
  let hash = Block_hash.hash_bytes [bytes] in
  match Block_header.of_bytes bytes with
  | None -> failwith "Cannot parse block header."
  | Some block ->
      if not (Clock_drift.is_not_too_far_in_the_future block.shell.timestamp)
      then failwith "Block in the future."
      else
        let* nv =
          match chain_id with
          | None -> (
              let*! o = read_block_header v.db block.shell.predecessor in
              match o with
              | None ->
                  failwith
                    "Unknown predecessor (%a), cannot inject the block."
                    Block_hash.pp_short
                    block.shell.predecessor
              | Some (chain_id, _bh) -> Lwt.return (get v chain_id))
          | Some chain_id -> (
              let* nv = Lwt.return (get v chain_id) in
              if force then return nv
              else
                let*! b =
                  Distributed_db.Block_header.known
                    (Chain_validator.chain_db nv)
                    block.shell.predecessor
                in
                match b with
                | true -> return nv
                | false ->
                    failwith
                      "Unknown predecessor (%a), cannot inject the block."
                      Block_hash.pp_short
                      block.shell.predecessor)
        in
        let validation =
          Chain_validator.validate_block nv ~force hash block operations
        in
        return (hash, validation)

let shutdown {active_chains; block_validator; _} =
  let open Lwt_syntax in
  let chain_validator_jobs =
    List.of_seq
    @@ Seq.map
         (fun (id, nv) ->
           let* () = Validator_event.(emit shutdown_chain_validator) id in
           Chain_validator.shutdown nv)
         (Chain_id.Table.to_seq active_chains)
  in
  (* Shutdown the chain_validator (peer_validators, prevalidator,
     etc.) before the block_validator *)
  let* () = Lwt.join chain_validator_jobs in
  let* () = Validator_event.(emit shutdown_block_validator) () in
  Block_validator.shutdown block_validator

let watcher {valid_block_input; _} = Lwt_watcher.create_stream valid_block_input

let chains_watcher {chains_input; _} = Lwt_watcher.create_stream chains_input

let inject_operation v ?chain_id ~force op =
  let open Lwt_result_syntax in
  let inject_operation_on nv ~handle_missing_prevalidator =
    match Chain_validator.prevalidator nv with
    | Some pv -> Prevalidator.inject_operation pv ~force op
    | None -> handle_missing_prevalidator
  in
  let handle_missing_prevalidator =
    failwith "Prevalidator is not running, cannot inject the operation."
  in
  match chain_id with
  | None -> (
      let*! o = read_block_header v.db op.Operation.shell.branch in
      match o with
      | None ->
          if force then
            Chain_id.Table.iter_es
              (fun _chain_id chain ->
                inject_operation_on
                  chain
                  ~handle_missing_prevalidator:return_unit)
              v.active_chains
          else
            failwith
              "Unknown branch (%a), cannot inject the operation."
              Block_hash.pp_short
              op.shell.branch
      | Some (chain_id, _bh) ->
          let*? nv = get v chain_id in
          inject_operation_on nv ~handle_missing_prevalidator)
  | Some chain_id ->
      let*? nv = get v chain_id in
      inject_operation_on nv ~handle_missing_prevalidator

let distributed_db {db; _} = db
back to top