2017-11-11 06:34:12 +04:00
|
|
|
(**************************************************************************)
|
|
|
|
(* *)
|
|
|
|
(* Copyright (c) 2014 - 2017. *)
|
|
|
|
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
|
|
|
(* *)
|
|
|
|
(* All rights reserved. No warranty, explicit or implicit, provided. *)
|
|
|
|
(* *)
|
|
|
|
(**************************************************************************)
|
|
|
|
|
|
|
|
(* FIXME ignore/postpone fetching/validating of block in the future... *)
|
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
open Peer_validator_worker_state
|
2017-11-11 06:34:12 +04:00
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
module Name = struct
|
|
|
|
type t = Net_id.t * P2p.Peer_id.t
|
|
|
|
let encoding =
|
|
|
|
Data_encoding.tup2 Net_id.encoding P2p.Peer_id.encoding
|
|
|
|
let base = [ "peer_validator" ]
|
|
|
|
let pp ppf (net, peer) =
|
|
|
|
Format.fprintf ppf "%a:%a"
|
|
|
|
Net_id.pp_short net P2p.Peer_id.pp_short peer
|
|
|
|
end
|
2017-11-11 06:34:12 +04:00
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
module Request = struct
|
|
|
|
include Request
|
2017-11-11 06:34:12 +04:00
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
type _ t =
|
|
|
|
| New_head : Block_hash.t * Block_header.t -> unit t
|
|
|
|
| New_branch : Block_hash.t * Block_locator.t -> unit t
|
2017-11-11 06:34:12 +04:00
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
let view (type a) (req : a t) : view = match req with
|
|
|
|
| New_head (hash, _) ->
|
|
|
|
New_head hash
|
|
|
|
| New_branch (hash, locator) ->
|
|
|
|
New_branch (hash, Block_locator_iterator.estimated_length locator)
|
|
|
|
end
|
|
|
|
|
|
|
|
type limits = {
|
2017-11-13 17:25:02 +04:00
|
|
|
new_head_request_timeout: float ;
|
|
|
|
block_header_timeout: float ;
|
|
|
|
block_operations_timeout: float ;
|
|
|
|
protocol_timeout: float ;
|
2018-01-22 20:47:18 +04:00
|
|
|
worker_limits: Worker_types.limits
|
|
|
|
}
|
2017-11-13 17:25:02 +04:00
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
module Types = struct
|
|
|
|
include Worker_state
|
2017-11-11 06:34:12 +04:00
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
type parameters = {
|
|
|
|
net_db: Distributed_db.net_db ;
|
|
|
|
block_validator: Block_validator.t ;
|
|
|
|
(* callback to net_validator *)
|
|
|
|
notify_new_block: State.Block.t -> unit ;
|
|
|
|
notify_bootstrapped: unit -> unit ;
|
|
|
|
notify_termination: unit -> unit ;
|
|
|
|
limits: limits;
|
|
|
|
}
|
2017-11-11 06:34:12 +04:00
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
type state = {
|
|
|
|
peer_id: P2p.Peer_id.t ;
|
|
|
|
parameters : parameters ;
|
|
|
|
mutable bootstrapped: bool ;
|
|
|
|
mutable last_validated_head: Block_header.t ;
|
|
|
|
mutable last_advertised_head: Block_header.t ;
|
|
|
|
}
|
2017-11-11 06:34:12 +04:00
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
let view (state : state) _ : view =
|
|
|
|
let { bootstrapped ; last_validated_head ; last_advertised_head } = state in
|
|
|
|
{ bootstrapped ;
|
|
|
|
last_validated_head = Block_header.hash last_validated_head ;
|
|
|
|
last_advertised_head = Block_header.hash last_advertised_head }
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
module Worker = Worker.Make (Name) (Event) (Request) (Types)
|
|
|
|
|
|
|
|
open Types
|
|
|
|
|
|
|
|
type t = Worker.dropbox Worker.t
|
|
|
|
|
|
|
|
let debug w =
|
|
|
|
Format.kasprintf (fun msg -> Worker.record_event w (Debug msg))
|
2017-11-11 06:34:12 +04:00
|
|
|
|
|
|
|
type error +=
|
|
|
|
| Unknown_ancestor
|
|
|
|
| Known_invalid
|
|
|
|
|
|
|
|
let set_bootstrapped pv =
|
|
|
|
if not pv.bootstrapped then begin
|
|
|
|
pv.bootstrapped <- true ;
|
2018-01-22 20:47:18 +04:00
|
|
|
pv.parameters.notify_bootstrapped () ;
|
2017-11-11 06:34:12 +04:00
|
|
|
end
|
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
let bootstrap_new_branch w _ancestor _head unknown_prefix =
|
|
|
|
let pv = Worker.state w in
|
2017-12-17 22:51:06 +04:00
|
|
|
let len = Block_locator_iterator.estimated_length unknown_prefix in
|
2018-01-22 20:47:18 +04:00
|
|
|
debug w
|
2017-11-11 06:34:12 +04:00
|
|
|
"validating new branch from peer %a (approx. %d blocks)"
|
2018-01-22 20:47:18 +04:00
|
|
|
P2p.Peer_id.pp_short pv.peer_id len ;
|
2017-11-11 06:34:12 +04:00
|
|
|
let pipeline =
|
|
|
|
Bootstrap_pipeline.create
|
2018-01-22 20:47:18 +04:00
|
|
|
~notify_new_block:pv.parameters.notify_new_block
|
|
|
|
~block_header_timeout:pv.parameters.limits.block_header_timeout
|
|
|
|
~block_operations_timeout:pv.parameters.limits.block_operations_timeout
|
|
|
|
pv.parameters.block_validator
|
|
|
|
pv.peer_id pv.parameters.net_db unknown_prefix in
|
|
|
|
Worker.protect w
|
2017-11-11 06:34:12 +04:00
|
|
|
~on_error:begin fun error ->
|
|
|
|
(* if the peer_validator is killed, let's cancel the pipeline *)
|
|
|
|
Bootstrap_pipeline.cancel pipeline >>= fun () ->
|
|
|
|
Lwt.return_error error
|
|
|
|
end
|
|
|
|
begin fun () ->
|
|
|
|
Bootstrap_pipeline.wait pipeline
|
|
|
|
end >>=? fun () ->
|
|
|
|
set_bootstrapped pv ;
|
2018-01-22 20:47:18 +04:00
|
|
|
debug w
|
2017-11-11 06:34:12 +04:00
|
|
|
"done validating new branch from peer %a."
|
2018-01-22 20:47:18 +04:00
|
|
|
P2p.Peer_id.pp_short pv.peer_id ;
|
2017-11-11 06:34:12 +04:00
|
|
|
return ()
|
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
let validate_new_head w hash (header : Block_header.t) =
|
|
|
|
let pv = Worker.state w in
|
|
|
|
let net_state = Distributed_db.net_state pv.parameters.net_db in
|
2017-11-11 06:34:12 +04:00
|
|
|
State.Block.known net_state header.shell.predecessor >>= function
|
|
|
|
| false ->
|
2018-01-22 20:47:18 +04:00
|
|
|
debug w
|
2017-11-11 06:34:12 +04:00
|
|
|
"missing predecessor for new head %a from peer %a"
|
|
|
|
Block_hash.pp_short hash
|
2018-01-22 20:47:18 +04:00
|
|
|
P2p.Peer_id.pp_short pv.peer_id ;
|
|
|
|
Distributed_db.Request.current_branch pv.parameters.net_db ~peer:pv.peer_id () ;
|
2017-11-11 06:34:12 +04:00
|
|
|
return ()
|
|
|
|
| true ->
|
2018-01-22 20:47:18 +04:00
|
|
|
debug w
|
2017-11-11 06:34:12 +04:00
|
|
|
"fetching operations for new head %a from peer %a"
|
|
|
|
Block_hash.pp_short hash
|
2018-01-22 20:47:18 +04:00
|
|
|
P2p.Peer_id.pp_short pv.peer_id ;
|
2017-11-11 06:34:12 +04:00
|
|
|
map_p
|
|
|
|
(fun i ->
|
2018-01-22 20:47:18 +04:00
|
|
|
Worker.protect w begin fun () ->
|
2017-11-11 06:34:12 +04:00
|
|
|
Distributed_db.Operations.fetch
|
2018-01-22 20:47:18 +04:00
|
|
|
~timeout:pv.parameters.limits.block_operations_timeout
|
|
|
|
pv.parameters.net_db ~peer:pv.peer_id
|
2017-11-11 06:34:12 +04:00
|
|
|
(hash, i) header.shell.operations_hash
|
|
|
|
end)
|
|
|
|
(0 -- (header.shell.validation_passes - 1)) >>=? fun operations ->
|
2018-01-22 20:47:18 +04:00
|
|
|
debug w
|
2017-11-11 06:34:12 +04:00
|
|
|
"requesting validation for new head %a from peer %a"
|
|
|
|
Block_hash.pp_short hash
|
2018-01-22 20:47:18 +04:00
|
|
|
P2p.Peer_id.pp_short pv.peer_id ;
|
2017-11-11 06:34:12 +04:00
|
|
|
Block_validator.validate
|
2018-01-22 20:47:18 +04:00
|
|
|
~notify_new_block:pv.parameters.notify_new_block
|
|
|
|
pv.parameters.block_validator pv.parameters.net_db
|
2017-11-11 06:34:12 +04:00
|
|
|
hash header operations >>=? fun _block ->
|
2018-01-22 20:47:18 +04:00
|
|
|
debug w
|
|
|
|
"end of validation for new head %a from peer %a"
|
2017-11-11 06:34:12 +04:00
|
|
|
Block_hash.pp_short hash
|
2018-01-22 20:47:18 +04:00
|
|
|
P2p.Peer_id.pp_short pv.peer_id ;
|
2017-11-11 06:34:12 +04:00
|
|
|
set_bootstrapped pv ;
|
|
|
|
return ()
|
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
let may_validate_new_head w hash header =
|
|
|
|
let pv = Worker.state w in
|
|
|
|
let net_state = Distributed_db.net_state pv.parameters.net_db in
|
2017-11-11 06:34:12 +04:00
|
|
|
State.Block.known net_state hash >>= function
|
|
|
|
| true -> begin
|
|
|
|
State.Block.known_valid net_state hash >>= function
|
|
|
|
| true ->
|
2018-01-22 20:47:18 +04:00
|
|
|
debug w
|
2017-11-11 06:34:12 +04:00
|
|
|
"ignoring previously validated block %a from peer %a"
|
|
|
|
Block_hash.pp_short hash
|
2018-01-22 20:47:18 +04:00
|
|
|
P2p.Peer_id.pp_short pv.peer_id ;
|
2017-11-11 06:34:12 +04:00
|
|
|
set_bootstrapped pv ;
|
2017-11-14 02:27:19 +04:00
|
|
|
pv.last_validated_head <- header ;
|
2017-11-11 06:34:12 +04:00
|
|
|
return ()
|
|
|
|
| false ->
|
2018-01-22 20:47:18 +04:00
|
|
|
debug w
|
2017-11-11 06:34:12 +04:00
|
|
|
"ignoring known invalid block %a from peer %a"
|
|
|
|
Block_hash.pp_short hash
|
2018-01-22 20:47:18 +04:00
|
|
|
P2p.Peer_id.pp_short pv.peer_id ;
|
2017-11-11 06:34:12 +04:00
|
|
|
fail Known_invalid
|
|
|
|
end
|
|
|
|
| false ->
|
2018-01-22 20:47:18 +04:00
|
|
|
validate_new_head w hash header
|
2017-11-11 06:34:12 +04:00
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
let may_validate_new_branch w distant_hash locator =
|
|
|
|
let pv = Worker.state w in
|
2017-11-11 06:34:12 +04:00
|
|
|
let distant_header, _ = (locator : Block_locator.t :> Block_header.t * _) in
|
2018-01-22 20:47:18 +04:00
|
|
|
let net_state = Distributed_db.net_state pv.parameters.net_db in
|
2017-11-11 06:34:12 +04:00
|
|
|
Chain.head net_state >>= fun local_header ->
|
|
|
|
if Fitness.compare
|
|
|
|
distant_header.Block_header.shell.fitness
|
|
|
|
(State.Block.fitness local_header) < 0 then begin
|
|
|
|
set_bootstrapped pv ;
|
2018-01-22 20:47:18 +04:00
|
|
|
debug w
|
2017-11-11 06:34:12 +04:00
|
|
|
"ignoring branch %a with low fitness from peer: %a."
|
|
|
|
Block_hash.pp_short distant_hash
|
2018-01-22 20:47:18 +04:00
|
|
|
P2p.Peer_id.pp_short pv.peer_id ;
|
2017-11-11 06:34:12 +04:00
|
|
|
(* Don't bother with downloading a branch with a low fitness. *)
|
|
|
|
return ()
|
|
|
|
end else begin
|
2018-01-22 20:47:18 +04:00
|
|
|
let net_state = Distributed_db.net_state pv.parameters.net_db in
|
2017-12-17 22:51:06 +04:00
|
|
|
Block_locator_iterator.known_ancestor net_state locator >>= function
|
2017-11-11 06:34:12 +04:00
|
|
|
| None ->
|
2018-01-22 20:47:18 +04:00
|
|
|
debug w
|
2017-11-11 06:34:12 +04:00
|
|
|
"ignoring branch %a without common ancestor from peer: %a."
|
|
|
|
Block_hash.pp_short distant_hash
|
2018-01-22 20:47:18 +04:00
|
|
|
P2p.Peer_id.pp_short pv.peer_id ;
|
2017-11-11 06:34:12 +04:00
|
|
|
fail Unknown_ancestor
|
|
|
|
| Some (ancestor, unknown_prefix) ->
|
2018-01-22 20:47:18 +04:00
|
|
|
bootstrap_new_branch w ancestor distant_header unknown_prefix
|
2017-11-11 06:34:12 +04:00
|
|
|
end
|
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
let on_no_request w =
|
|
|
|
let pv = Worker.state w in
|
|
|
|
debug w "no new head from peer %a for %g seconds."
|
|
|
|
P2p.Peer_id.pp_short pv.peer_id
|
|
|
|
pv.parameters.limits.new_head_request_timeout ;
|
|
|
|
Distributed_db.Request.current_head pv.parameters.net_db ~peer:pv.peer_id () ;
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let on_request (type a) w (req : a Request.t) : a tzresult Lwt.t =
|
|
|
|
let pv = Worker.state w in
|
|
|
|
match req with
|
|
|
|
| Request.New_head (hash, header) ->
|
|
|
|
debug w
|
|
|
|
"processing new head %a from peer %a."
|
|
|
|
Block_hash.pp_short hash
|
|
|
|
P2p.Peer_id.pp_short pv.peer_id ;
|
|
|
|
may_validate_new_head w hash header
|
|
|
|
| Request.New_branch (hash, locator) ->
|
|
|
|
(* TODO penalize empty locator... ?? *)
|
|
|
|
debug w "processing new branch %a from peer %a."
|
|
|
|
Block_hash.pp_short hash
|
|
|
|
P2p.Peer_id.pp_short pv.peer_id ;
|
|
|
|
may_validate_new_branch w hash locator
|
|
|
|
|
|
|
|
let on_completion w r _ st =
|
|
|
|
Worker.record_event w (Event.Request (Request.view r, st, None )) ;
|
|
|
|
Lwt.return ()
|
|
|
|
|
|
|
|
let on_error w r st errs =
|
|
|
|
let pv = Worker.state w in
|
|
|
|
match errs with
|
|
|
|
((( Unknown_ancestor
|
|
|
|
| Bootstrap_pipeline.Invalid_locator _
|
|
|
|
| Block_validator_errors.Invalid_block _ ) :: _) as errors ) ->
|
2017-11-11 06:34:12 +04:00
|
|
|
(* TODO ban the peer_id... *)
|
2018-01-22 20:47:18 +04:00
|
|
|
debug w
|
|
|
|
"Terminating the validation worker for peer %a (kickban)."
|
|
|
|
P2p.Peer_id.pp_short pv.peer_id ;
|
|
|
|
debug w "%a" Error_monad.pp_print_error errors ;
|
|
|
|
Worker.trigger_shutdown w ;
|
|
|
|
Worker.record_event w (Event.Request (r, st, Some errs)) ;
|
|
|
|
Lwt.return (Error errs)
|
|
|
|
| [Block_validator_errors.Unavailable_protocol { protocol } ] -> begin
|
2017-11-11 06:34:12 +04:00
|
|
|
Block_validator.fetch_and_compile_protocol
|
2018-01-22 20:47:18 +04:00
|
|
|
pv.parameters.block_validator
|
2017-11-13 17:25:02 +04:00
|
|
|
~peer:pv.peer_id
|
2018-01-22 20:47:18 +04:00
|
|
|
~timeout:pv.parameters.limits.protocol_timeout
|
2017-11-13 17:25:02 +04:00
|
|
|
protocol >>= function
|
2018-01-22 20:47:18 +04:00
|
|
|
| Ok _ -> return ()
|
2017-11-11 06:34:12 +04:00
|
|
|
| Error _ ->
|
|
|
|
(* TODO penality... *)
|
2018-01-22 20:47:18 +04:00
|
|
|
debug w
|
|
|
|
"Terminating the validation worker for peer %a \
|
|
|
|
(missing protocol %a)."
|
2017-11-11 06:34:12 +04:00
|
|
|
P2p.Peer_id.pp_short pv.peer_id
|
2018-01-22 20:47:18 +04:00
|
|
|
Protocol_hash.pp_short protocol ;
|
|
|
|
Worker.record_event w (Event.Request (r, st, Some errs)) ;
|
|
|
|
Lwt.return (Error errs)
|
2017-11-11 06:34:12 +04:00
|
|
|
end
|
2018-01-22 20:47:18 +04:00
|
|
|
| _ ->
|
|
|
|
Worker.record_event w (Event.Request (r, st, Some errs)) ;
|
|
|
|
Lwt.return (Error errs)
|
2017-11-11 06:34:12 +04:00
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
let on_close w =
|
|
|
|
let pv = Worker.state w in
|
|
|
|
pv.parameters.notify_termination () ;
|
|
|
|
Distributed_db.disconnect pv.parameters.net_db pv.peer_id >>= fun () ->
|
|
|
|
Lwt.return ()
|
|
|
|
|
|
|
|
let on_launch _ name parameters =
|
|
|
|
let net_state = Distributed_db.net_state parameters.net_db in
|
2017-11-14 02:27:19 +04:00
|
|
|
State.Block.read_exn net_state
|
|
|
|
(State.Net.genesis net_state).block >>= fun genesis ->
|
2018-01-22 20:47:18 +04:00
|
|
|
let rec pv = {
|
|
|
|
peer_id = snd name ;
|
|
|
|
parameters = { parameters with notify_new_block } ;
|
|
|
|
bootstrapped = false ;
|
|
|
|
last_validated_head = State.Block.header genesis ;
|
|
|
|
last_advertised_head = State.Block.header genesis ;
|
|
|
|
}
|
|
|
|
and notify_new_block block =
|
2017-11-14 02:27:19 +04:00
|
|
|
pv.last_validated_head <- State.Block.header block ;
|
2018-01-22 20:47:18 +04:00
|
|
|
parameters.notify_new_block block in
|
|
|
|
Lwt.return pv
|
|
|
|
|
|
|
|
let table =
|
|
|
|
let merge w (Worker.Any_request neu) old =
|
|
|
|
let pv = Worker.state w in
|
|
|
|
match neu with
|
|
|
|
| Request.New_branch (_, locator) ->
|
|
|
|
let header, _ = (locator : Block_locator.t :> _ * _) in
|
|
|
|
pv.last_advertised_head <- header ;
|
|
|
|
Some (Worker.Any_request neu)
|
|
|
|
| Request.New_head (_, header) ->
|
|
|
|
pv.last_advertised_head <- header ;
|
|
|
|
(* TODO penalize decreasing fitness *)
|
|
|
|
match old with
|
|
|
|
| Some (Worker.Any_request (Request.New_branch _) as old) ->
|
|
|
|
Some old (* ignore *)
|
|
|
|
| Some (Worker.Any_request (Request.New_head _)) ->
|
|
|
|
Some (Any_request neu)
|
|
|
|
| None ->
|
|
|
|
Some (Any_request neu) in
|
|
|
|
Worker.create_table (Dropbox { merge })
|
|
|
|
|
|
|
|
let create
|
|
|
|
?(notify_new_block = fun _ -> ())
|
|
|
|
?(notify_bootstrapped = fun () -> ())
|
|
|
|
?(notify_termination = fun _ -> ())
|
|
|
|
limits block_validator net_db peer_id =
|
|
|
|
let name = (State.Net.id (Distributed_db.net_state net_db), peer_id) in
|
|
|
|
let parameters = {
|
|
|
|
net_db ;
|
|
|
|
notify_termination ;
|
2017-11-11 06:34:12 +04:00
|
|
|
block_validator ;
|
|
|
|
notify_new_block ;
|
|
|
|
notify_bootstrapped ;
|
2018-01-22 20:47:18 +04:00
|
|
|
limits ;
|
2017-11-11 06:34:12 +04:00
|
|
|
} in
|
2018-01-22 20:47:18 +04:00
|
|
|
let module Handlers = struct
|
|
|
|
type self = t
|
|
|
|
let on_launch = on_launch
|
|
|
|
let on_request = on_request
|
|
|
|
let on_close = on_close
|
|
|
|
let on_error = on_error
|
|
|
|
let on_completion = on_completion
|
|
|
|
let on_no_request _ = return ()
|
|
|
|
end in
|
|
|
|
Worker.launch table ~timeout: limits.new_head_request_timeout limits.worker_limits
|
|
|
|
name parameters
|
|
|
|
(module Handlers)
|
2017-11-11 06:34:12 +04:00
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
let notify_branch w locator =
|
2017-11-14 02:27:19 +04:00
|
|
|
let header, _ = (locator : Block_locator.t :> _ * _) in
|
|
|
|
let hash = Block_header.hash header in
|
2018-01-22 20:47:18 +04:00
|
|
|
Worker.drop_request w (New_branch (hash, locator))
|
2017-11-11 06:34:12 +04:00
|
|
|
|
2018-01-22 20:47:18 +04:00
|
|
|
let notify_head w header =
|
2017-11-11 06:34:12 +04:00
|
|
|
let hash = Block_header.hash header in
|
2018-01-22 20:47:18 +04:00
|
|
|
Worker.drop_request w (New_head (hash, header))
|
|
|
|
|
|
|
|
let shutdown w =
|
|
|
|
Worker.shutdown w
|
|
|
|
|
|
|
|
let peer_id w =
|
|
|
|
let pv = Worker.state w in
|
|
|
|
pv.peer_id
|
|
|
|
|
|
|
|
let bootstrapped w =
|
|
|
|
let pv = Worker.state w in
|
|
|
|
pv.bootstrapped
|
|
|
|
|
|
|
|
let current_head w =
|
|
|
|
let pv = Worker.state w in
|
|
|
|
pv.last_validated_head
|
|
|
|
|
|
|
|
let status = Worker.status
|
|
|
|
|
|
|
|
let running_workers () = Worker.list table
|
|
|
|
|
|
|
|
let current_request t = Worker.current_request t
|
|
|
|
|
|
|
|
let last_events = Worker.last_events
|