2017-01-14 16:14:02 +04:00
|
|
|
(**************************************************************************)
|
|
|
|
(* *)
|
|
|
|
(* Copyright (c) 2014 - 2016. *)
|
|
|
|
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
|
|
|
(* *)
|
|
|
|
(* All rights reserved. No warranty, explicit or implicit, provided. *)
|
|
|
|
(* *)
|
|
|
|
(**************************************************************************)
|
|
|
|
|
|
|
|
(* TODO Use Kaputt on the client side and remove `assert` from the
|
|
|
|
server. *)
|
|
|
|
|
|
|
|
open Error_monad
|
|
|
|
open P2p_types
|
2017-04-10 00:52:24 +04:00
|
|
|
include Logging.Make (struct let name = "test.p2p.connection" end)
|
|
|
|
|
|
|
|
let default_addr = Ipaddr.V6.localhost
|
2017-01-14 16:14:02 +04:00
|
|
|
|
2017-01-24 01:01:18 +04:00
|
|
|
let proof_of_work_target = Crypto_box.make_target 16.
|
2017-01-14 16:14:02 +04:00
|
|
|
let id1 = Identity.generate proof_of_work_target
|
|
|
|
let id2 = Identity.generate proof_of_work_target
|
|
|
|
|
|
|
|
let id0 =
|
|
|
|
(* Luckilly, this will be an insuficient proof of work! *)
|
2017-01-24 01:01:18 +04:00
|
|
|
Identity.generate (Crypto_box.make_target 0.)
|
2017-01-14 16:14:02 +04:00
|
|
|
|
|
|
|
let versions = Version.[{ name = "TEST" ; minor = 0 ; major = 0 }]
|
|
|
|
|
|
|
|
let rec listen ?port addr =
|
|
|
|
let tentative_port =
|
|
|
|
match port with
|
|
|
|
| None -> 1024 + Random.int 8192
|
|
|
|
| Some port -> port in
|
|
|
|
let uaddr = Ipaddr_unix.V6.to_inet_addr addr in
|
|
|
|
let main_socket = Lwt_unix.(socket PF_INET6 SOCK_STREAM 0) in
|
|
|
|
Lwt_unix.(setsockopt main_socket SO_REUSEADDR true) ;
|
|
|
|
Lwt.catch begin fun () ->
|
|
|
|
Lwt_unix.Versioned.bind_2 main_socket
|
|
|
|
(ADDR_INET (uaddr, tentative_port)) >>= fun () ->
|
|
|
|
Lwt_unix.listen main_socket 1 ;
|
|
|
|
Lwt.return (main_socket, tentative_port)
|
|
|
|
end begin function
|
|
|
|
| Unix.Unix_error
|
|
|
|
((Unix.EADDRINUSE | Unix.EADDRNOTAVAIL), _, _) when port = None ->
|
|
|
|
listen addr
|
|
|
|
| exn -> Lwt.fail exn
|
|
|
|
end
|
|
|
|
|
2017-04-10 00:52:24 +04:00
|
|
|
let sync ch =
|
|
|
|
Process.Channel.push ch () >>=? fun () ->
|
|
|
|
Process.Channel.pop ch >>=? fun () ->
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let rec sync_nodes nodes =
|
|
|
|
iter_p
|
|
|
|
(fun { Process.channel } -> Process.Channel.pop channel)
|
|
|
|
nodes >>=? fun () ->
|
|
|
|
iter_p
|
|
|
|
(fun { Process.channel } -> Process.Channel.push channel ())
|
|
|
|
nodes >>=? fun () ->
|
|
|
|
sync_nodes nodes
|
|
|
|
|
|
|
|
let sync_nodes nodes =
|
|
|
|
sync_nodes nodes >>= function
|
|
|
|
| Ok () | Error (Exn End_of_file :: _) ->
|
|
|
|
return ()
|
2017-04-11 02:57:34 +04:00
|
|
|
| Error _ as err ->
|
2017-04-10 00:52:24 +04:00
|
|
|
Lwt.return err
|
|
|
|
|
|
|
|
let run_nodes client server =
|
|
|
|
listen default_addr >>= fun (main_socket, port) ->
|
|
|
|
Process.detach ~prefix:"server: " begin fun channel ->
|
|
|
|
let sched = P2p_io_scheduler.create ~read_buffer_size:(1 lsl 12) () in
|
|
|
|
server channel sched main_socket >>=? fun () ->
|
|
|
|
P2p_io_scheduler.shutdown sched >>= fun () ->
|
|
|
|
return ()
|
|
|
|
end >>= fun server_node ->
|
|
|
|
Process.detach ~prefix:"client: " begin fun channel ->
|
|
|
|
Lwt_utils.safe_close main_socket >>= fun () ->
|
|
|
|
let sched = P2p_io_scheduler.create ~read_buffer_size:(1 lsl 12) () in
|
|
|
|
client channel sched default_addr port >>=? fun () ->
|
|
|
|
P2p_io_scheduler.shutdown sched >>= fun () ->
|
|
|
|
return ()
|
|
|
|
end >>= fun client_node ->
|
|
|
|
let nodes = [ server_node ; client_node ] in
|
|
|
|
Lwt.ignore_result (sync_nodes nodes) ;
|
|
|
|
Process.wait_all nodes
|
|
|
|
|
2017-01-14 16:14:02 +04:00
|
|
|
let raw_accept sched main_socket =
|
|
|
|
Lwt_unix.accept main_socket >>= fun (fd, sockaddr) ->
|
|
|
|
let fd = P2p_io_scheduler.register sched fd in
|
|
|
|
let point =
|
|
|
|
match sockaddr with
|
|
|
|
| Lwt_unix.ADDR_UNIX _ -> assert false
|
|
|
|
| Lwt_unix.ADDR_INET (addr, port) ->
|
|
|
|
Ipaddr_unix.V6.of_inet_addr_exn addr, port in
|
|
|
|
Lwt.return (fd, point)
|
|
|
|
|
|
|
|
let accept sched main_socket =
|
|
|
|
raw_accept sched main_socket >>= fun (fd, point) ->
|
|
|
|
P2p_connection.authenticate
|
|
|
|
~proof_of_work_target
|
|
|
|
~incoming:true fd point id1 versions
|
|
|
|
|
|
|
|
let raw_connect sched addr port =
|
|
|
|
let fd = Lwt_unix.socket PF_INET6 SOCK_STREAM 0 in
|
|
|
|
let uaddr =
|
|
|
|
Lwt_unix.ADDR_INET (Ipaddr_unix.V6.to_inet_addr addr, port) in
|
|
|
|
Lwt_unix.connect fd uaddr >>= fun () ->
|
|
|
|
let fd = P2p_io_scheduler.register sched fd in
|
|
|
|
Lwt.return fd
|
|
|
|
|
|
|
|
let connect sched addr port id =
|
|
|
|
raw_connect sched addr port >>= fun fd ->
|
|
|
|
P2p_connection.authenticate
|
|
|
|
~proof_of_work_target
|
|
|
|
~incoming:false fd (addr, port) id versions >>=? fun (info, auth_fd) ->
|
2017-04-10 00:52:24 +04:00
|
|
|
_assert (not info.incoming) __LOC__ "" >>=? fun () ->
|
|
|
|
_assert (Peer_id.compare info.peer_id id1.peer_id = 0)
|
|
|
|
__LOC__ "" >>=? fun () ->
|
2017-01-14 16:14:02 +04:00
|
|
|
return auth_fd
|
|
|
|
|
2017-04-10 00:52:24 +04:00
|
|
|
let is_connection_closed = function
|
|
|
|
| Error [P2p_io_scheduler.Connection_closed] -> true
|
2017-01-20 18:25:12 +04:00
|
|
|
| Ok _ -> false
|
|
|
|
| Error err ->
|
|
|
|
log_notice "Error: %a" pp_print_error err ;
|
|
|
|
false
|
2017-01-14 16:14:02 +04:00
|
|
|
|
2017-04-10 00:52:24 +04:00
|
|
|
let is_decoding_error = function
|
|
|
|
| Error [P2p_connection.Decoding_error] -> true
|
2017-01-20 18:25:12 +04:00
|
|
|
| Ok _ -> false
|
|
|
|
| Error err ->
|
|
|
|
log_notice "Error: %a" pp_print_error err ;
|
|
|
|
false
|
2017-01-14 16:14:02 +04:00
|
|
|
|
2017-04-10 00:52:24 +04:00
|
|
|
module Low_level = struct
|
2017-01-14 16:14:02 +04:00
|
|
|
|
2017-04-10 00:52:24 +04:00
|
|
|
let simple_msg = MBytes.create (1 lsl 4)
|
2017-01-14 16:14:02 +04:00
|
|
|
|
2017-04-10 00:52:24 +04:00
|
|
|
let client _ch sched addr port =
|
|
|
|
let msg = MBytes.create (MBytes.length simple_msg) in
|
|
|
|
raw_connect sched addr port >>= fun fd ->
|
|
|
|
P2p_io_scheduler.read_full fd msg >>=? fun () ->
|
|
|
|
_assert (MBytes.compare simple_msg msg = 0) __LOC__ "" >>=? fun () ->
|
|
|
|
P2p_io_scheduler.close fd >>=? fun () ->
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let server _ch sched socket =
|
2017-04-11 02:57:34 +04:00
|
|
|
raw_accept sched socket >>= fun (fd, _point) ->
|
2017-04-10 00:52:24 +04:00
|
|
|
P2p_io_scheduler.write fd simple_msg >>=? fun () ->
|
|
|
|
P2p_io_scheduler.close fd >>=? fun _ ->
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let run _dir = run_nodes client server
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
module Kick = struct
|
|
|
|
|
|
|
|
let encoding = Data_encoding.bytes
|
|
|
|
|
|
|
|
let is_rejected = function
|
|
|
|
| Error [P2p_connection.Rejected] -> true
|
|
|
|
| Ok _ -> false
|
|
|
|
| Error err ->
|
|
|
|
log_notice "Error: %a" pp_print_error err ;
|
|
|
|
false
|
|
|
|
|
|
|
|
let server _ch sched socket =
|
|
|
|
accept sched socket >>=? fun (info, auth_fd) ->
|
|
|
|
_assert (info.incoming) __LOC__ "" >>=? fun () ->
|
|
|
|
_assert (Peer_id.compare info.peer_id id2.peer_id = 0)
|
|
|
|
__LOC__ "" >>=? fun () ->
|
|
|
|
P2p_connection.kick auth_fd >>= fun () ->
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let client _ch sched addr port =
|
|
|
|
connect sched addr port id2 >>=? fun auth_fd ->
|
|
|
|
P2p_connection.accept auth_fd encoding >>= fun conn ->
|
|
|
|
_assert (is_rejected conn) __LOC__ "" >>=? fun () ->
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let run _dir = run_nodes client server
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
module Kicked = struct
|
|
|
|
|
|
|
|
let encoding = Data_encoding.bytes
|
|
|
|
|
|
|
|
let server _ch sched socket =
|
2017-04-11 02:57:34 +04:00
|
|
|
accept sched socket >>=? fun (_info, auth_fd) ->
|
2017-04-10 00:52:24 +04:00
|
|
|
P2p_connection.accept auth_fd encoding >>= fun conn ->
|
|
|
|
_assert (Kick.is_rejected conn) __LOC__ "" >>=? fun () ->
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let client _ch sched addr port =
|
|
|
|
connect sched addr port id2 >>=? fun auth_fd ->
|
|
|
|
P2p_connection.kick auth_fd >>= fun () ->
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let run _dir = run_nodes client server
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
module Simple_message = struct
|
|
|
|
|
|
|
|
let encoding = Data_encoding.bytes
|
|
|
|
|
|
|
|
let simple_msg = MBytes.create (1 lsl 4)
|
|
|
|
let simple_msg2 = MBytes.create (1 lsl 4)
|
|
|
|
|
|
|
|
let server ch sched socket =
|
2017-04-11 02:57:34 +04:00
|
|
|
accept sched socket >>=? fun (_info, auth_fd) ->
|
2017-04-10 00:52:24 +04:00
|
|
|
P2p_connection.accept auth_fd encoding >>=? fun conn ->
|
|
|
|
P2p_connection.write_sync conn simple_msg >>=? fun () ->
|
|
|
|
P2p_connection.read conn >>=? fun (_msg_size, msg) ->
|
|
|
|
_assert (MBytes.compare simple_msg2 msg = 0) __LOC__ "" >>=? fun () ->
|
|
|
|
sync ch >>=? fun () ->
|
|
|
|
P2p_connection.close conn >>= fun _stat ->
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let client ch sched addr port =
|
|
|
|
connect sched addr port id2 >>=? fun auth_fd ->
|
|
|
|
P2p_connection.accept auth_fd encoding >>=? fun conn ->
|
|
|
|
P2p_connection.write_sync conn simple_msg2 >>=? fun () ->
|
|
|
|
P2p_connection.read conn >>=? fun (_msg_size, msg) ->
|
|
|
|
_assert (MBytes.compare simple_msg msg = 0) __LOC__ "" >>=? fun () ->
|
|
|
|
sync ch >>=? fun () ->
|
|
|
|
P2p_connection.close conn >>= fun _stat ->
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let run _dir = run_nodes client server
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2017-04-18 20:32:31 +04:00
|
|
|
module Chunked_message = struct
|
|
|
|
|
|
|
|
let encoding = Data_encoding.bytes
|
|
|
|
|
|
|
|
let simple_msg = MBytes.create (1 lsl 8)
|
|
|
|
let simple_msg2 = MBytes.create (1 lsl 8)
|
|
|
|
|
|
|
|
let server ch sched socket =
|
|
|
|
accept sched socket >>=? fun (_info, auth_fd) ->
|
|
|
|
P2p_connection.accept
|
|
|
|
~binary_chunks_size:21 auth_fd encoding >>=? fun conn ->
|
|
|
|
P2p_connection.write_sync conn simple_msg >>=? fun () ->
|
|
|
|
P2p_connection.read conn >>=? fun (_msg_size, msg) ->
|
|
|
|
_assert (MBytes.compare simple_msg2 msg = 0) __LOC__ "" >>=? fun () ->
|
|
|
|
sync ch >>=? fun () ->
|
|
|
|
P2p_connection.close conn >>= fun _stat ->
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let client ch sched addr port =
|
|
|
|
connect sched addr port id2 >>=? fun auth_fd ->
|
|
|
|
P2p_connection.accept
|
|
|
|
~binary_chunks_size:21 auth_fd encoding >>=? fun conn ->
|
|
|
|
P2p_connection.write_sync conn simple_msg2 >>=? fun () ->
|
|
|
|
P2p_connection.read conn >>=? fun (_msg_size, msg) ->
|
|
|
|
_assert (MBytes.compare simple_msg msg = 0) __LOC__ "" >>=? fun () ->
|
|
|
|
sync ch >>=? fun () ->
|
|
|
|
P2p_connection.close conn >>= fun _stat ->
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let run _dir = run_nodes client server
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2017-04-10 00:52:24 +04:00
|
|
|
module Close_on_read = struct
|
|
|
|
|
|
|
|
let encoding = Data_encoding.bytes
|
|
|
|
|
|
|
|
let simple_msg = MBytes.create (1 lsl 4)
|
|
|
|
|
|
|
|
let server _ch sched socket =
|
2017-04-11 02:57:34 +04:00
|
|
|
accept sched socket >>=? fun (_info, auth_fd) ->
|
2017-04-10 00:52:24 +04:00
|
|
|
P2p_connection.accept auth_fd encoding >>=? fun conn ->
|
|
|
|
P2p_connection.close conn >>= fun _stat ->
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let client _ch sched addr port =
|
|
|
|
connect sched addr port id2 >>=? fun auth_fd ->
|
|
|
|
P2p_connection.accept auth_fd encoding >>=? fun conn ->
|
|
|
|
P2p_connection.read conn >>= fun err ->
|
|
|
|
_assert (is_connection_closed err) __LOC__ "" >>=? fun () ->
|
|
|
|
P2p_connection.close conn >>= fun _stat ->
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let run _dir = run_nodes client server
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
module Close_on_write = struct
|
|
|
|
|
|
|
|
let encoding = Data_encoding.bytes
|
|
|
|
|
|
|
|
let simple_msg = MBytes.create (1 lsl 4)
|
|
|
|
|
|
|
|
let server ch sched socket =
|
2017-04-11 02:57:34 +04:00
|
|
|
accept sched socket >>=? fun (_info, auth_fd) ->
|
2017-04-10 00:52:24 +04:00
|
|
|
P2p_connection.accept auth_fd encoding >>=? fun conn ->
|
|
|
|
P2p_connection.close conn >>= fun _stat ->
|
|
|
|
sync ch >>=? fun ()->
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let client ch sched addr port =
|
|
|
|
connect sched addr port id2 >>=? fun auth_fd ->
|
|
|
|
P2p_connection.accept auth_fd encoding >>=? fun conn ->
|
|
|
|
sync ch >>=? fun ()->
|
|
|
|
P2p_connection.write_sync conn simple_msg >>= fun err ->
|
|
|
|
_assert (is_connection_closed err) __LOC__ "" >>=? fun () ->
|
|
|
|
P2p_connection.close conn >>= fun _stat ->
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let run _dir = run_nodes client server
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2017-04-10 02:44:10 +04:00
|
|
|
module Garbled_data = struct
|
|
|
|
|
2017-04-14 14:57:34 +04:00
|
|
|
let encoding =
|
|
|
|
let open Data_encoding in
|
|
|
|
dynamic_size @@ option @@ string
|
|
|
|
|
|
|
|
(* generate a fixed garbled_msg to avoid 'Data_encoding.Binary.Await
|
|
|
|
_', which blocks 'make test' *)
|
|
|
|
let garbled_msg =
|
|
|
|
let buf = MBytes.create (1 lsl 4) in
|
|
|
|
MBytes.set_int32 buf 0 (Int32.of_int 4);
|
|
|
|
MBytes.set_int32 buf 4 (Int32.of_int (-1));
|
|
|
|
MBytes.set_int32 buf 8 (Int32.of_int (-1));
|
|
|
|
MBytes.set_int32 buf 12 (Int32.of_int (-1));
|
|
|
|
buf
|
2017-04-10 02:44:10 +04:00
|
|
|
|
2017-04-11 02:57:34 +04:00
|
|
|
let server _ch sched socket =
|
|
|
|
accept sched socket >>=? fun (_info, auth_fd) ->
|
2017-04-10 02:44:10 +04:00
|
|
|
P2p_connection.accept auth_fd encoding >>=? fun conn ->
|
|
|
|
P2p_connection.raw_write_sync conn garbled_msg >>=? fun () ->
|
|
|
|
P2p_connection.read conn >>= fun err ->
|
|
|
|
_assert (is_connection_closed err) __LOC__ "" >>=? fun () ->
|
|
|
|
P2p_connection.close conn >>= fun _stat ->
|
|
|
|
return ()
|
|
|
|
|
2017-04-11 02:57:34 +04:00
|
|
|
let client _ch sched addr port =
|
2017-04-10 02:44:10 +04:00
|
|
|
connect sched addr port id2 >>=? fun auth_fd ->
|
|
|
|
P2p_connection.accept auth_fd encoding >>=? fun conn ->
|
|
|
|
P2p_connection.read conn >>= fun err ->
|
|
|
|
_assert (is_decoding_error err) __LOC__ "" >>=? fun () ->
|
|
|
|
P2p_connection.close conn >>= fun _stat ->
|
|
|
|
return ()
|
|
|
|
|
|
|
|
let run _dir = run_nodes client server
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2017-04-10 00:52:24 +04:00
|
|
|
let spec = Arg.[
|
|
|
|
|
|
|
|
"-v", Unit (fun () ->
|
|
|
|
Lwt_log_core.(add_rule "test.p2p.connection" Info) ;
|
|
|
|
Lwt_log_core.(add_rule "p2p.connection" Info)),
|
|
|
|
" Log up to info msgs" ;
|
|
|
|
|
|
|
|
"-vv", Unit (fun () ->
|
|
|
|
Lwt_log_core.(add_rule "test.p2p.connection" Debug) ;
|
|
|
|
Lwt_log_core.(add_rule "p2p.connection" Debug)),
|
|
|
|
" Log up to debug msgs";
|
|
|
|
|
|
|
|
]
|
2017-01-14 16:14:02 +04:00
|
|
|
|
|
|
|
let main () =
|
2017-04-10 00:52:24 +04:00
|
|
|
let open Utils in
|
2017-04-11 02:57:34 +04:00
|
|
|
let anon_fun _num_peers = raise (Arg.Bad "No anonymous argument.") in
|
2017-04-10 00:52:24 +04:00
|
|
|
let usage_msg = "Usage: %s.\nArguments are:" in
|
|
|
|
Arg.parse spec anon_fun usage_msg ;
|
|
|
|
Test.run "p2p-connection." [
|
|
|
|
"low-level", Low_level.run ;
|
|
|
|
"kick", Kick.run ;
|
|
|
|
"kicked", Kicked.run ;
|
|
|
|
"simple-message", Simple_message.run ;
|
2017-04-18 20:32:31 +04:00
|
|
|
"chunked-message", Chunked_message.run ;
|
2017-04-10 00:52:24 +04:00
|
|
|
"close-on-read", Close_on_read.run ;
|
|
|
|
"close-on-write", Close_on_write.run ;
|
2017-04-10 02:44:10 +04:00
|
|
|
"garbled-data", Garbled_data.run ;
|
2017-04-10 00:52:24 +04:00
|
|
|
]
|
2017-01-14 16:14:02 +04:00
|
|
|
|
|
|
|
let () =
|
2017-04-10 00:52:24 +04:00
|
|
|
Sys.catch_break true ;
|
|
|
|
try main ()
|
|
|
|
with _ -> ()
|