Commit 8abbac0a authored by Julien's avatar Julien

P2p: `Nack` with a list of known peers

parent 072e168b
......@@ -30,5 +30,12 @@ let pp = Format.pp_print_int
let encoding = Data_encoding.uint16
let zero = 0
let one = 1
let supported = [ zero ]
let supported = [ zero ; one ]
type feature = Nack_with_list
let feature_available feature p2p_version =
match feature with
| Nack_with_list -> p2p_version > 0
......@@ -35,3 +35,19 @@ val encoding: t Data_encoding.t
val supported: t list
val zero: t
val one: t
(** Features *)
(** Any new network-protocol feature should be declared here, and the
[feature_available] function should be updated accordingly. This
avoid to clutter the code base with hardcoded version
comparisons.*)
type feature =
| Nack_with_list
(** [feature_available feature version] returns true if the
[feature] is known to be available for the given p2p protocol
[version]. *)
val feature_available: feature -> t -> bool
......@@ -914,6 +914,15 @@ and raw_authenticate pool ?point_info canceler fd point =
if incoming then
log pool
(Request_rejected (point, Some (info.id_point, info.peer_id))) ;
begin
match err with
| [ P2p_errors.Rejected_by_nack { alternative_points = Some points } ] ->
lwt_debug "Connection to %a rejected. Peer list received :%a"
P2p_point.Id.pp point
P2p_point.Id.pp_list points >>= fun () ->
register_new_points_raw pool info.peer_id points
| _ -> Lwt.return_unit
end >>= fun () ->
lwt_debug "authenticate: %a -> rejected %a"
P2p_point.Id.pp point
P2p_peer.Id.pp info.peer_id >>= fun () ->
......@@ -954,7 +963,8 @@ and raw_authenticate pool ?point_info canceler fd point =
(acceptable_point, rejection_argument)
acceptable_peer_id
end >>= fun () ->
P2p_socket.kick auth_fd >>= fun () ->
list_known_points_raw ~ignore_private:true pool >>= fun point_list ->
P2p_socket.kick auth_fd point_list >>= fun () ->
if not incoming then begin
Option.iter ~f:P2p_point_state.set_disconnected point_info ;
(* FIXME P2p_peer_state.set_disconnected ~requested:true peer_info ; *)
......@@ -981,7 +991,7 @@ and create_connection pool p2p_conn id_point point_info peer_info negotiated_ver
advertise =
(fun points -> register_new_points pool conn points ) ;
bootstrap =
(fun () -> list_known_points ~ignore_private:true pool conn) ;
(fun () -> list_known_points ~ignore_private:true pool conn.conn conn.peer_info) ;
swap_request =
(fun point peer_id -> swap_request pool conn point peer_id ) ;
swap_ack =
......@@ -1077,29 +1087,38 @@ and disconnect ?(wait = false) conn =
and register_new_points ?trusted pool conn =
let source_peer_id = P2p_peer_state.Info.peer_id conn.peer_info in
fun points ->
List.iter (register_new_point ?trusted pool source_peer_id) points ;
Lwt.return_unit
register_new_points_raw ?trusted pool source_peer_id points
and register_new_points_raw ?trusted pool source_peer_id points : unit Lwt.t=
debug "Getting points from %a: %a"
P2p_peer.Id.pp source_peer_id
P2p_point.Id.pp_list points ;
List.iter (register_new_point ?trusted pool source_peer_id) points ;
Lwt.return_unit
and register_new_point ?trusted pool source_peer_id point =
if not (P2p_point.Table.mem pool.my_id_points point) then
ignore (register_point ?trusted pool source_peer_id point)
and list_known_points ?(ignore_private = false) pool conn =
if Connection.private_node conn then
and list_known_points ?ignore_private ?size pool p2p_conn peer_info =
if P2p_socket.private_node p2p_conn then
private_node_warn "Private peer (%a) asked other peers addresses"
P2p_peer.Id.pp (P2p_peer_state.Info.peer_id conn.peer_info) >>= fun () ->
P2p_peer.Id.pp (P2p_peer_state.Info.peer_id peer_info) >>= fun () ->
Lwt.return_nil
else
let knowns =
P2p_point.Table.fold
(fun _ point_info acc ->
if ignore_private &&
not (P2p_point_state.Info.known_public point_info) then acc
else point_info :: acc)
pool.known_points [] in
let best_knowns =
List.take_n ~compare:compare_known_point_info 50 knowns in
Lwt.return (List.map P2p_point_state.Info.point best_knowns)
list_known_points_raw ?ignore_private ?size pool
and list_known_points_raw ?(ignore_private = false) ?(size=50) pool =
let knowns =
P2p_point.Table.fold
(fun _ point_info acc ->
if ignore_private &&
not (P2p_point_state.Info.known_public point_info) then acc
else point_info :: acc)
pool.known_points [] in
let best_knowns =
List.take_n ~compare:compare_known_point_info size knowns in
Lwt.return (List.map P2p_point_state.Info.point best_knowns)
and active_connections pool = P2p_peer.Table.length pool.connected_peer_ids
......@@ -1251,6 +1270,9 @@ let create
latest_succesfull_swap = Time.epoch ;
} in
List.iter (Points.set_trusted pool) config.trusted_points ;
debug "create pool: known points %a"
(fun ppf known_points -> P2p_point.Table.iter (fun id _ -> P2p_point.Id.pp ppf id; Format.pp_print_string ppf " ") known_points)
pool.known_points;
P2p_peer_state.Info.File.load
config.peers_file
peer_meta_config.peer_meta_encoding >>= function
......
......@@ -223,30 +223,40 @@ end
module Ack = struct
type t = Ack | Nack
type t = Ack
| Nack_v_0
| Nack of { potential_peers_to_connect : P2p_point.Id.t list }
let encoding =
let open Data_encoding in
let ack_encoding = obj1 (req "ack" empty) in
let nack_encoding = obj1 (req "nack" empty) in
let nack_v_0_encoding = obj1 (req "nack_v_0" empty) in
let nack_encoding =
obj1 (req "nack" (Data_encoding.list P2p_point.Id.encoding))
in
let ack_case tag =
case tag ack_encoding
~title:"Ack"
(function
| Ack -> Some ()
| _ -> None)
(function Ack -> Some () | _ -> None)
(fun () -> Ack) in
let nack_case tag =
case tag nack_encoding
~title:"Nack"
(function
| Nack -> Some ()
| Nack { potential_peers_to_connect } ->
Some potential_peers_to_connect
| _ -> None
)
(fun _ -> Nack) in
(fun lst -> Nack { potential_peers_to_connect = lst }) in
let nack_v_0_case tag =
case tag nack_v_0_encoding
~title:"Nack_v_0"
(function Nack_v_0 -> Some () | _ -> None)
(fun () -> Nack_v_0) in
union [
ack_case (Tag 0) ;
nack_case (Tag 255) ;
nack_v_0_case (Tag 255) ;
nack_case (Tag 1) ;
]
let write ?canceler fd cryptobox_data message =
......@@ -281,8 +291,22 @@ type 'meta authenticated_connection = {
cryptobox_data: Crypto.data ;
}
let kick { fd ; cryptobox_data ; _ } =
Ack.write fd cryptobox_data Nack >>= fun _ ->
let kick { fd ; cryptobox_data ; info } potential_peers_to_connect =
let nack =
if P2p_version.feature_available
P2p_version.Nack_with_list
info.announced_version.p2p_version
then begin
debug "Nack point %a with point list %a"
P2p_connection.Id.pp info.id_point
P2p_point.Id.pp_list potential_peers_to_connect ;
Ack.Nack { potential_peers_to_connect }
end else begin
debug "Nack point %a (no point list due to p2p version)"
P2p_connection.Id.pp info.id_point ;
Ack.Nack_v_0
end in
Ack.write fd cryptobox_data nack >>= fun _ ->
P2p_io_scheduler.close fd >>= fun _ ->
Lwt.return_unit
......@@ -589,8 +613,10 @@ let accept
Lwt.return_unit
end ;
return conn
| Nack ->
fail P2p_errors.Rejected_socket_connection
| Nack_v_0 ->
fail (P2p_errors.Rejected_by_nack { alternative_points = None })
| Nack { potential_peers_to_connect } ->
fail (P2p_errors.Rejected_by_nack { alternative_points = Some potential_peers_to_connect })
let catch_closed_pipe f =
Lwt.catch f begin function
......
......@@ -76,7 +76,7 @@ val authenticate:
[P2P_io_scheduler.connection] into an [authenticated_connection] (auth
correct, acceptation undecided). *)
val kick: 'meta authenticated_connection -> unit Lwt.t
val kick: 'meta authenticated_connection -> P2p_point.Id.t list -> unit Lwt.t
(** (Low-level) (Cancelable) [kick afd] notifies the remote peer that
we refuse this connection and then closes [afd]. Used in
[P2p_connection_pool] to reject an [authenticated_connection] which we do
......
......@@ -146,6 +146,10 @@ let signal_name =
Sys.sigxfsz, "XFSZ" ] in
fun n -> List.assoc n names
(** Wait for all process to terminate.
If a node terminates with an error, all remaining process
are canceled and an exception is raised *)
let wait_all processes =
let rec loop processes =
match processes with
......
This diff is collapsed.
......@@ -192,7 +192,7 @@ module Kick = struct
let encoding = Data_encoding.bytes
let is_rejected = function
| Error [P2p_errors.Rejected_socket_connection] -> true
| Error [P2p_errors.Rejected_by_nack _] -> true
| Ok _ -> false
| Error err ->
log_notice "Error: %a" pp_print_error err ;
......@@ -203,7 +203,7 @@ module Kick = struct
_assert (info.incoming) __LOC__ "" >>=? fun () ->
_assert (P2p_peer.Id.compare info.peer_id id2.peer_id = 0)
__LOC__ "" >>=? fun () ->
P2p_socket.kick auth_fd >>= fun () ->
P2p_socket.kick auth_fd [] >>= fun () ->
return_unit
let client _ch sched addr port =
......@@ -228,7 +228,7 @@ module Kicked = struct
let client _ch sched addr port =
connect sched addr port id2 >>=? fun auth_fd ->
P2p_socket.kick auth_fd >>= fun () ->
P2p_socket.kick auth_fd [] >>= fun () ->
return_unit
let run _dir = run_nodes client server
......
......@@ -45,6 +45,7 @@ type error += Decipher_error
type error += Invalid_message_size
type error += Encoding_error
type error += Rejected_socket_connection
type error += Rejected_by_nack of { alternative_points : P2p_point.Id.t list option }
type error += Rejected_no_common_protocol of { announced : Network_version.t }
type error += Decoding_error
type error += Myself of P2p_connection.Id.t
......@@ -93,6 +94,19 @@ let () =
Data_encoding.empty
(function Rejected_socket_connection -> Some () | _ -> None)
(fun () -> Rejected_socket_connection) ;
(* Rejected socket connection, peer gave us with alternative points *)
register_error_kind
`Permanent
~id:"node.p2p_socket.rejected_by_nack"
~title:"Rejected socket connection by Nack"
~description:"Rejected peer connection: \
rejected socket connection by Nack with a list of alternative peers."
~pp:(fun ppf _lst ->
Format.fprintf ppf
"Rejected peer connection: rejected by Nack with a list of alternative peers.")
Data_encoding.(obj1 (opt "alternative_points" (list P2p_point.Id.encoding)))
(function Rejected_by_nack { alternative_points } -> Some alternative_points | _ -> None)
(fun alternative_points -> Rejected_by_nack { alternative_points }) ;
(* Rejected socket connection, no common network protocol *)
register_error_kind
`Permanent
......@@ -127,7 +141,7 @@ let () =
~pp:(fun ppf id -> Format.fprintf ppf
"Remote peer %a cannot be authenticated: peer is actually yourself."
P2p_connection.Id.pp id)
Data_encoding.(obj1 (req "connection id" P2p_connection.Id.encoding))
Data_encoding.(obj1 (req "connection_id" P2p_connection.Id.encoding))
(function Myself id -> Some id | _ -> None)
(fun id -> Myself id) ;
(* Not enough proof of work *)
......@@ -140,7 +154,7 @@ let () =
Format.fprintf ppf
"Remote peer %a cannot be authenticated: not enough proof of work."
P2p_peer.Id.pp id)
Data_encoding.(obj1 (req "peer id" P2p_peer.Id.encoding))
Data_encoding.(obj1 (req "peer_id" P2p_peer.Id.encoding))
(function Not_enough_proof_of_work id -> Some id | _ -> None)
(fun id -> Not_enough_proof_of_work id) ;
(* Invalid authentication *)
......@@ -219,7 +233,7 @@ let () =
~description:"Connection to peer was rejected."
~pp:(fun ppf id ->
Format.fprintf ppf "Connection to peer %a was rejected." P2p_peer.Id.pp id)
Data_encoding.(obj1 (req "peer id" P2p_peer.Id.encoding))
Data_encoding.(obj1 (req "peer_id" P2p_peer.Id.encoding))
(function Rejected id -> Some id | _ -> None)
(fun id -> Rejected id) ;
(* Too many connections *)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment