From e6c62d4f3a01cab811ff75b273fc01b646a5f778 Mon Sep 17 00:00:00 2001 From: Nathan Holland Date: Wed, 21 Aug 2024 20:13:04 -0400 Subject: [PATCH 1/6] Refactor Mina RPC implementations into unified location --- .../src/cli_entrypoint/mina_cli_entrypoint.ml | 9 +- src/lib/daemon_rpcs/daemon_rpcs.ml | 4 +- src/lib/fake_network/fake_network.ml | 314 ++-- src/lib/fake_network/fake_network.mli | 184 +-- src/lib/gossip_net/any.ml | 31 +- src/lib/gossip_net/fake.ml | 109 +- src/lib/gossip_net/gossip_net.ml | 18 +- src/lib/gossip_net/intf.ml | 81 +- src/lib/gossip_net/libp2p.ml | 68 +- src/lib/mina_lib/mina_lib.ml | 158 +- src/lib/mina_lib/mina_lib.mli | 6 + src/lib/mina_networking/dune | 2 + src/lib/mina_networking/mina_networking.ml | 1356 +---------------- src/lib/mina_networking/mina_networking.mli | 188 +-- src/lib/mina_networking/rpcs.ml | 1355 ++++++++++++++++ src/lib/network_peer/network_peer.ml | 10 +- src/lib/network_peer/rpc_intf.ml | 62 - 17 files changed, 1895 insertions(+), 2060 deletions(-) create mode 100644 src/lib/mina_networking/rpcs.ml delete mode 100644 src/lib/network_peer/rpc_intf.ml diff --git a/src/app/cli/src/cli_entrypoint/mina_cli_entrypoint.ml b/src/app/cli/src/cli_entrypoint/mina_cli_entrypoint.ml index b41ff75a606..9cbd56864b0 100644 --- a/src/app/cli/src/cli_entrypoint/mina_cli_entrypoint.ml +++ b/src/app/cli/src/cli_entrypoint/mina_cli_entrypoint.ml @@ -1290,20 +1290,13 @@ Pass one of -peer, -peer-list-file, -seed, -peer-list-url.|} ; } in let net_config = - { Mina_networking.Config.logger - ; trust_system - ; time_controller - ; consensus_constants = precomputed_values.consensus_constants - ; consensus_local_state - ; genesis_ledger_hash - ; constraint_constants = precomputed_values.constraint_constants + { Mina_networking.Config.genesis_ledger_hash ; log_gossip_heard ; is_seed ; creatable_gossip_net = Mina_networking.Gossip_net.( Any.Creatable ((module Libp2p), Libp2p.create ~pids gossip_net_params)) - ; precomputed_values } in let coinbase_receiver : Consensus.Coinbase_receiver.t = diff --git a/src/lib/daemon_rpcs/daemon_rpcs.ml b/src/lib/daemon_rpcs/daemon_rpcs.ml index 5338b3e142c..fae68afc136 100644 --- a/src/lib/daemon_rpcs/daemon_rpcs.ml +++ b/src/lib/daemon_rpcs/daemon_rpcs.ml @@ -330,9 +330,7 @@ end module Get_node_status = struct type query = Mina_net2.Multiaddr.t list option [@@deriving bin_io_unversioned] - type response = - Mina_networking.Rpcs.Get_node_status.Node_status.Stable.Latest.t Or_error.t - list + type response = Mina_networking.Node_status.Stable.Latest.t Or_error.t list [@@deriving bin_io_unversioned] let rpc : (query, response) Rpc.Rpc.t = diff --git a/src/lib/fake_network/fake_network.ml b/src/lib/fake_network/fake_network.ml index 44dce57651c..05e5dc29348 100644 --- a/src/lib/fake_network/fake_network.ml +++ b/src/lib/fake_network/fake_network.ml @@ -1,6 +1,5 @@ open Async open Core -open Mina_base module Sync_ledger = Mina_ledger.Sync_ledger open Gadt_lib open Signature_lib @@ -24,47 +23,7 @@ type 'n num_peers = 'n Peano.gt_1 type peer_state = { frontier : Transition_frontier.t ; consensus_local_state : Consensus.Data.Local_state.t - ; get_staged_ledger_aux_and_pending_coinbases_at_hash : - Pasta_bindings.Fp.t Envelope.Incoming.t - -> ( Staged_ledger.Scan_state.t - * Pasta_bindings.Fp.t - * Pending_coinbase.t - * Mina_state.Protocol_state.value list ) - option - Deferred.t - ; get_some_initial_peers : unit Envelope.Incoming.t -> Peer.t list Deferred.t - ; answer_sync_ledger_query : - (Pasta_bindings.Fp.t * Sync_ledger.Query.t) Envelope.Incoming.t - -> (Sync_ledger.Answer.t, Error.t) result Deferred.t - ; get_ancestry : - ( Consensus.Data.Consensus_state.Value.t - , Pasta_bindings.Fp.t ) - With_hash.t - Envelope.Incoming.t - -> ( Mina_block.t - , State_body_hash.t list * Mina_block.t ) - Proof_carrying_data.t - option - Deferred.t - ; get_best_tip : - unit Envelope.Incoming.t - -> ( Mina_block.t - , Pasta_bindings.Fp.t list * Mina_block.t ) - Proof_carrying_data.t - option - Deferred.t - ; get_node_status : - unit Envelope.Incoming.t - -> (Mina_networking.Rpcs.Get_node_status.Node_status.t, Error.t) result - Deferred.t - ; get_transition_knowledge : - unit Envelope.Incoming.t -> Pasta_bindings.Fp.t list Deferred.t - ; get_transition_chain_proof : - Pasta_bindings.Fp.t Envelope.Incoming.t - -> (Pasta_bindings.Fp.t * Pasta_bindings.Fp.t list) option Deferred.t - ; get_transition_chain : - Pasta_bindings.Fp.t list Envelope.Incoming.t - -> Mina_block.t list option Deferred.t + ; rpc_mocks : Gossip_net.Fake.rpc_mocks } type peer_network = @@ -107,28 +66,29 @@ let setup (type n) ~context:(module Context : CONTEXT) let fake_gossip_network = Gossip_net.Fake.create_network (Vect.to_list peers) in - let config peer consensus_local_state = - let trust_system = Trust_system.null () in + let context trust_system consensus_local_state : + (module Mina_networking.CONTEXT) = + ( module struct + include Context + + let trust_system = trust_system + + let time_controller = time_controller + + let consensus_local_state = consensus_local_state + end ) + in + let config rpc_mocks peer = let open Mina_networking.Config in - don't_wait_for - (Pipe_lib.Strict_pipe.Reader.iter - (Trust_system.upcall_pipe trust_system) - ~f:(const Deferred.unit) ) ; - { logger - ; trust_system - ; time_controller - ; consensus_local_state - ; is_seed = Vect.is_empty peers + { is_seed = Vect.is_empty peers ; genesis_ledger_hash = Mina_ledger.Ledger.merkle_root (Lazy.force (Precomputed_values.genesis_ledger precomputed_values)) - ; constraint_constants = precomputed_values.constraint_constants - ; consensus_constants = precomputed_values.consensus_constants - ; precomputed_values ; creatable_gossip_net = Gossip_net.Any.Creatable ( (module Gossip_net.Fake) - , Gossip_net.Fake.create_instance fake_gossip_network peer ) + , Gossip_net.Fake.create_instance ~network:fake_gossip_network + ~rpc_mocks ~local_ip:peer ) ; log_gossip_heard = { snark_pool_diff = true ; transaction_pool_diff = true @@ -136,32 +96,112 @@ let setup (type n) ~context:(module Context : CONTEXT) } } in + let get_node_status _ = failwith "unimplemented" in let peer_networks = Vect.map2 peers states ~f:(fun peer state -> + let trust_system = Trust_system.null () in + don't_wait_for + (Pipe_lib.Strict_pipe.Reader.iter + Trust_system.(upcall_pipe trust_system) + ~f:(const Deferred.unit) ) ; let network = Thread_safe.block_on_async_exn (fun () -> - (* TODO: merge implementations with mina_lib *) Mina_networking.create - (config peer state.consensus_local_state) + (context trust_system state.consensus_local_state) + (config state.rpc_mocks peer) ~sinks: ( Transition_handler.Block_sink.void , Network_pool.Transaction_pool.Remote_sink.void , Network_pool.Snark_pool.Remote_sink.void ) - ~get_staged_ledger_aux_and_pending_coinbases_at_hash: - state.get_staged_ledger_aux_and_pending_coinbases_at_hash - ~get_some_initial_peers:state.get_some_initial_peers - ~answer_sync_ledger_query:state.answer_sync_ledger_query - ~get_ancestry:state.get_ancestry - ~get_best_tip:state.get_best_tip - ~get_node_status:state.get_node_status - ~get_transition_knowledge:state.get_transition_knowledge - ~get_transition_chain_proof:state.get_transition_chain_proof - ~get_transition_chain:state.get_transition_chain ) + ~get_transition_frontier:(Fn.const (Some state.frontier)) + ~get_node_status ) in { peer; state; network } ) in { fake_gossip_network; peer_networks } +include struct + open Mina_networking + + type 'a fn_with_mocks = + ?get_some_initial_peers: + ( Rpcs.Get_some_initial_peers.query + , Rpcs.Get_some_initial_peers.response ) + Gossip_net.Fake.rpc_mock + -> ?get_staged_ledger_aux_and_pending_coinbases_at_hash: + ( Rpcs.Get_staged_ledger_aux_and_pending_coinbases_at_hash.query + , Rpcs.Get_staged_ledger_aux_and_pending_coinbases_at_hash.response ) + Gossip_net.Fake.rpc_mock + -> ?answer_sync_ledger_query: + ( Rpcs.Answer_sync_ledger_query.query + , Rpcs.Answer_sync_ledger_query.response ) + Gossip_net.Fake.rpc_mock + -> ?get_transition_chain: + ( Rpcs.Get_transition_chain.query + , Rpcs.Get_transition_chain.response ) + Gossip_net.Fake.rpc_mock + -> ?get_transition_knowledge: + ( Rpcs.Get_transition_knowledge.query + , Rpcs.Get_transition_knowledge.response ) + Gossip_net.Fake.rpc_mock + -> ?get_transition_chain_proof: + ( Rpcs.Get_transition_chain_proof.query + , Rpcs.Get_transition_chain_proof.response ) + Gossip_net.Fake.rpc_mock + -> ?get_node_status: + ( Rpcs.Get_node_status.query + , Rpcs.Get_node_status.response ) + Gossip_net.Fake.rpc_mock + -> ?get_ancestry: + ( Rpcs.Get_ancestry.query + , Rpcs.Get_ancestry.response ) + Gossip_net.Fake.rpc_mock + -> ?get_best_tip: + ( Rpcs.Get_best_tip.query + , Rpcs.Get_best_tip.response ) + Gossip_net.Fake.rpc_mock + -> 'a + + let make_peer_state : + ( frontier:Transition_frontier.t + -> consensus_local_state:Consensus.Data.Local_state.t + -> peer_state ) + fn_with_mocks = + fun ?get_some_initial_peers + ?get_staged_ledger_aux_and_pending_coinbases_at_hash + ?answer_sync_ledger_query ?get_transition_chain ?get_transition_knowledge + ?get_transition_chain_proof ?get_node_status ?get_ancestry ?get_best_tip + ~frontier ~consensus_local_state -> + let rpc_mocks : Gossip_net.Fake.rpc_mocks = + let get_mock (type q r) (rpc : (q, r) Rpcs.rpc) : + (q, r) Gossip_net.Fake.rpc_mock option = + match rpc with + | Get_some_initial_peers -> + get_some_initial_peers + | Get_staged_ledger_aux_and_pending_coinbases_at_hash -> + get_staged_ledger_aux_and_pending_coinbases_at_hash + | Answer_sync_ledger_query -> + answer_sync_ledger_query + | Get_transition_chain -> + get_transition_chain + | Get_transition_knowledge -> + get_transition_knowledge + | Get_transition_chain_proof -> + get_transition_chain_proof + | Get_node_status -> + get_node_status + | Get_ancestry -> + get_ancestry + | Ban_notify -> + None + | Get_best_tip -> + get_best_tip + in + { get_mock } + in + { frontier; consensus_local_state; rpc_mocks } +end + module Generator = struct open Quickcheck open Generator.Let_syntax @@ -173,129 +213,10 @@ module Generator = struct -> use_super_catchup:bool -> peer_state Generator.t - let make_peer_state ?get_staged_ledger_aux_and_pending_coinbases_at_hash - ?get_some_initial_peers ?answer_sync_ledger_query ?get_ancestry - ?get_best_tip ?get_node_status ?get_transition_knowledge - ?get_transition_chain_proof ?get_transition_chain ~frontier - ~consensus_local_state ~context:(module Context : CONTEXT) = - let open Context in - { frontier - ; consensus_local_state - ; get_staged_ledger_aux_and_pending_coinbases_at_hash = - ( match get_staged_ledger_aux_and_pending_coinbases_at_hash with - | Some f -> - f - | None -> - fun query_env -> - let input = Envelope.Incoming.data query_env in - Deferred.return - (let open Option.Let_syntax in - let%map ( scan_state - , expected_merkle_root - , pending_coinbases - , protocol_states ) = - Sync_handler - .get_staged_ledger_aux_and_pending_coinbases_at_hash ~frontier - input - in - let staged_ledger_hash = - Staged_ledger_hash.of_aux_ledger_and_coinbase_hash - (Staged_ledger.Scan_state.hash scan_state) - expected_merkle_root pending_coinbases - in - [%log debug] - ~metadata: - [ ( "staged_ledger_hash" - , Staged_ledger_hash.to_yojson staged_ledger_hash ) - ] - "sending scan state and pending coinbase" ; - ( scan_state - , expected_merkle_root - , pending_coinbases - , protocol_states )) ) - ; get_some_initial_peers = - ( match get_some_initial_peers with - | Some f -> - f - | None -> - fun _ -> Deferred.return [] ) - ; answer_sync_ledger_query = - ( match answer_sync_ledger_query with - | Some f -> - f - | None -> - fun query_env -> - let ledger_hash, _ = Envelope.Incoming.data query_env in - Sync_handler.answer_query ~frontier ledger_hash - (Envelope.Incoming.map ~f:Tuple2.get2 query_env) - ~logger:(Logger.create ()) ~trust_system:(Trust_system.null ()) - |> Deferred.map - (* begin error string prefix so we can pattern-match *) - ~f: - (Result.of_option - ~error: - (Error.createf - !"%s for ledger_hash: %{sexp:Ledger_hash.t}" - Mina_networking.refused_answer_query_string - ledger_hash ) ) ) - ; get_ancestry = - ( match get_ancestry with - | Some f -> - f - | None -> - fun query_env -> - Deferred.return - (Sync_handler.Root.prove - ~context:(module Context) - ~frontier - ( Envelope.Incoming.data query_env - |> With_hash.map_hash ~f:(fun state_hash -> - { State_hash.State_hashes.state_hash - ; state_body_hash = None - } ) ) ) ) - ; get_best_tip = - ( match get_best_tip with - | Some f -> - f - | None -> - fun _ -> failwith "Get_best_tip unimplemented" ) - ; get_node_status = - ( match get_node_status with - | Some f -> - f - | None -> - fun _ -> failwith "Get_node_status unimplemented" ) - ; get_transition_knowledge = - ( match get_transition_knowledge with - | Some f -> - f - | None -> - fun _query -> Deferred.return (Sync_handler.best_tip_path ~frontier) - ) - ; get_transition_chain_proof = - ( match get_transition_chain_proof with - | Some f -> - f - | None -> - fun query_env -> - Deferred.return - (Transition_chain_prover.prove ~frontier - (Envelope.Incoming.data query_env) ) ) - ; get_transition_chain = - ( match get_transition_chain with - | Some f -> - f - | None -> - fun query_env -> - Deferred.return - (Sync_handler.get_transition_chain ~frontier - (Envelope.Incoming.data query_env) ) ) - } - - let fresh_peer_custom_rpc ?get_staged_ledger_aux_and_pending_coinbases_at_hash - ?get_some_initial_peers ?answer_sync_ledger_query ?get_ancestry - ?get_best_tip ?get_node_status ?get_transition_knowledge - ?get_transition_chain_proof ?get_transition_chain + let fresh_peer_custom_rpc ?get_some_initial_peers + ?get_staged_ledger_aux_and_pending_coinbases_at_hash + ?answer_sync_ledger_query ?get_transition_chain ?get_transition_knowledge + ?get_transition_chain_proof ?get_node_status ?get_ancestry ?get_best_tip ~context:(module Context : CONTEXT) ~verifier ~max_frontier_length ~use_super_catchup = let open Context in @@ -319,7 +240,6 @@ module Generator = struct ~use_super_catchup () in make_peer_state ~frontier ~consensus_local_state - ~context:(module Context) ?get_staged_ledger_aux_and_pending_coinbases_at_hash ?get_some_initial_peers ?answer_sync_ledger_query ?get_ancestry ?get_best_tip ?get_node_status ?get_transition_knowledge @@ -336,11 +256,10 @@ module Generator = struct ~context:(module Context) ~verifier ~max_frontier_length ~use_super_catchup - let peer_with_branch_custom_rpc ~frontier_branch_size + let peer_with_branch_custom_rpc ~frontier_branch_size ?get_some_initial_peers ?get_staged_ledger_aux_and_pending_coinbases_at_hash - ?get_some_initial_peers ?answer_sync_ledger_query ?get_ancestry - ?get_best_tip ?get_node_status ?get_transition_knowledge - ?get_transition_chain_proof ?get_transition_chain + ?answer_sync_ledger_query ?get_transition_chain ?get_transition_knowledge + ?get_transition_chain_proof ?get_node_status ?get_ancestry ?get_best_tip ~context:(module Context : CONTEXT) ~verifier ~max_frontier_length ~use_super_catchup = let open Context in @@ -369,7 +288,6 @@ module Generator = struct ~f:(Transition_frontier.add_breadcrumb_exn frontier) ) ; make_peer_state ~frontier ~consensus_local_state - ~context:(module Context) ?get_staged_ledger_aux_and_pending_coinbases_at_hash ?get_some_initial_peers ?answer_sync_ledger_query ?get_ancestry ?get_best_tip ?get_node_status ?get_transition_knowledge diff --git a/src/lib/fake_network/fake_network.mli b/src/lib/fake_network/fake_network.mli index 3705020cbc4..556b490a4e6 100644 --- a/src/lib/fake_network/fake_network.mli +++ b/src/lib/fake_network/fake_network.mli @@ -1,8 +1,5 @@ -open Async open Core open Gadt_lib -open Network_peer -open Mina_base module Sync_ledger = Mina_ledger.Sync_ledger module type CONTEXT = sig @@ -21,47 +18,7 @@ type 'n num_peers = 'n Peano.gt_1 type peer_state = { frontier : Transition_frontier.t ; consensus_local_state : Consensus.Data.Local_state.t - ; get_staged_ledger_aux_and_pending_coinbases_at_hash : - Pasta_bindings.Fp.t Envelope.Incoming.t - -> ( Staged_ledger.Scan_state.t - * Pasta_bindings.Fp.t - * Pending_coinbase.t - * Mina_state.Protocol_state.value list ) - option - Deferred.t - ; get_some_initial_peers : unit Envelope.Incoming.t -> Peer.t list Deferred.t - ; answer_sync_ledger_query : - (Pasta_bindings.Fp.t * Sync_ledger.Query.t) Envelope.Incoming.t - -> (Sync_ledger.Answer.t, Error.t) result Deferred.t - ; get_ancestry : - ( Consensus.Data.Consensus_state.Value.t - , Pasta_bindings.Fp.t ) - With_hash.t - Envelope.Incoming.t - -> ( Mina_block.t - , State_body_hash.t list * Mina_block.t ) - Proof_carrying_data.t - option - Deferred.t - ; get_best_tip : - unit Envelope.Incoming.t - -> ( Mina_block.t - , Pasta_bindings.Fp.t list * Mina_block.t ) - Proof_carrying_data.t - option - Deferred.t - ; get_node_status : - unit Envelope.Incoming.t - -> (Mina_networking.Rpcs.Get_node_status.Node_status.t, Error.t) result - Deferred.t - ; get_transition_knowledge : - unit Envelope.Incoming.t -> Pasta_bindings.Fp.t list Deferred.t - ; get_transition_chain_proof : - Pasta_bindings.Fp.t Envelope.Incoming.t - -> (Pasta_bindings.Fp.t * Pasta_bindings.Fp.t list) option Deferred.t - ; get_transition_chain : - Pasta_bindings.Fp.t list Envelope.Incoming.t - -> Mina_block.t list option Deferred.t + ; rpc_mocks : Mina_networking.Gossip_net.Fake.rpc_mocks } type peer_network = @@ -76,6 +33,49 @@ type nonrec 'n t = } constraint 'n = _ num_peers +include sig + open Mina_networking + + type 'a fn_with_mocks = + ?get_some_initial_peers: + ( Rpcs.Get_some_initial_peers.query + , Rpcs.Get_some_initial_peers.response ) + Gossip_net.Fake.rpc_mock + -> ?get_staged_ledger_aux_and_pending_coinbases_at_hash: + ( Rpcs.Get_staged_ledger_aux_and_pending_coinbases_at_hash.query + , Rpcs.Get_staged_ledger_aux_and_pending_coinbases_at_hash.response ) + Gossip_net.Fake.rpc_mock + -> ?answer_sync_ledger_query: + ( Rpcs.Answer_sync_ledger_query.query + , Rpcs.Answer_sync_ledger_query.response ) + Gossip_net.Fake.rpc_mock + -> ?get_transition_chain: + ( Rpcs.Get_transition_chain.query + , Rpcs.Get_transition_chain.response ) + Gossip_net.Fake.rpc_mock + -> ?get_transition_knowledge: + ( Rpcs.Get_transition_knowledge.query + , Rpcs.Get_transition_knowledge.response ) + Gossip_net.Fake.rpc_mock + -> ?get_transition_chain_proof: + ( Rpcs.Get_transition_chain_proof.query + , Rpcs.Get_transition_chain_proof.response ) + Gossip_net.Fake.rpc_mock + -> ?get_node_status: + ( Rpcs.Get_node_status.query + , Rpcs.Get_node_status.response ) + Gossip_net.Fake.rpc_mock + -> ?get_ancestry: + ( Rpcs.Get_ancestry.query + , Rpcs.Get_ancestry.response ) + Gossip_net.Fake.rpc_mock + -> ?get_best_tip: + ( Rpcs.Get_best_tip.query + , Rpcs.Get_best_tip.response ) + Gossip_net.Fake.rpc_mock + -> 'a +end + module Generator : sig open Quickcheck @@ -86,104 +86,12 @@ module Generator : sig -> use_super_catchup:bool -> peer_state Generator.t - val fresh_peer_custom_rpc : - ?get_staged_ledger_aux_and_pending_coinbases_at_hash: - ( Pasta_bindings.Fp.t Envelope.Incoming.t - -> ( Staged_ledger.Scan_state.t - * Pasta_bindings.Fp.t - * Pending_coinbase.t - * Mina_state.Protocol_state.value list ) - option - Deferred.t ) - -> ?get_some_initial_peers: - (unit Envelope.Incoming.t -> Peer.t list Deferred.t) - -> ?answer_sync_ledger_query: - ( (Pasta_bindings.Fp.t * Sync_ledger.Query.t) Envelope.Incoming.t - -> (Sync_ledger.Answer.t, Error.t) result Deferred.t ) - -> ?get_ancestry: - ( ( Consensus.Data.Consensus_state.Value.t - , Pasta_bindings.Fp.t ) - With_hash.t - Envelope.Incoming.t - -> ( Mina_block.t - , State_body_hash.t list * Mina_block.t ) - Proof_carrying_data.t - option - Deferred.t ) - -> ?get_best_tip: - ( unit Envelope.Incoming.t - -> ( Mina_block.t - , Pasta_bindings.Fp.t list * Mina_block.t ) - Proof_carrying_data.t - option - Deferred.t ) - -> ?get_node_status: - ( unit Envelope.Incoming.t - -> ( Mina_networking.Rpcs.Get_node_status.Node_status.t - , Error.t ) - result - Deferred.t ) - -> ?get_transition_knowledge: - (unit Envelope.Incoming.t -> Pasta_bindings.Fp.t list Deferred.t) - -> ?get_transition_chain_proof: - ( Pasta_bindings.Fp.t Envelope.Incoming.t - -> (Pasta_bindings.Fp.t * Pasta_bindings.Fp.t list) option Deferred.t - ) - -> ?get_transition_chain: - ( Pasta_bindings.Fp.t list Envelope.Incoming.t - -> Mina_block.t list option Deferred.t ) - -> peer_config + val fresh_peer_custom_rpc : peer_config fn_with_mocks val fresh_peer : peer_config val peer_with_branch_custom_rpc : - frontier_branch_size:int - -> ?get_staged_ledger_aux_and_pending_coinbases_at_hash: - ( Pasta_bindings.Fp.t Envelope.Incoming.t - -> ( Staged_ledger.Scan_state.t - * Pasta_bindings.Fp.t - * Pending_coinbase.t - * Mina_state.Protocol_state.value list ) - option - Deferred.t ) - -> ?get_some_initial_peers: - (unit Envelope.Incoming.t -> Peer.t list Deferred.t) - -> ?answer_sync_ledger_query: - ( (Pasta_bindings.Fp.t * Sync_ledger.Query.t) Envelope.Incoming.t - -> (Sync_ledger.Answer.t, Error.t) result Deferred.t ) - -> ?get_ancestry: - ( ( Consensus.Data.Consensus_state.Value.t - , Pasta_bindings.Fp.t ) - With_hash.t - Envelope.Incoming.t - -> ( Mina_block.t - , State_body_hash.t list * Mina_block.t ) - Proof_carrying_data.t - option - Deferred.t ) - -> ?get_best_tip: - ( unit Envelope.Incoming.t - -> ( Mina_block.t - , Pasta_bindings.Fp.t list * Mina_block.t ) - Proof_carrying_data.t - option - Deferred.t ) - -> ?get_node_status: - ( unit Envelope.Incoming.t - -> ( Mina_networking.Rpcs.Get_node_status.Node_status.t - , Error.t ) - result - Deferred.t ) - -> ?get_transition_knowledge: - (unit Envelope.Incoming.t -> Pasta_bindings.Fp.t list Deferred.t) - -> ?get_transition_chain_proof: - ( Pasta_bindings.Fp.t Envelope.Incoming.t - -> (Pasta_bindings.Fp.t * Pasta_bindings.Fp.t list) option Deferred.t - ) - -> ?get_transition_chain: - ( Pasta_bindings.Fp.t list Envelope.Incoming.t - -> Mina_block.t list option Deferred.t ) - -> peer_config + frontier_branch_size:int -> peer_config fn_with_mocks val peer_with_branch : frontier_branch_size:int -> peer_config diff --git a/src/lib/gossip_net/any.ml b/src/lib/gossip_net/any.ml index 9f7c520844b..f86fbe51175 100644 --- a/src/lib/gossip_net/any.ml +++ b/src/lib/gossip_net/any.ml @@ -1,41 +1,40 @@ open Async_kernel +open Intf module type S = sig - module Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf + module Rpc_interface : RPC_INTERFACE - module type Implementation_intf = - Intf.Gossip_net_intf with module Rpc_intf := Rpc_intf + module type IMPLEMENTATION = + GOSSIP_NET with module Rpc_interface := Rpc_interface - type 't implementation = (module Implementation_intf with type t = 't) + type 't implementation = (module IMPLEMENTATION with type t = 't) type t = Any : 't implementation * 't -> t - include Intf.Gossip_net_intf with module Rpc_intf := Rpc_intf and type t := t + include IMPLEMENTATION with type t := t - type 't creator = Rpc_intf.rpc_handler list -> Message.sinks -> 't Deferred.t + type 't creator = Rpc_interface.ctx -> Message.sinks -> 't Deferred.t type creatable = Creatable : 't implementation * 't creator -> creatable val create : creatable -> t creator end -module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : - S with module Rpc_intf := Rpc_intf = struct - open Rpc_intf +module Make (Rpc_interface : RPC_INTERFACE) : + S with module Rpc_interface := Rpc_interface = struct + module type IMPLEMENTATION = + GOSSIP_NET with module Rpc_interface := Rpc_interface - module type Implementation_intf = - Intf.Gossip_net_intf with module Rpc_intf := Rpc_intf - - type 't implementation = (module Implementation_intf with type t = 't) + type 't implementation = (module IMPLEMENTATION with type t = 't) type t = Any : 't implementation * 't -> t - type 't creator = rpc_handler list -> Message.sinks -> 't Deferred.t + type 't creator = Rpc_interface.ctx -> Message.sinks -> 't Deferred.t type creatable = Creatable : 't implementation * 't creator -> creatable - let create (Creatable ((module M), creator)) impls sinks = - let%map gossip_net = creator impls sinks in + let create (Creatable ((module M), creator)) ctx sinks = + let%map gossip_net = creator ctx sinks in Any ((module M), gossip_net) let peers (Any ((module M), t)) = M.peers t diff --git a/src/lib/gossip_net/fake.ml b/src/lib/gossip_net/fake.ml index c438a2712d3..efbcd0faeed 100644 --- a/src/lib/gossip_net/fake.ml +++ b/src/lib/gossip_net/fake.ml @@ -2,38 +2,47 @@ open Async_kernel open Core open Pipe_lib open Network_peer +open Intf (* TODO: Implement RPC version translations (documented in Async_rpc_kernel). * This code currently only supports the latest version of RPCs. *) module type S = sig - include Intf.Gossip_net_intf + include GOSSIP_NET type network + type ('q, 'r) rpc_mock = 'q Envelope.Incoming.t -> 'r Deferred.t + + type rpc_mocks = + { get_mock : 'q 'r. ('q, 'r) Rpc_interface.rpc -> ('q, 'r) rpc_mock option } + val create_network : Peer.t list -> network val create_instance : - network - -> Peer.t - -> Rpc_intf.rpc_handler list + network:network + -> rpc_mocks:rpc_mocks + -> local_ip:Peer.t + -> Rpc_interface.ctx -> Message.sinks -> t Deferred.t end -module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : - S with module Rpc_intf := Rpc_intf = struct - open Intf - open Rpc_intf +module Make (Rpc_interface : RPC_INTERFACE) : + S with module Rpc_interface := Rpc_interface = struct + type ('q, 'r) rpc_mock = 'q Envelope.Incoming.t -> 'r Deferred.t + + type rpc_mocks = + { get_mock : 'q 'r. ('q, 'r) Rpc_interface.rpc -> ('q, 'r) rpc_mock option } module Network = struct type rpc_hook = { hook : 'q 'r. Peer.Id.t - -> ('q, 'r) rpc + -> ('q, 'r) Rpc_interface.rpc -> 'q - -> 'r Network_peer.Rpc_intf.rpc_response Deferred.t + -> 'r rpc_response Deferred.t } type network_interface = { sinks : Message.sinks; rpc_hook : rpc_hook } @@ -103,9 +112,9 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : -> _ -> sender_id:Peer.Id.t -> responder_id:Peer.Id.t - -> (q, r) rpc + -> (q, r) Rpc_interface.rpc -> q - -> r Network_peer.Rpc_intf.rpc_response Deferred.t = + -> r rpc_response Deferred.t = fun t peer_table ~sender_id ~responder_id rpc query -> let responder = Option.value_exn @@ -117,56 +126,56 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : | Ok intf -> intf.rpc_hook.hook sender_id rpc query | Error e -> - Deferred.return (Network_peer.Rpc_intf.Failed_to_connect e) + Deferred.return (Failed_to_connect e) end module Instance = struct type t = - { network : Network.t - ; me : Peer.t - ; rpc_handlers : rpc_handler list + { time_controller : Block_time.Controller.t + ; network : Network.t + ; local_ip : Peer.t ; peer_table : (Peer.Id.t, Peer.t) Hashtbl.t ; initial_peers : Peer.t list ; connection_gating : Mina_net2.connection_gating ref ; ban_notification_reader : ban_notification Linear_pipe.Reader.t ; ban_notification_writer : ban_notification Linear_pipe.Writer.t - ; time_controller : Block_time.Controller.t } - let rpc_hook t rpc_handlers = + let rpc_hook ~rpc_mocks ctx t = let hook : type q r. Peer.Id.t - -> (q, r) rpc + -> (q, r) Rpc_interface.rpc -> q - -> r Network_peer.Rpc_intf.rpc_response Deferred.t = + -> r rpc_response Deferred.t = fun peer rpc query -> - let (module Impl) = implementation_of_rpc rpc in - let latest_version = - (* this is assumed safe since there should always be at least one version *) - Int.Set.max_elt (Impl.versions ()) - |> Option.value_exn ~error:(Error.of_string "no versions?") - in let sender = Hashtbl.find t.peer_table peer |> Option.value_exn ~error:(Error.createf "cannot find peer %s" peer) in - match - List.find_map rpc_handlers ~f:(fun handler -> - match_handler handler rpc ~do_:(fun f -> - f sender ~version:latest_version query ) ) - with - | None -> - failwith "fake gossip net error: rpc not implemented" - | Some deferred -> - let%map response = deferred in - Network_peer.Rpc_intf.Connected - (Envelope.Incoming.wrap_peer ~data:(Ok response) ~sender) + let query_env = Envelope.Incoming.wrap_peer ~data:query ~sender in + let%map response = + match rpc_mocks.get_mock rpc with + | Some f -> + f query_env + | None -> + let (module Impl) = Rpc_interface.implementation rpc in + let latest_version = + (* this is assumed safe since there should always be at least one version *) + Int.Set.max_elt (Impl.versions ()) + |> Option.value_exn ~error:(Error.of_string "no versions?") + in + Impl.handle_request ctx ~version:latest_version query_env + in + let response_env = + Envelope.Incoming.wrap_peer ~data:(Ok response) ~sender:t.local_ip + in + Connected response_env in Network.{ hook } - let create network me rpc_handlers sinks = - let initial_peers = Network.get_initial_peers network me.Peer.host in + let create ~network ~rpc_mocks ~(local_ip : Peer.t) ctx sinks = + let initial_peers = Network.get_initial_peers network local_ip.host in let peer_table = Hashtbl.create (module Peer.Id) in List.iter initial_peers ~f:(fun peer -> Hashtbl.add_exn peer_table ~key:peer.peer_id ~data:peer ) ; @@ -179,8 +188,7 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : in let t = { network - ; me - ; rpc_handlers + ; local_ip ; peer_table ; initial_peers ; connection_gating = @@ -193,9 +201,9 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : } in Network.( - attach_interface network me - { sinks; rpc_hook = rpc_hook t rpc_handlers }) ; - t + attach_interface network local_ip + { sinks; rpc_hook = rpc_hook ~rpc_mocks ctx t }) ; + return t let peers { peer_table; _ } = Hashtbl.data peer_table |> Deferred.return @@ -235,7 +243,7 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : ban_notification_reader let query_peer ?heartbeat_timeout:_ ?timeout:_ t peer rpc query = - Network.call_rpc t.network t.peer_table ~sender_id:t.me.peer_id + Network.call_rpc t.network t.peer_table ~sender_id:t.local_ip.peer_id ~responder_id:peer rpc query let query_peer' ?how ?heartbeat_timeout ?timeout t peer rpc qs = @@ -249,7 +257,7 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : | Connected x -> x.data | Failed_to_connect e -> - return (Network_peer.Rpc_intf.Failed_to_connect e) ) + return (Failed_to_connect e) ) |> Or_error.all in let sender = @@ -263,7 +271,7 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : let broadcast_state ?origin_topic t state = ignore origin_topic ; - Network.broadcast t.network ~sender:t.me state + Network.broadcast t.network ~sender:t.local_ip state (fun (Any_sinks (sinksM, (sink_block, _, _))) (env, vc) -> let time = Block_time.now t.time_controller in let module M = (val sinksM) in @@ -273,7 +281,7 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : let broadcast_snark_pool_diff ?origin_topic ?nonce t diff = ignore origin_topic ; ignore nonce ; - Network.broadcast t.network ~sender:t.me diff + Network.broadcast t.network ~sender:t.local_ip diff (fun (Any_sinks (sinksM, (_, _, sink_snark_work))) -> let module M = (val sinksM) in M.Snark_sink.push sink_snark_work ) @@ -281,7 +289,7 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : let broadcast_transaction_pool_diff ?origin_topic ?nonce t diff = ignore origin_topic ; ignore nonce ; - Network.broadcast t.network ~sender:t.me diff + Network.broadcast t.network ~sender:t.local_ip diff (fun (Any_sinks (sinksM, (_, sink_tx, _))) -> let module M = (val sinksM) in M.Tx_sink.push sink_tx ) @@ -301,6 +309,5 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : let create_network = Network.create - let create_instance network local_ip impls sinks = - Deferred.return (Instance.create network local_ip impls sinks) + let create_instance = Instance.create end diff --git a/src/lib/gossip_net/gossip_net.ml b/src/lib/gossip_net/gossip_net.ml index 943798692cc..f2dcc294a37 100644 --- a/src/lib/gossip_net/gossip_net.ml +++ b/src/lib/gossip_net/gossip_net.ml @@ -4,24 +4,24 @@ module Libp2p = Libp2p module Fake = Fake module type S = sig - module Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf + module Rpc_interface : RPC_INTERFACE include module type of Intf module Message : module type of Message - module Any : Any.S with module Rpc_intf := Rpc_intf + module Any : Any.S with module Rpc_interface := Rpc_interface - module Libp2p : Libp2p.S with module Rpc_intf := Rpc_intf + module Libp2p : Libp2p.S with module Rpc_interface := Rpc_interface - module Fake : Fake.S with module Rpc_intf := Rpc_intf + module Fake : Fake.S with module Rpc_interface := Rpc_interface end -module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : - S with module Rpc_intf := Rpc_intf = struct +module Make (Rpc_interface : RPC_INTERFACE) : + S with module Rpc_interface := Rpc_interface = struct include Intf module Message = Message - module Any = Any.Make (Rpc_intf) - module Fake = Fake.Make (Rpc_intf) - module Libp2p = Libp2p.Make (Rpc_intf) + module Any = Any.Make (Rpc_interface) + module Fake = Fake.Make (Rpc_interface) + module Libp2p = Libp2p.Make (Rpc_interface) end diff --git a/src/lib/gossip_net/intf.ml b/src/lib/gossip_net/intf.ml index d4568aab422..59dec2c1141 100644 --- a/src/lib/gossip_net/intf.ml +++ b/src/lib/gossip_net/intf.ml @@ -2,17 +2,86 @@ open Async open Core_kernel open Network_peer open Pipe_lib -open Network_peer.Rpc_intf type ban_creator = { banned_peer : Peer.t; banned_until : Time.t } [@@deriving fields] type ban_notification = { banned_peer : Peer.t; banned_until : Time.t } -module type Gossip_net_intf = sig +type ('query, 'response) rpc_fn = + version:int -> 'query Envelope.Incoming.t -> 'response Deferred.t + +type 'r rpc_response = + | Failed_to_connect of Error.t + | Connected of 'r Or_error.t Envelope.Incoming.t + +module type RPC_IMPLEMENTATION = sig + type ctx + + type query + + type response + + val name : string + + val versions : unit -> Int.Set.t + + val sent_counter : Mina_metrics.Counter.t * Mina_metrics.Gauge.t + + val received_counter : Mina_metrics.Counter.t * Mina_metrics.Gauge.t + + val failed_request_counter : Mina_metrics.Counter.t + + val failed_response_counter : Mina_metrics.Counter.t + + val implement_multi : + ?log_not_previously_seen_version:(name:string -> int -> unit) + -> (Peer.t -> version:int -> query -> response Deferred.t) + -> Peer.t Rpc.Implementation.t list + + val dispatch_multi : + Versioned_rpc.Connection_with_menu.t + -> query + -> response Deferred.Or_error.t + + val log_request_received : logger:Logger.t -> sender:Peer.t -> query -> unit + + (* TODO: make this non-optional *) + val receipt_trust_action_message : + query -> (string * (string, Yojson.Safe.t) List.Assoc.t) option + + val handle_request : ctx -> (query, response) rpc_fn + + val response_is_successful : response -> bool + + val rate_limit_cost : query -> int + + val rate_limit_budget : int * [ `Per of Time.Span.t ] +end + +type ('ctx, 'query, 'response) rpc_implementation = + (module RPC_IMPLEMENTATION + with type ctx = 'ctx + and type query = 'query + and type response = 'response ) + +module type RPC_INTERFACE = sig + type ctx + + type ('query, 'response) rpc + + type any_rpc = Rpc : ('query, 'response) rpc -> any_rpc + + val all_rpcs : any_rpc list + + val implementation : + ('query, 'response) rpc -> (ctx, 'query, 'response) rpc_implementation +end + +module type GOSSIP_NET = sig type t - module Rpc_intf : Rpc_interface_intf + module Rpc_interface : RPC_INTERFACE val restart_helper : t -> unit @@ -50,7 +119,7 @@ module type Gossip_net_intf = sig -> ?timeout:Time.Span.t -> t -> Peer.Id.t - -> ('q, 'r) Rpc_intf.rpc + -> ('q, 'r) Rpc_interface.rpc -> 'q list -> 'r list rpc_response Deferred.t @@ -59,14 +128,14 @@ module type Gossip_net_intf = sig -> ?timeout:Time.Span.t -> t -> Peer.Id.t - -> ('q, 'r) Rpc_intf.rpc + -> ('q, 'r) Rpc_interface.rpc -> 'q -> 'r rpc_response Deferred.t val query_random_peers : t -> int - -> ('q, 'r) Rpc_intf.rpc + -> ('q, 'r) Rpc_interface.rpc -> 'q -> 'r rpc_response Deferred.t List.t Deferred.t diff --git a/src/lib/gossip_net/libp2p.ml b/src/lib/gossip_net/libp2p.ml index 476bb33ff74..2bdf77364bf 100644 --- a/src/lib/gossip_net/libp2p.ml +++ b/src/lib/gossip_net/libp2p.ml @@ -2,7 +2,7 @@ open Core open Async open Network_peer open Pipe_lib -open Network_peer.Rpc_intf +open Intf type ('q, 'r) dispatch = Versioned_rpc.Connection_with_menu.t -> 'q -> 'r Deferred.Or_error.t @@ -57,13 +57,13 @@ module Config = struct end module type S = sig - include Intf.Gossip_net_intf + include GOSSIP_NET val create : ?allow_multiple_instances:bool -> Config.t -> pids:Child_processes.Termination.t - -> Rpc_intf.rpc_handler list + -> Rpc_interface.ctx -> Message.sinks -> t Deferred.t end @@ -121,10 +121,8 @@ let on_gossip_decode_failure (config : Config.t) envelope (err : Error.t) = |> don't_wait_for ; () -module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : - S with module Rpc_intf := Rpc_intf = struct - open Rpc_intf - +module Make (Rpc_interface : RPC_INTERFACE) : + S with module Rpc_interface := Rpc_interface = struct module T = struct type t = { config : Config.t @@ -137,9 +135,11 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : ; restart_helper : unit -> unit } - let create_rpc_implementations - (Rpc_handler { rpc; f = handler; cost; budget }) = - let (module Impl) = implementation_of_rpc rpc in + (* TODO: should we share this with the Fake network impl? *) + let setup_rpc (type query response) ctx trust_system + (rpc : (query, response) Rpc_interface.rpc) = + let (module Impl) = Rpc_interface.implementation rpc in + (* TODO: should be passed in from the config.logger instance *) let logger = Logger.create () in let log_rate_limiter_occasionally rl = let t = Time.Span.of_min 1. in @@ -149,16 +149,18 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : [ ("rate_limiter", Network_pool.Rate_limiter.summary rl) ] !"%s $rate_limiter" Impl.name ) in - let rl = Network_pool.Rate_limiter.create ~capacity:budget in + (* TODO: kill rate limit *) + let rl = + Network_pool.Rate_limiter.create ~capacity:Impl.rate_limit_budget + in log_rate_limiter_occasionally rl ; - let handler (peer : Network_peer.Peer.t) ~version q = + let handler (peer : Network_peer.Peer.t) ~version request = Mina_metrics.(Counter.inc_one Network.rpc_requests_received) ; Mina_metrics.(Counter.inc_one @@ fst Impl.received_counter) ; Mina_metrics.(Gauge.inc_one @@ snd Impl.received_counter) ; - let score = cost q in match Network_pool.Rate_limiter.add rl (Remote peer) ~now:(Time.now ()) - ~score + ~score:(Impl.rate_limit_cost request) with | `Capacity_exceeded -> failwithf "peer exceeded capacity: %s" @@ -166,7 +168,30 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : () | `Within_capacity -> O1trace.thread (Printf.sprintf "handle_rpc_%s" Impl.name) (fun () -> - handler peer ~version q ) + Impl.log_request_received ~logger ~sender:peer request ; + let request_env = + Envelope.Incoming.wrap_peer ~data:request ~sender:peer + in + let sender = Envelope.Incoming.sender request_env in + let%bind () = + (* TODO: don't have optional rpc actions (only needed because Ban_notify doesn't log an action) *) + match Impl.receipt_trust_action_message request with + | None -> + return () + | Some msg -> + (* TODO: kill trust system *) + Trust_system.( + record_envelope_sender trust_system logger sender + Actions.(Made_request, Some msg)) + in + let%map response = + (* TODO: kill this additional thread *) + O1trace.thread ("handle_request_" ^ Impl.name) (fun () -> + Impl.handle_request ctx ~version request_env ) + in + if not (Impl.response_is_successful response) then + Mina_metrics.Counter.inc_one Impl.failed_response_counter ; + response ) in Impl.implement_multi handler @@ -200,7 +225,7 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : (* Creates just the helper, making sure to register everything BEFORE we start listening/advertise ourselves for discovery. *) let create_libp2p ?(allow_multiple_instances = false) (config : Config.t) - rpc_handlers first_peer_ivar high_connectivity_ivar ~added_seeds ~pids + ctx first_peer_ivar high_connectivity_ivar ~added_seeds ~pids ~on_unexpected_termination ~sinks: (Message.Any_sinks (sinksM, (sink_block, sink_tx, sink_snark_work))) = @@ -334,7 +359,8 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : ~topic_config:[ [ v0_topic ]; v1_topics ] in let implementation_list = - List.bind rpc_handlers ~f:create_rpc_implementations + List.bind Rpc_interface.all_rpcs ~f:(fun (Rpc rpc) -> + setup_rpc ctx config.trust_system rpc ) in let implementations = let handle_unknown_rpc conn_state ~rpc_tag ~version = @@ -865,11 +891,11 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : -> t -> Peer.t -> _ - -> (q, r) rpc + -> (q, r) Rpc_interface.rpc -> q -> r Deferred.Or_error.t = fun ?heartbeat_timeout ?timeout t peer transport rpc query -> - let (module Impl) = implementation_of_rpc rpc in + let (module Impl) = Rpc_interface.implementation rpc in try_call_rpc_with_dispatch ?heartbeat_timeout ?timeout ~rpc_counter:Impl.sent_counter ~rpc_failed_counter:Impl.failed_request_counter ~rpc_name:Impl.name t @@ -893,7 +919,7 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : return (Failed_to_connect e) let query_peer' (type q r) ?how ?heartbeat_timeout ?timeout t - (peer_id : Peer.Id.t) (rpc : (q, r) rpc) (qs : q list) = + (peer_id : Peer.Id.t) (rpc : (q, r) Rpc_interface.rpc) (qs : q list) = let%bind net2 = !(t.net2) in match%bind Mina_net2.open_stream net2 ~protocol:rpc_transport_proto ~peer:peer_id @@ -901,7 +927,7 @@ module Make (Rpc_intf : Network_peer.Rpc_intf.Rpc_interface_intf) : | Ok stream -> let peer = Mina_net2.Libp2p_stream.remote_peer stream in let transport = prepare_stream_transport stream in - let (module Impl) = implementation_of_rpc rpc in + let (module Impl) = Rpc_interface.implementation rpc in try_call_rpc_with_dispatch ?heartbeat_timeout ?timeout ~rpc_counter:Impl.sent_counter ~rpc_failed_counter:Impl.failed_request_counter ~rpc_name:Impl.name diff --git a/src/lib/mina_lib/mina_lib.ml b/src/lib/mina_lib/mina_lib.ml index 4e8afa0dd16..9b684628339 100644 --- a/src/lib/mina_lib/mina_lib.ml +++ b/src/lib/mina_lib/mina_lib.ml @@ -7,7 +7,6 @@ open Mina_block open Pipe_lib open Strict_pipe open Signature_lib -open Network_peer module Archive_client = Archive_client module Config = Config module Conf_dir = Conf_dir @@ -1220,6 +1219,12 @@ let online_broadcaster ~constraint_constants time_controller = module type CONTEXT = sig val logger : Logger.t + val time_controller : Block_time.Controller.t + + val trust_system : Trust_system.t + + val consensus_local_state : Consensus.Data.Local_state.t + val precomputed_values : Precomputed_values.t val constraint_constants : Genesis_constants.Constraint_constants.t @@ -1233,6 +1238,12 @@ let context ~commit_id (config : Config.t) : (module CONTEXT) = ( module struct let logger = config.logger + let time_controller = config.time_controller + + let trust_system = config.trust_system + + let consensus_local_state = config.consensus_local_state + let precomputed_values = config.precomputed_values let consensus_constants = precomputed_values.consensus_constants @@ -1302,12 +1313,13 @@ let start t = t.block_production_status := block_production_status ; t.next_producer_timing <- Some next_producer_timing in - if + ( if not (Keypair.And_compressed_pk.Set.is_empty t.config.block_production_keypairs) then + let module Context = (val context ~commit_id:t.commit_id t.config) in Block_producer.run - ~context:(context ~commit_id:t.commit_id t.config) + ~context:(module Context) ~vrf_evaluator:t.processes.vrf_evaluator ~verifier:t.processes.verifier ~set_next_producer_timing ~prover:t.processes.prover ~trust_system:t.config.trust_system @@ -1326,7 +1338,7 @@ let start t = ~block_produced_bvar:t.components.block_produced_bvar ~vrf_evaluation_state:t.vrf_evaluation_state ~net:t.components.net ~zkapp_cmd_limit_hardcap: - t.config.precomputed_values.genesis_constants.zkapp_cmd_limit_hardcap ; + t.config.precomputed_values.genesis_constants.zkapp_cmd_limit_hardcap ) ; perform_compaction t ; let () = match t.config.node_status_url with @@ -1380,9 +1392,10 @@ let start t = Snark_worker.start t let start_with_precomputed_blocks t blocks = + let module Context = (val context ~commit_id:t.commit_id t.config) in let%bind () = Block_producer.run_precomputed - ~context:(context ~commit_id:t.commit_id t.config) + ~context:(module Context) ~verifier:t.processes.verifier ~trust_system:t.config.trust_system ~time_controller:t.config.time_controller ~frontier_reader:t.components.transition_frontier @@ -1395,8 +1408,8 @@ let send_resource_pool_diff_or_wait ~rl ~diff_score ~max_per_15_seconds diff = (* HACK: Pretend we're a remote peer so that we can rate limit ourselves. *) - let us = - { Network_peer.Peer.host = Unix.Inet_addr.of_string "127.0.0.1" + let us : Network_peer.Peer.t = + { host = Unix.Inet_addr.of_string "127.0.0.1" ; libp2p_port = 0 ; peer_id = "" } @@ -1638,15 +1651,6 @@ let create ~commit_id ?wallets (config : Config.t) = Deferred.unit | Some frontier -> Transition_frontier.close ~loc:__LOC__ frontier ) ; - let handle_request name ~f query_env = - O1trace.thread ("handle_request_" ^ name) (fun () -> - let input = Envelope.Incoming.data query_env in - Deferred.return - @@ - let open Option.Let_syntax in - let%bind frontier = get_current_frontier () in - f ~frontier input ) - in (* knot-tying hacks so we can pass a get_node_status function before net, Mina_lib.t created *) let net_ref = ref None in let sync_status_ref = ref None in @@ -1765,7 +1769,7 @@ let create ~commit_id ?wallets (config : Config.t) = @@ Consensus.Data.Consensus_state .blockchain_length consensus_state ) in - Mina_networking.Rpcs.Get_node_status.Node_status. + Mina_networking.Node_status.Stable.V2. { node_ip_addr ; node_peer_id ; sync_status @@ -1779,17 +1783,6 @@ let create ~commit_id ?wallets (config : Config.t) = ; block_height_opt } ) in - let get_some_initial_peers _ = - O1trace.thread "handle_request_get_some_initial_peers" (fun () -> - match !net_ref with - | None -> - (* should be unreachable; without a network, we wouldn't receive this RPC call *) - [%log' error config.logger] - "Network not instantiated when initial peers requested" ; - Deferred.return [] - | Some net -> - Mina_networking.peers net ) - in let slot_tx_end = Runtime_config.slot_tx_end config.Config.precomputed_values.runtime_config @@ -1805,7 +1798,7 @@ let create ~commit_id ?wallets (config : Config.t) = let first_received_message_signal = Ivar.create () in let online_status, notify_online_impl = online_broadcaster - ~constraint_constants:config.net_config.constraint_constants + ~constraint_constants:Context.constraint_constants config.time_controller in let on_first_received_message ~f = @@ -1850,7 +1843,7 @@ let create ~commit_id ?wallets (config : Config.t) = config.precomputed_values.consensus_constants.slot_duration_ms ; on_push = notify_online ; log_gossip_heard = config.net_config.log_gossip_heard.new_state - ; time_controller = config.net_config.time_controller + ; time_controller = Context.time_controller ; consensus_constants ; genesis_constants = config.precomputed_values.genesis_constants ; constraint_constants @@ -1859,105 +1852,12 @@ let create ~commit_id ?wallets (config : Config.t) = let sinks = (block_sink, tx_remote_sink, snark_remote_sink) in let%bind net = O1trace.thread "mina_networking" (fun () -> - Mina_networking.create config.net_config ~get_some_initial_peers - ~sinks - ~get_staged_ledger_aux_and_pending_coinbases_at_hash:(fun query_env - -> - O1trace.thread - "handle_request_get_staged_ledger_aux_and_pending_coinbases_at_hash" - (fun () -> - let input = Envelope.Incoming.data query_env in - Deferred.return - @@ - let open Option.Let_syntax in - let%bind frontier = get_current_frontier () in - let%map ( scan_state - , expected_merkle_root - , pending_coinbases - , protocol_states ) = - Sync_handler - .get_staged_ledger_aux_and_pending_coinbases_at_hash - ~frontier input - in - let staged_ledger_hash = - Staged_ledger_hash.of_aux_ledger_and_coinbase_hash - (Staged_ledger.Scan_state.hash scan_state) - expected_merkle_root pending_coinbases - in - [%log' debug config.logger] - ~metadata: - [ ( "staged_ledger_hash" - , Staged_ledger_hash.to_yojson staged_ledger_hash - ) - ] - "sending scan state and pending coinbase" ; - ( scan_state - , expected_merkle_root - , pending_coinbases - , protocol_states ) ) ) - ~answer_sync_ledger_query:(fun query_env -> - let open Deferred.Or_error.Let_syntax in - O1trace.thread "handle_request_answer_sync_ledger_query" - (fun () -> - let ledger_hash, _ = Envelope.Incoming.data query_env in - let%bind frontier = - Deferred.return - @@ peek_frontier frontier_broadcast_pipe_r - in - Sync_handler.answer_query ~frontier ledger_hash - (Envelope.Incoming.map ~f:Tuple2.get2 query_env) - ~logger:config.logger - ~trust_system:config.trust_system - |> Deferred.map - (* begin error string prefix so we can pattern-match *) - ~f: - (Result.of_option - ~error: - (Error.createf - !"%s for ledger_hash: \ - %{sexp:Ledger_hash.t}" - Mina_networking - .refused_answer_query_string ledger_hash ) ) ) - ) - ~get_ancestry: - (handle_request "get_ancestry" ~f:(fun ~frontier s -> - s - |> With_hash.map_hash ~f:(fun state_hash -> - { State_hash.State_hashes.state_hash - ; state_body_hash = None - } ) - |> Sync_handler.Root.prove - ~context:(module Context) - ~frontier ) ) - ~get_best_tip: - (handle_request "get_best_tip" ~f:(fun ~frontier () -> - let open Option.Let_syntax in - let open Proof_carrying_data in - let%map proof_with_data = - Best_tip_prover.prove - ~context:(module Context) - frontier - in - { proof_with_data with - data = With_hash.data proof_with_data.data - } ) ) - ~get_node_status - ~get_transition_chain_proof: - (handle_request "get_transition_chain_proof" - ~f:(fun ~frontier hash -> - Transition_chain_prover.prove ~frontier hash ) ) - ~get_transition_chain: - (handle_request "get_transition_chain" - ~f:Sync_handler.get_transition_chain ) - ~get_transition_knowledge:(fun _q -> - O1trace.thread "handle_request_get_transition_knowledge" - (fun () -> - return - ( match get_current_frontier () with - | None -> - [] - | Some frontier -> - Sync_handler.best_tip_path ~frontier ) ) ) ) + Mina_networking.create + (module Context) + config.net_config ~sinks + ~get_transition_frontier:(fun () -> + Broadcast_pipe.Reader.peek frontier_broadcast_pipe_r ) + ~get_node_status ) in (* tie the first knot *) net_ref := Some net ; diff --git a/src/lib/mina_lib/mina_lib.mli b/src/lib/mina_lib/mina_lib.mli index 6b77d6a1e90..02389c2eb09 100644 --- a/src/lib/mina_lib/mina_lib.mli +++ b/src/lib/mina_lib/mina_lib.mli @@ -23,6 +23,12 @@ type Structured_log_events.t += module type CONTEXT = sig val logger : Logger.t + val time_controller : Block_time.Controller.t + + val trust_system : Trust_system.t + + val consensus_local_state : Consensus.Data.Local_state.t + val precomputed_values : Precomputed_values.t val constraint_constants : Genesis_constants.Constraint_constants.t diff --git a/src/lib/mina_networking/dune b/src/lib/mina_networking/dune index 8857e15962b..38b9efd1775 100644 --- a/src/lib/mina_networking/dune +++ b/src/lib/mina_networking/dune @@ -49,6 +49,8 @@ o1trace ppx_version.runtime bounded_types + sync_handler + transition_chain_prover ) (inline_tests (flags -verbose -show-counts)) (preprocess diff --git a/src/lib/mina_networking/mina_networking.ml b/src/lib/mina_networking/mina_networking.ml index cbb89e762b0..04dd9269d60 100644 --- a/src/lib/mina_networking/mina_networking.ml +++ b/src/lib/mina_networking/mina_networking.ml @@ -2,13 +2,10 @@ open Core open Async open Mina_base module Sync_ledger = Mina_ledger.Sync_ledger -open Mina_block open Network_peer open Network_pool open Pipe_lib -let refused_answer_query_string = "Refused to answer_query" - exception No_initial_peers type Structured_log_events.t += @@ -30,957 +27,26 @@ type Structured_log_events.t += [@@deriving register_event { msg = "Broadcasting snark pool diff over gossip net" }] -(* INSTRUCTIONS FOR ADDING A NEW RPC: - * - define a new module under the Rpcs module - * - add an entry to the Rpcs.rpc GADT definition for the new module (type ('query, 'response) rpc, below) - * - add the new constructor for Rpcs.rpc to Rpcs.all_of_type_erased_rpc - * - add a pattern matching case to Rpcs.implementation_of_rpc mapping the - * new constructor to the new module for your RPC - * - add a match case to `match_handler`, below - *) -module Rpcs = struct - (* for versioning of the types here, see - - RFC 0012, and - - https://ocaml.janestreet.com/ocaml-core/latest/doc/async_rpc_kernel/Async_rpc_kernel/Versioned_rpc/ - - The "master" types are the ones used internally in the code base. Each - version has coercions between their query and response types and the master - types. - *) - - [%%versioned_rpc - module Get_some_initial_peers = struct - module Master = struct - let name = "get_some_initial_peers" - - module T = struct - type query = unit [@@deriving sexp, yojson] - - type response = Network_peer.Peer.t list [@@deriving sexp, yojson] - end - - module Caller = T - module Callee = T - end - - include Master.T - - let sent_counter = Mina_metrics.Network.get_some_initial_peers_rpcs_sent - - let received_counter = - Mina_metrics.Network.get_some_initial_peers_rpcs_received - - let failed_request_counter = - Mina_metrics.Network.get_some_initial_peers_rpc_requests_failed - - let failed_response_counter = - Mina_metrics.Network.get_some_initial_peers_rpc_responses_failed - - module M = Versioned_rpc.Both_convert.Plain.Make (Master) - include M - - include Perf_histograms.Rpc.Plain.Extend (struct - include M - include Master - end) - - module V1 = struct - module T = struct - type query = unit - - type response = Network_peer.Peer.Stable.V1.t list - - let query_of_caller_model = Fn.id - - let callee_model_of_query = Fn.id - - let response_of_callee_model = Fn.id - - let caller_model_of_response = Fn.id - end - - module T' = - Perf_histograms.Rpc.Plain.Decorate_bin_io - (struct - include M - include Master - end) - (T) - - include T' - include Register (T') - end - end] - - [%%versioned_rpc - module Get_staged_ledger_aux_and_pending_coinbases_at_hash = struct - module Master = struct - let name = "get_staged_ledger_aux_and_pending_coinbases_at_hash" - - module T = struct - type query = State_hash.t - - type response = - ( Staged_ledger.Scan_state.t - * Ledger_hash.t - * Pending_coinbase.t - * Mina_state.Protocol_state.value list ) - option - end - - module Caller = T - module Callee = T - end - - include Master.T - - let sent_counter = - Mina_metrics.Network - .get_staged_ledger_aux_and_pending_coinbases_at_hash_rpcs_sent - - let received_counter = - Mina_metrics.Network - .get_staged_ledger_aux_and_pending_coinbases_at_hash_rpcs_received - - let failed_request_counter = - Mina_metrics.Network - .get_staged_ledger_aux_and_pending_coinbases_at_hash_rpc_requests_failed - - let failed_response_counter = - Mina_metrics.Network - .get_staged_ledger_aux_and_pending_coinbases_at_hash_rpc_responses_failed - - module M = Versioned_rpc.Both_convert.Plain.Make (Master) - include M - - include Perf_histograms.Rpc.Plain.Extend (struct - include M - include Master - end) - - module V2 = struct - module T = struct - type query = State_hash.Stable.V1.t - - type response = - ( Staged_ledger.Scan_state.Stable.V2.t - * Ledger_hash.Stable.V1.t - * Pending_coinbase.Stable.V2.t - * Mina_state.Protocol_state.Value.Stable.V2.t list ) - option - - let query_of_caller_model = Fn.id - - let callee_model_of_query = Fn.id - - let response_of_callee_model = Fn.id - - let caller_model_of_response = Fn.id - end - - module T' = - Perf_histograms.Rpc.Plain.Decorate_bin_io - (struct - include M - include Master - end) - (T) - - include T' - include Register (T') - end - end] - - [%%versioned_rpc - module Answer_sync_ledger_query = struct - module Master = struct - let name = "answer_sync_ledger_query" - - module T = struct - type query = Ledger_hash.t * Sync_ledger.Query.t - - type response = - (( Sync_ledger.Answer.t - , Bounded_types.Wrapped_error.Stable.V1.t ) - Result.t - [@version_asserted] ) - end - - module Caller = T - module Callee = T - end - - include Master.T - - let sent_counter = Mina_metrics.Network.answer_sync_ledger_query_rpcs_sent - - let received_counter = - Mina_metrics.Network.answer_sync_ledger_query_rpcs_received - - let failed_request_counter = - Mina_metrics.Network.answer_sync_ledger_query_rpc_requests_failed - - let failed_response_counter = - Mina_metrics.Network.answer_sync_ledger_query_rpc_responses_failed - - module M = Versioned_rpc.Both_convert.Plain.Make (Master) - include M - - include Perf_histograms.Rpc.Plain.Extend (struct - include M - include Master - end) - - module V3 = struct - module T = struct - type query = Ledger_hash.Stable.V1.t * Sync_ledger.Query.Stable.V1.t - [@@deriving sexp] - - type response = - (( Sync_ledger.Answer.Stable.V2.t - , Bounded_types.Wrapped_error.Stable.V1.t ) - Result.t - [@version_asserted] ) - [@@deriving sexp] - - let query_of_caller_model = Fn.id - - let callee_model_of_query = Fn.id - - let response_of_callee_model = Fn.id - - let caller_model_of_response = Fn.id - end - - module T' = - Perf_histograms.Rpc.Plain.Decorate_bin_io - (struct - include M - include Master - end) - (T) - - include T' - include Register (T') - end - end] - - [%%versioned_rpc - module Get_transition_chain = struct - module Master = struct - let name = "get_transition_chain" - - module T = struct - type query = State_hash.t list [@@deriving sexp, to_yojson] - - type response = Mina_block.t list option - end - - module Caller = T - module Callee = T - end - - include Master.T - - let sent_counter = Mina_metrics.Network.get_transition_chain_rpcs_sent - - let received_counter = - Mina_metrics.Network.get_transition_chain_rpcs_received - - let failed_request_counter = - Mina_metrics.Network.get_transition_chain_rpc_requests_failed - - let failed_response_counter = - Mina_metrics.Network.get_transition_chain_rpc_responses_failed - - module M = Versioned_rpc.Both_convert.Plain.Make (Master) - include M - - include Perf_histograms.Rpc.Plain.Extend (struct - include M - include Master - end) - - module V2 = struct - module T = struct - type query = State_hash.Stable.V1.t list [@@deriving sexp] - - type response = Mina_block.Stable.V2.t list option - - let query_of_caller_model = Fn.id - - let callee_model_of_query = Fn.id - - let response_of_callee_model = ident - - let caller_model_of_response = ident - end - - module T' = - Perf_histograms.Rpc.Plain.Decorate_bin_io - (struct - include M - include Master - end) - (T) - - include T' - include Register (T') - end - end] - - [%%versioned_rpc - module Get_transition_chain_proof = struct - module Master = struct - let name = "get_transition_chain_proof" - - module T = struct - type query = State_hash.t [@@deriving sexp, to_yojson] - - type response = (State_hash.t * State_body_hash.t list) option - end - - module Caller = T - module Callee = T - end - - include Master.T - - let sent_counter = Mina_metrics.Network.get_transition_chain_proof_rpcs_sent - - let received_counter = - Mina_metrics.Network.get_transition_chain_proof_rpcs_received - - let failed_request_counter = - Mina_metrics.Network.get_transition_chain_proof_rpc_requests_failed - - let failed_response_counter = - Mina_metrics.Network.get_transition_chain_proof_rpc_responses_failed - - module M = Versioned_rpc.Both_convert.Plain.Make (Master) - include M - - include Perf_histograms.Rpc.Plain.Extend (struct - include M - include Master - end) - - module V1 = struct - module T = struct - type query = State_hash.Stable.V1.t [@@deriving sexp] - - type response = - (State_hash.Stable.V1.t * State_body_hash.Stable.V1.t list) option - - let query_of_caller_model = Fn.id - - let callee_model_of_query = Fn.id - - let response_of_callee_model = Fn.id - - let caller_model_of_response = Fn.id - end - - module T' = - Perf_histograms.Rpc.Plain.Decorate_bin_io - (struct - include M - include Master - end) - (T) - - include T' - include Register (T') - end - end] - - [%%versioned_rpc - module Get_transition_knowledge = struct - module Master = struct - let name = "Get_transition_knowledge" - - module T = struct - type query = unit [@@deriving sexp, to_yojson] - - type response = State_hash.t list - end - - module Caller = T - module Callee = T - end - - include Master.T - - let sent_counter = Mina_metrics.Network.get_transition_knowledge_rpcs_sent - - let received_counter = - Mina_metrics.Network.get_transition_knowledge_rpcs_received - - let failed_request_counter = - Mina_metrics.Network.get_transition_knowledge_rpc_requests_failed - - let failed_response_counter = - Mina_metrics.Network.get_transition_knowledge_rpc_responses_failed - - module M = Versioned_rpc.Both_convert.Plain.Make (Master) - include M - - include Perf_histograms.Rpc.Plain.Extend (struct - include M - include Master - end) - - module V1 = struct - module T = struct - type query = unit [@@deriving sexp] - - type response = State_hash.Stable.V1.t list - - let query_of_caller_model = Fn.id - - let callee_model_of_query = Fn.id - - let response_of_callee_model = Fn.id - - let caller_model_of_response = Fn.id - end - - module T' = - Perf_histograms.Rpc.Plain.Decorate_bin_io - (struct - include M - include Master - end) - (T) - - include T' - include Register (T') - end - end] - - [%%versioned_rpc - module Get_ancestry = struct - module Master = struct - let name = "get_ancestry" - - module T = struct - (** NB: The state hash sent in this query should not be trusted, as it can be forged. This is ok for how this RPC is implented, as we only use the state hash for tie breaking when checking whether or not the proof is worth serving. *) - type query = - (Consensus.Data.Consensus_state.Value.t, State_hash.t) With_hash.t - [@@deriving sexp, to_yojson] - - type response = - ( Mina_block.t - , State_body_hash.t list * Mina_block.t ) - Proof_carrying_data.t - option - end - - module Caller = T - module Callee = T - end - - include Master.T - - let sent_counter = Mina_metrics.Network.get_ancestry_rpcs_sent - - let received_counter = Mina_metrics.Network.get_ancestry_rpcs_received +module type CONTEXT = sig + val logger : Logger.t - let failed_request_counter = - Mina_metrics.Network.get_ancestry_rpc_requests_failed + val trust_system : Trust_system.t - let failed_response_counter = - Mina_metrics.Network.get_ancestry_rpc_responses_failed + val time_controller : Block_time.Controller.t - module M = Versioned_rpc.Both_convert.Plain.Make (Master) - include M + val consensus_local_state : Consensus.Data.Local_state.t - include Perf_histograms.Rpc.Plain.Extend (struct - include M - include Master - end) + val precomputed_values : Precomputed_values.t - module V2 = struct - module T = struct - type query = - ( Consensus.Data.Consensus_state.Value.Stable.V2.t - , State_hash.Stable.V1.t ) - With_hash.Stable.V1.t - [@@deriving sexp] + val constraint_constants : Genesis_constants.Constraint_constants.t - type response = - ( Mina_block.Stable.V2.t - , State_body_hash.Stable.V1.t list * Mina_block.Stable.V2.t ) - Proof_carrying_data.Stable.V1.t - option - - let query_of_caller_model = Fn.id - - let callee_model_of_query = Fn.id - - let response_of_callee_model = ident - - let caller_model_of_response = ident - end - - module T' = - Perf_histograms.Rpc.Plain.Decorate_bin_io - (struct - include M - include Master - end) - (T) - - include T' - include Register (T') - end - end] - - [%%versioned_rpc - module Ban_notify = struct - module Master = struct - let name = "ban_notify" - - module T = struct - (* banned until this time *) - type query = Core.Time.t [@@deriving sexp] - - type response = unit - end - - module Caller = T - module Callee = T - end - - include Master.T - - let sent_counter = Mina_metrics.Network.ban_notify_rpcs_sent - - let received_counter = Mina_metrics.Network.ban_notify_rpcs_received - - let failed_request_counter = - Mina_metrics.Network.ban_notify_rpc_requests_failed - - let failed_response_counter = - Mina_metrics.Network.ban_notify_rpc_responses_failed - - module M = Versioned_rpc.Both_convert.Plain.Make (Master) - include M - - include Perf_histograms.Rpc.Plain.Extend (struct - include M - include Master - end) - - module V1 = struct - module T = struct - type query = Core.Time.Stable.V1.t [@@deriving sexp] - - type response = unit - - let query_of_caller_model = Fn.id - - let callee_model_of_query = Fn.id - - let response_of_callee_model = Fn.id - - let caller_model_of_response = Fn.id - end - - module T' = - Perf_histograms.Rpc.Plain.Decorate_bin_io - (struct - include M - include Master - end) - (T) - - include T' - include Register (T') - end - end] - - [%%versioned_rpc - module Get_best_tip = struct - module Master = struct - let name = "get_best_tip" - - module T = struct - type query = unit [@@deriving sexp, to_yojson] - - type response = - ( Mina_block.t - , State_body_hash.t list * Mina_block.t ) - Proof_carrying_data.t - option - end - - module Caller = T - module Callee = T - end - - include Master.T - - let sent_counter = Mina_metrics.Network.get_best_tip_rpcs_sent - - let received_counter = Mina_metrics.Network.get_best_tip_rpcs_received - - let failed_request_counter = - Mina_metrics.Network.get_best_tip_rpc_requests_failed - - let failed_response_counter = - Mina_metrics.Network.get_best_tip_rpc_responses_failed - - module M = Versioned_rpc.Both_convert.Plain.Make (Master) - include M - - include Perf_histograms.Rpc.Plain.Extend (struct - include M - include Master - end) - - module V2 = struct - module T = struct - type query = unit [@@deriving sexp] - - type response = - ( Mina_block.Stable.V2.t - , State_body_hash.Stable.V1.t list * Mina_block.Stable.V2.t ) - Proof_carrying_data.Stable.V1.t - option - - let query_of_caller_model = Fn.id - - let callee_model_of_query = Fn.id - - let response_of_callee_model = ident - - let caller_model_of_response = ident - end - - module T' = - Perf_histograms.Rpc.Plain.Decorate_bin_io - (struct - include M - include Master - end) - (T) - - include T' - include Register (T') - end - end] - - [%%versioned_rpc - module Get_node_status = struct - module Node_status = struct - [%%versioned - module Stable = struct - module V2 = struct - type t = - { node_ip_addr : Network_peer.Peer.Inet_addr.Stable.V1.t - ; node_peer_id : Network_peer.Peer.Id.Stable.V1.t - [@to_yojson fun peer_id -> `String peer_id] - [@of_yojson - function `String s -> Ok s | _ -> Error "expected string"] - ; sync_status : Sync_status.Stable.V1.t - ; peers : Network_peer.Peer.Stable.V1.t list - ; block_producers : - Signature_lib.Public_key.Compressed.Stable.V1.t list - ; protocol_state_hash : State_hash.Stable.V1.t - ; ban_statuses : - ( Network_peer.Peer.Stable.V1.t - * Trust_system.Peer_status.Stable.V1.t ) - list - ; k_block_hashes_and_timestamps : - (State_hash.Stable.V1.t * Bounded_types.String.Stable.V1.t) list - ; git_commit : Bounded_types.String.Stable.V1.t - ; uptime_minutes : int - ; block_height_opt : int option [@default None] - } - [@@deriving to_yojson, of_yojson] - - let to_latest = Fn.id - end - - module V1 = struct - type t = - { node_ip_addr : Network_peer.Peer.Inet_addr.Stable.V1.t - ; node_peer_id : Network_peer.Peer.Id.Stable.V1.t - [@to_yojson fun peer_id -> `String peer_id] - [@of_yojson - function `String s -> Ok s | _ -> Error "expected string"] - ; sync_status : Sync_status.Stable.V1.t - ; peers : Network_peer.Peer.Stable.V1.t list - ; block_producers : - Signature_lib.Public_key.Compressed.Stable.V1.t list - ; protocol_state_hash : State_hash.Stable.V1.t - ; ban_statuses : - ( Network_peer.Peer.Stable.V1.t - * Trust_system.Peer_status.Stable.V1.t ) - list - ; k_block_hashes_and_timestamps : - (State_hash.Stable.V1.t * Bounded_types.String.Stable.V1.t) list - ; git_commit : Bounded_types.String.Stable.V1.t - ; uptime_minutes : int - } - [@@deriving to_yojson, of_yojson] - - let to_latest status : Latest.t = - { node_ip_addr = status.node_ip_addr - ; node_peer_id = status.node_peer_id - ; sync_status = status.sync_status - ; peers = status.peers - ; block_producers = status.block_producers - ; protocol_state_hash = status.protocol_state_hash - ; ban_statuses = status.ban_statuses - ; k_block_hashes_and_timestamps = - status.k_block_hashes_and_timestamps - ; git_commit = status.git_commit - ; uptime_minutes = status.uptime_minutes - ; block_height_opt = None - } - end - end] - end - - module Master = struct - let name = "get_node_status" - - module T = struct - type query = unit [@@deriving sexp, to_yojson] - - type response = - (Node_status.t, Bounded_types.Wrapped_error.Stable.V1.t) result - end - - module Caller = T - module Callee = T - end - - include Master.T - - let sent_counter = Mina_metrics.Network.get_node_status_rpcs_sent - - let received_counter = Mina_metrics.Network.get_node_status_rpcs_received - - let failed_request_counter = - Mina_metrics.Network.get_node_status_rpc_requests_failed - - let failed_response_counter = - Mina_metrics.Network.get_node_status_rpc_responses_failed - - module M = Versioned_rpc.Both_convert.Plain.Make (Master) - include M - - let response_to_yojson response = - match response with - | Ok status -> - Node_status.Stable.Latest.to_yojson status - | Error err -> - `Assoc [ ("error", Error_json.error_to_yojson err) ] - - include Perf_histograms.Rpc.Plain.Extend (struct - include M - include Master - end) - - module V2 = struct - module T = struct - type query = unit [@@deriving sexp] - - type response = - (( Node_status.Stable.V2.t - , Bounded_types.Wrapped_error.Stable.V1.t ) - Result.t - [@version_asserted] ) - - let query_of_caller_model = Fn.id - - let callee_model_of_query = Fn.id - - let response_of_callee_model = Fn.id - - let caller_model_of_response = Fn.id - end - - module T' = - Perf_histograms.Rpc.Plain.Decorate_bin_io - (struct - include M - include Master - end) - (T) - - include T' - include Register (T') - end - - module V1 = struct - module T = struct - type query = unit [@@deriving sexp] - - type response = - (( Node_status.Stable.V1.t - , Bounded_types.Wrapped_error.Stable.V1.t ) - Core_kernel.Result.t - [@version_asserted] ) - - let query_of_caller_model = Fn.id - - let callee_model_of_query = Fn.id - - let response_of_callee_model = function - | Error err -> - Error err - | Ok (status : Node_status.Stable.Latest.t) -> - Ok - { Node_status.Stable.V1.node_ip_addr = status.node_ip_addr - ; node_peer_id = status.node_peer_id - ; sync_status = status.sync_status - ; peers = status.peers - ; block_producers = status.block_producers - ; protocol_state_hash = status.protocol_state_hash - ; ban_statuses = status.ban_statuses - ; k_block_hashes_and_timestamps = - status.k_block_hashes_and_timestamps - ; git_commit = status.git_commit - ; uptime_minutes = status.uptime_minutes - } - - let caller_model_of_response = function - | Error err -> - Error err - | Ok (status : Node_status.Stable.V1.t) -> - Ok (Node_status.Stable.V1.to_latest status) - end - - module T' = - Perf_histograms.Rpc.Plain.Decorate_bin_io - (struct - include M - include Master - end) - (T) - - include T' - include Register (T') - end - end] - - type ('query, 'response) rpc = - | Get_some_initial_peers - : (Get_some_initial_peers.query, Get_some_initial_peers.response) rpc - | Get_staged_ledger_aux_and_pending_coinbases_at_hash - : ( Get_staged_ledger_aux_and_pending_coinbases_at_hash.query - , Get_staged_ledger_aux_and_pending_coinbases_at_hash.response ) - rpc - | Answer_sync_ledger_query - : ( Answer_sync_ledger_query.query - , Answer_sync_ledger_query.response ) - rpc - | Get_transition_chain - : (Get_transition_chain.query, Get_transition_chain.response) rpc - | Get_transition_knowledge - : ( Get_transition_knowledge.query - , Get_transition_knowledge.response ) - rpc - | Get_transition_chain_proof - : ( Get_transition_chain_proof.query - , Get_transition_chain_proof.response ) - rpc - | Get_node_status : (Get_node_status.query, Get_node_status.response) rpc - | Get_ancestry : (Get_ancestry.query, Get_ancestry.response) rpc - | Ban_notify : (Ban_notify.query, Ban_notify.response) rpc - | Get_best_tip : (Get_best_tip.query, Get_best_tip.response) rpc - - type rpc_handler = - | Rpc_handler : - { rpc : ('q, 'r) rpc - ; f : ('q, 'r) Rpc_intf.rpc_fn - ; cost : 'q -> int - ; budget : int * [ `Per of Time.Span.t ] - } - -> rpc_handler - - let implementation_of_rpc : - type q r. (q, r) rpc -> (q, r) Rpc_intf.rpc_implementation = function - | Get_some_initial_peers -> - (module Get_some_initial_peers) - | Get_staged_ledger_aux_and_pending_coinbases_at_hash -> - (module Get_staged_ledger_aux_and_pending_coinbases_at_hash) - | Answer_sync_ledger_query -> - (module Answer_sync_ledger_query) - | Get_transition_chain -> - (module Get_transition_chain) - | Get_transition_knowledge -> - (module Get_transition_knowledge) - | Get_transition_chain_proof -> - (module Get_transition_chain_proof) - | Get_node_status -> - (module Get_node_status) - | Get_ancestry -> - (module Get_ancestry) - | Ban_notify -> - (module Ban_notify) - | Get_best_tip -> - (module Get_best_tip) - - let match_handler : - type q r. - rpc_handler - -> (q, r) rpc - -> do_:((q, r) Rpc_intf.rpc_fn -> 'a) - -> 'a option = - fun (Rpc_handler { rpc = impl_rpc; f; cost = _; budget = _ }) rpc ~do_ -> - match (rpc, impl_rpc) with - | Get_some_initial_peers, Get_some_initial_peers -> - Some (do_ f) - | Get_some_initial_peers, _ -> - None - | ( Get_staged_ledger_aux_and_pending_coinbases_at_hash - , Get_staged_ledger_aux_and_pending_coinbases_at_hash ) -> - Some (do_ f) - | Get_staged_ledger_aux_and_pending_coinbases_at_hash, _ -> - None - | Answer_sync_ledger_query, Answer_sync_ledger_query -> - Some (do_ f) - | Answer_sync_ledger_query, _ -> - None - | Get_transition_chain, Get_transition_chain -> - Some (do_ f) - | Get_transition_chain, _ -> - None - | Get_transition_knowledge, Get_transition_knowledge -> - Some (do_ f) - | Get_transition_knowledge, _ -> - None - | Get_transition_chain_proof, Get_transition_chain_proof -> - Some (do_ f) - | Get_transition_chain_proof, _ -> - None - | Get_node_status, Get_node_status -> - Some (do_ f) - | Get_node_status, _ -> - None - | Get_ancestry, Get_ancestry -> - Some (do_ f) - | Get_ancestry, _ -> - None - | Ban_notify, Ban_notify -> - Some (do_ f) - | Ban_notify, _ -> - None - | Get_best_tip, Get_best_tip -> - Some (do_ f) - | Get_best_tip, _ -> - None + val consensus_constants : Consensus.Constants.t end module Sinks = Sinks +module Rpcs = Rpcs module Gossip_net = Gossip_net.Make (Rpcs) +module Node_status = Rpcs.Get_node_status.Node_status module Config = struct type log_gossip_heard = @@ -988,14 +54,7 @@ module Config = struct [@@deriving make] type t = - { logger : Logger.t - ; trust_system : Trust_system.t - ; time_controller : Block_time.Controller.t - ; consensus_constants : Consensus.Constants.t - ; consensus_local_state : Consensus.Data.Local_state.t - ; genesis_ledger_hash : Ledger_hash.t - ; constraint_constants : Genesis_constants.Constraint_constants.t - ; precomputed_values : Precomputed_values.t + { genesis_ledger_hash : Ledger_hash.t ; creatable_gossip_net : Gossip_net.Any.creatable ; is_seed : bool ; log_gossip_heard : log_gossip_heard @@ -1010,383 +69,34 @@ type t = } [@@deriving fields] -let wrap_rpc_data_in_envelope conn data = - Envelope.Incoming.wrap_peer ~data ~sender:conn - -type protocol_version_status = - { valid_current : bool; valid_next : bool; matches_daemon : bool } - -let protocol_version_status t = - let header = Mina_block.header t in - let valid_current = - Protocol_version.is_valid (Header.current_protocol_version header) - in - let valid_next = - Option.for_all - (Header.proposed_protocol_version_opt header) - ~f:Protocol_version.is_valid - in - let matches_daemon = - Protocol_version.compatible_with_daemon - (Header.current_protocol_version header) - in - { valid_current; valid_next; matches_daemon } - -let create (config : Config.t) ~sinks - ~(get_some_initial_peers : - Rpcs.Get_some_initial_peers.query Envelope.Incoming.t - -> Rpcs.Get_some_initial_peers.response Deferred.t ) - ~(get_staged_ledger_aux_and_pending_coinbases_at_hash : - Rpcs.Get_staged_ledger_aux_and_pending_coinbases_at_hash.query - Envelope.Incoming.t - -> Rpcs.Get_staged_ledger_aux_and_pending_coinbases_at_hash.response - Deferred.t ) - ~(answer_sync_ledger_query : - Rpcs.Answer_sync_ledger_query.query Envelope.Incoming.t - -> Rpcs.Answer_sync_ledger_query.response Deferred.t ) - ~(get_ancestry : - Rpcs.Get_ancestry.query Envelope.Incoming.t - -> Rpcs.Get_ancestry.response Deferred.t ) - ~(get_best_tip : - Rpcs.Get_best_tip.query Envelope.Incoming.t - -> Rpcs.Get_best_tip.response Deferred.t ) +let create (module Context : CONTEXT) (config : Config.t) ~sinks + ~(get_transition_frontier : unit -> Transition_frontier.t option) ~(get_node_status : Rpcs.Get_node_status.query Envelope.Incoming.t - -> Rpcs.Get_node_status.response Deferred.t ) - ~(get_transition_chain_proof : - Rpcs.Get_transition_chain_proof.query Envelope.Incoming.t - -> Rpcs.Get_transition_chain_proof.response Deferred.t ) - ~(get_transition_chain : - Rpcs.Get_transition_chain.query Envelope.Incoming.t - -> Rpcs.Get_transition_chain.response Deferred.t ) - ~(get_transition_knowledge : - Rpcs.Get_transition_knowledge.query Envelope.Incoming.t - -> Rpcs.Get_transition_knowledge.response Deferred.t ) = - let module Context = struct - let logger = config.logger - end in + -> Rpcs.Get_node_status.response Deferred.t ) = let open Context in - let run_for_rpc_result conn data ~f action_msg msg_args = - let data_in_envelope = wrap_rpc_data_in_envelope conn data in - let sender = Envelope.Incoming.sender data_in_envelope in - let%bind () = - Trust_system.( - record_envelope_sender config.trust_system config.logger sender - Actions.(Made_request, Some (action_msg, msg_args))) - in - let%bind result = f data_in_envelope in - return (result, sender) - in - let incr_failed_response = Mina_metrics.Counter.inc_one in - let record_unknown_item result sender action_msg msg_args - failed_response_counter = - let%map () = - if Option.is_none result then ( - incr_failed_response failed_response_counter ; - Trust_system.( - record_envelope_sender config.trust_system config.logger sender - Actions.(Requested_unknown_item, Some (action_msg, msg_args))) ) - else return () - in - result - in - let validate_protocol_versions ~rpc_name sender external_transition = - let open Trust_system.Actions in - let { valid_current; valid_next; matches_daemon } = - protocol_version_status external_transition - in - let%bind () = - if valid_current then return () - else - let actions = - ( Sent_invalid_protocol_version - , Some - ( "$rpc_name: external transition with invalid current protocol \ - version" - , [ ("rpc_name", `String rpc_name) - ; ( "current_protocol_version" - , `String - (Protocol_version.to_string - (Header.current_protocol_version - (Mina_block.header external_transition) ) ) ) - ] ) ) - in - Trust_system.record_envelope_sender config.trust_system config.logger - sender actions - in - let%bind () = - if valid_next then return () - else - let actions = - ( Sent_invalid_protocol_version - , Some - ( "$rpc_name: external transition with invalid proposed protocol \ - version" - , [ ("rpc_name", `String rpc_name) - ; ( "proposed_protocol_version" - , `String - (Protocol_version.to_string - (Option.value_exn - (Header.proposed_protocol_version_opt - (Mina_block.header external_transition) ) ) ) ) - ] ) ) - in - Trust_system.record_envelope_sender config.trust_system config.logger - sender actions - in - let%map () = - if matches_daemon then return () - else - let actions = - ( Sent_mismatched_protocol_version - , Some - ( "$rpc_name: current protocol version in external transition \ - does not match daemon current protocol version" - , [ ("rpc_name", `String rpc_name) - ; ( "current_protocol_version" - , `String - (Protocol_version.to_string - (Header.current_protocol_version - (Mina_block.header external_transition) ) ) ) - ; ( "daemon_current_protocol_version" - , `String Protocol_version.(to_string current) ) - ] ) ) - in - Trust_system.record_envelope_sender config.trust_system config.logger - sender actions - in - valid_current && valid_next && matches_daemon - in - (* each of the passed-in procedures expects an enveloped input, so - we wrap the data received via RPC *) - let get_staged_ledger_aux_and_pending_coinbases_at_hash_rpc conn ~version:_ - hash = - let action_msg = "Staged ledger and pending coinbases at hash: $hash" in - let msg_args = [ ("hash", State_hash.to_yojson hash) ] in - let%bind result, sender = - run_for_rpc_result conn hash - ~f:get_staged_ledger_aux_and_pending_coinbases_at_hash action_msg - msg_args - in - record_unknown_item result sender action_msg msg_args - Rpcs.Get_staged_ledger_aux_and_pending_coinbases_at_hash - .failed_response_counter - in - let answer_sync_ledger_query_rpc conn ~version:_ ((hash, query) as sync_query) - = - let%bind result, sender = - run_for_rpc_result conn sync_query ~f:answer_sync_ledger_query - "Answer_sync_ledger_query: $query" - [ ("query", Sync_ledger.Query.to_yojson query) ] - in - let%bind () = - match result with - | Ok _ -> - return () - | Error err -> - (* N.B.: to_string_mach double-quotes the string, don't want that *) - incr_failed_response - Rpcs.Answer_sync_ledger_query.failed_response_counter ; - let err_msg = Error.to_string_hum err in - if String.is_prefix err_msg ~prefix:refused_answer_query_string then - Trust_system.( - record_envelope_sender config.trust_system config.logger sender - Actions. - ( Requested_unknown_item - , Some - ( "Sync ledger query with hash: $hash, query: $query, \ - with error: $error" - , [ ("hash", Ledger_hash.to_yojson hash) - ; ( "query" - , Syncable_ledger.Query.to_yojson - Mina_ledger.Ledger.Addr.to_yojson query ) - ; ("error", Error_json.error_to_yojson err) - ] ) )) - else return () - in - return result - in - let md p = [ ("peer", Peer.to_yojson p) ] in - let get_ancestry_rpc conn ~version:_ query = - [%log debug] "Sending root proof to $peer" ~metadata:(md conn) ; - let action_msg = "Get_ancestry query: $query" in - let msg_args = [ ("query", Rpcs.Get_ancestry.query_to_yojson query) ] in - let%bind result, sender = - run_for_rpc_result conn query ~f:get_ancestry action_msg msg_args - in - match result with - | None -> - record_unknown_item result sender action_msg msg_args - Rpcs.Get_ancestry.failed_response_counter - | Some { proof = _, ext_trans; _ } -> - let%map valid_protocol_versions = - validate_protocol_versions ~rpc_name:"Get_ancestry" sender ext_trans - in - if valid_protocol_versions then result else None - in - let get_some_initial_peers_rpc (conn : Peer.t) ~version:_ () = - [%log trace] "Sending some initial peers to $peer" ~metadata:(md conn) ; - let action_msg = "Get_some_initial_peers query: $query" in - let msg_args = [ ("query", `Assoc []) ] in - let%map result, _sender = - run_for_rpc_result conn () ~f:get_some_initial_peers action_msg msg_args - in - if List.is_empty result then - incr_failed_response Rpcs.Get_some_initial_peers.failed_response_counter ; - result - in - let get_best_tip_rpc conn ~version:_ () = - [%log debug] "Sending best_tip to $peer" ~metadata:(md conn) ; - let action_msg = "Get_best_tip. query: $query" in - let msg_args = [ ("query", Rpcs.Get_best_tip.query_to_yojson ()) ] in - let%bind result, sender = - run_for_rpc_result conn () ~f:get_best_tip action_msg msg_args - in - match result with - | None -> - record_unknown_item result sender action_msg msg_args - Rpcs.Get_best_tip.failed_response_counter - | Some { data = data_ext_trans; proof = _, proof_ext_trans } -> - let%bind valid_data_protocol_versions = - validate_protocol_versions ~rpc_name:"Get_best_tip (data)" sender - data_ext_trans - in - let%map valid_proof_protocol_versions = - validate_protocol_versions ~rpc_name:"Get_best_tip (proof)" sender - proof_ext_trans - in - if valid_data_protocol_versions && valid_proof_protocol_versions then - result - else None - in - let get_transition_chain_proof_rpc conn ~version:_ query = - [%log info] "Sending transition_chain_proof to $peer" ~metadata:(md conn) ; - let action_msg = "Get_transition_chain_proof query: $query" in - let msg_args = - [ ("query", Rpcs.Get_transition_chain_proof.query_to_yojson query) ] - in - let%bind result, sender = - run_for_rpc_result conn query ~f:get_transition_chain_proof action_msg - msg_args - in - record_unknown_item result sender action_msg msg_args - Rpcs.Get_transition_chain_proof.failed_response_counter - in - let get_transition_knowledge_rpc conn ~version:_ query = - [%log info] "Sending transition_knowledge to $peer" ~metadata:(md conn) ; - let action_msg = "Get_transition_knowledge query: $query" in - let msg_args = - [ ("query", Rpcs.Get_transition_knowledge.query_to_yojson query) ] - in - let%map result = - run_for_rpc_result conn query ~f:get_transition_knowledge action_msg - msg_args - >>| fst - in - if List.is_empty result then - incr_failed_response Rpcs.Get_transition_knowledge.failed_response_counter ; - result - in - let get_transition_chain_rpc conn ~version:_ query = - [%log info] "Sending transition_chain to $peer" ~metadata:(md conn) ; - let action_msg = "Get_transition_chain query: $query" in - let msg_args = - [ ("query", Rpcs.Get_transition_chain.query_to_yojson query) ] - in - let%bind result, sender = - run_for_rpc_result conn query ~f:get_transition_chain action_msg msg_args - in - match result with - | None -> - record_unknown_item result sender action_msg msg_args - Rpcs.Get_transition_chain.failed_response_counter - | Some ext_trans -> - let%map valid_protocol_versions = - Deferred.List.map ext_trans - ~f: - (validate_protocol_versions ~rpc_name:"Get_transition_chain" - sender ) - in - if List.for_all valid_protocol_versions ~f:(Bool.equal true) then result - else None - in - let ban_notify_rpc conn ~version:_ ban_until = - (* the port in `conn' is an ephemeral port, not of interest *) - [%log warn] "Node banned by peer $peer until $ban_until" - ~metadata: - [ ("peer", Peer.to_yojson conn) - ; ( "ban_until" - , `String (Time.to_string_abs ~zone:Time.Zone.utc ban_until) ) - ] ; - (* no computation to do; we're just getting notification *) - Deferred.unit - in - let rpc_handlers = - let open Rpcs in - let open Time.Span in - let unit _ = 1 in - [ Rpc_handler - { rpc = Get_some_initial_peers - ; f = get_some_initial_peers_rpc - ; budget = (1, `Per minute) - ; cost = unit - } - ; Rpc_handler - { rpc = Get_staged_ledger_aux_and_pending_coinbases_at_hash - ; f = get_staged_ledger_aux_and_pending_coinbases_at_hash_rpc - ; budget = (4, `Per minute) - ; cost = unit - } - ; Rpc_handler - { rpc = Answer_sync_ledger_query - ; f = answer_sync_ledger_query_rpc - ; budget = - (Int.pow 2 17, `Per minute) (* Not that confident about this one. *) - ; cost = unit - } - ; Rpc_handler - { rpc = Get_best_tip - ; f = get_best_tip_rpc - ; budget = (3, `Per minute) - ; cost = unit - } - ; Rpc_handler - { rpc = Get_ancestry - ; f = get_ancestry_rpc - ; budget = (5, `Per minute) - ; cost = unit - } - ; Rpc_handler - { rpc = Get_transition_knowledge - ; f = get_transition_knowledge_rpc - ; budget = (1, `Per minute) - ; cost = unit - } - ; Rpc_handler - { rpc = Get_transition_chain - ; f = get_transition_chain_rpc - ; budget = (1, `Per second) (* Not that confident about this one. *) - ; cost = (fun x -> Int.max 1 (List.length x)) - } - ; Rpc_handler - { rpc = Get_transition_chain_proof - ; f = get_transition_chain_proof_rpc - ; budget = (3, `Per minute) - ; cost = unit - } - ; Rpc_handler - { rpc = Ban_notify - ; f = ban_notify_rpc - ; budget = (1, `Per minute) - ; cost = unit - } - ] - in + let gossip_net_ref = ref None in + let module Rpc_context = struct + include Context + + let list_peers () = + match !gossip_net_ref with + | None -> + (* should be unreachable; without a network, we wouldn't receive this RPC call *) + [%log error] "Network not instantiated when initial peers requested" ; + return [] + | Some gossip_net -> + Gossip_net.Any.peers gossip_net + + let get_transition_frontier = get_transition_frontier + end in let%map gossip_net = O1trace.thread "gossip_net" (fun () -> - Gossip_net.Any.create config.creatable_gossip_net rpc_handlers + Gossip_net.Any.create config.creatable_gossip_net + (module Rpc_context) (Gossip_net.Message.Any_sinks ((module Sinks), sinks)) ) in + gossip_net_ref := Some gossip_net ; (* The node status RPC is implemented directly in go, serving a string which is periodically updated. This is so that one can make this RPC on a node even if that node is at its connection limit. *) @@ -1419,7 +129,7 @@ let create (config : Config.t) ~sinks For example, some things you really want to not drop (like your outgoing block announcment). *) - { gossip_net; logger = config.logger; trust_system = config.trust_system } + { gossip_net; logger; trust_system } (* lift and expose select gossip net functions *) include struct diff --git a/src/lib/mina_networking/mina_networking.mli b/src/lib/mina_networking/mina_networking.mli index 11b8794420b..50fc7040188 100644 --- a/src/lib/mina_networking/mina_networking.mli +++ b/src/lib/mina_networking/mina_networking.mli @@ -1,6 +1,7 @@ open Async open Core open Mina_base +open Mina_ledger open Network_pool open Pipe_lib open Network_peer @@ -14,9 +15,79 @@ type Structured_log_events.t += | Gossip_snark_pool_diff of { work : Snark_pool.Resource_pool.Diff.compact } [@@deriving register_event] -val refused_answer_query_string : string +module type CONTEXT = sig + val logger : Logger.t + + val trust_system : Trust_system.t + + val time_controller : Block_time.Controller.t + + val consensus_local_state : Consensus.Data.Local_state.t + + val precomputed_values : Precomputed_values.t + + val constraint_constants : Genesis_constants.Constraint_constants.t + + val consensus_constants : Consensus.Constants.t +end + +module Sinks : module type of Sinks + +module Gossip_net : Gossip_net.S with module Rpc_interface := Rpcs + +module Node_status : sig + [%%versioned: + module Stable : sig + module V2 : sig + type t = Rpcs.Get_node_status.Node_status.Stable.V2.t = + { node_ip_addr : Network_peer.Peer.Inet_addr.Stable.V1.t + ; node_peer_id : Network_peer.Peer.Id.Stable.V1.t + ; sync_status : Sync_status.Stable.V1.t + ; peers : Network_peer.Peer.Stable.V1.t list + ; block_producers : Signature_lib.Public_key.Compressed.Stable.V1.t list + ; protocol_state_hash : State_hash.Stable.V1.t + ; ban_statuses : + ( Network_peer.Peer.Stable.V1.t + * Trust_system.Peer_status.Stable.V1.t ) + list + ; k_block_hashes_and_timestamps : + (State_hash.Stable.V1.t * Bounded_types.String.Stable.V1.t) list + ; git_commit : Bounded_types.String.Stable.V1.t + ; uptime_minutes : int + ; block_height_opt : int option + } + [@@deriving bin_io, yojson] + end + + module V1 : sig + type t = Rpcs.Get_node_status.Node_status.Stable.V1.t = + { node_ip_addr : Network_peer.Peer.Inet_addr.Stable.V1.t + ; node_peer_id : Network_peer.Peer.Id.Stable.V1.t + ; sync_status : Sync_status.Stable.V1.t + ; peers : Network_peer.Peer.Stable.V1.t list + ; block_producers : Signature_lib.Public_key.Compressed.Stable.V1.t list + ; protocol_state_hash : State_hash.Stable.V1.t + ; ban_statuses : + ( Network_peer.Peer.Stable.V1.t + * Trust_system.Peer_status.Stable.V1.t ) + list + ; k_block_hashes_and_timestamps : + (State_hash.Stable.V1.t * Bounded_types.String.Stable.V1.t) list + ; git_commit : Bounded_types.String.Stable.V1.t + ; uptime_minutes : int + } + [@@deriving bin_io, yojson] + end + end] +end module Rpcs : sig + module Get_some_initial_peers : sig + type query = unit + + type response = Peer.t list + end + module Get_staged_ledger_aux_and_pending_coinbases_at_hash : sig type query = State_hash.t @@ -29,9 +100,10 @@ module Rpcs : sig end module Answer_sync_ledger_query : sig - type query = Ledger_hash.t * Mina_ledger.Sync_ledger.Query.t + type query = Ledger_hash.t * Sync_ledger.Query.t - type response = Mina_ledger.Sync_ledger.Answer.t Core.Or_error.t + type response = + (Sync_ledger.Answer.t, Bounded_types.Wrapped_error.Stable.V1.t) Result.t end module Get_transition_chain : sig @@ -40,16 +112,26 @@ module Rpcs : sig type response = Mina_block.t list option end + module Get_transition_chain_proof : sig + type query = State_hash.t + + type response = (State_hash.t * State_body_hash.t list) option + end + module Get_transition_knowledge : sig type query = unit type response = State_hash.t list end - module Get_transition_chain_proof : sig - type query = State_hash.t + module Get_node_status : sig + type query = unit - type response = (State_hash.t * State_body_hash.t list) option + type response = + ( Node_status.Stable.Latest.t + , Bounded_types.Wrapped_error.Stable.V1.t ) + result + [@@deriving to_yojson] end module Get_ancestry : sig @@ -64,13 +146,14 @@ module Rpcs : sig end module Ban_notify : sig + (* banned until this time *) type query = Core.Time.t type response = unit end module Get_best_tip : sig - type query = unit [@@deriving sexp, to_yojson] + type query = unit type response = ( Mina_block.t @@ -79,45 +162,7 @@ module Rpcs : sig option end - module Get_node_status : sig - module Node_status : sig - [%%versioned: - module Stable : sig - module V2 : sig - type t = - { node_ip_addr : Network_peer.Peer.Inet_addr.Stable.V1.t - ; node_peer_id : Peer.Id.Stable.V1.t - ; sync_status : Sync_status.Stable.V1.t - ; peers : Network_peer.Peer.Stable.V1.t list - ; block_producers : - Signature_lib.Public_key.Compressed.Stable.V1.t list - ; protocol_state_hash : State_hash.Stable.V1.t - ; ban_statuses : - ( Network_peer.Peer.Stable.V1.t - * Trust_system.Peer_status.Stable.V1.t ) - list - ; k_block_hashes_and_timestamps : - (State_hash.Stable.V1.t * string) list - ; git_commit : string - ; uptime_minutes : int - ; block_height_opt : int option - } - end - end] - end - - type query = unit [@@deriving sexp, to_yojson] - - type response = Node_status.t Or_error.t [@@deriving to_yojson] - end - - module Get_some_initial_peers : sig - type query = unit [@@deriving sexp, to_yojson] - - type response = Network_peer.Peer.t list [@@deriving to_yojson] - end - - type ('query, 'response) rpc = + type ('query, 'response) rpc = ('query, 'response) Rpcs.rpc = | Get_some_initial_peers : (Get_some_initial_peers.query, Get_some_initial_peers.response) rpc | Get_staged_ledger_aux_and_pending_coinbases_at_hash @@ -142,28 +187,15 @@ module Rpcs : sig | Get_ancestry : (Get_ancestry.query, Get_ancestry.response) rpc | Ban_notify : (Ban_notify.query, Ban_notify.response) rpc | Get_best_tip : (Get_best_tip.query, Get_best_tip.response) rpc - - include Rpc_intf.Rpc_interface_intf with type ('q, 'r) rpc := ('q, 'r) rpc end -module Sinks : module type of Sinks - -module Gossip_net : Gossip_net.S with module Rpc_intf := Rpcs - module Config : sig type log_gossip_heard = { snark_pool_diff : bool; transaction_pool_diff : bool; new_state : bool } [@@deriving make] type t = - { logger : Logger.t - ; trust_system : Trust_system.t - ; time_controller : Block_time.Controller.t - ; consensus_constants : Consensus.Constants.t - ; consensus_local_state : Consensus.Data.Local_state.t - ; genesis_ledger_hash : Ledger_hash.t - ; constraint_constants : Genesis_constants.Constraint_constants.t - ; precomputed_values : Precomputed_values.t + { genesis_ledger_hash : Ledger_hash.t ; creatable_gossip_net : Gossip_net.Any.creatable ; is_seed : bool ; log_gossip_heard : log_gossip_heard @@ -181,9 +213,7 @@ val bandwidth_info : Deferred.Or_error.t val get_peer_node_status : - t - -> Network_peer.Peer.t - -> Rpcs.Get_node_status.Node_status.t Deferred.Or_error.t + t -> Network_peer.Peer.t -> Node_status.t Deferred.Or_error.t val add_peer : t -> Network_peer.Peer.t -> is_seed:bool -> unit Deferred.Or_error.t @@ -264,7 +294,7 @@ val query_peer : -> Network_peer.Peer.Id.t -> ('q, 'r) Rpcs.rpc -> 'q - -> 'r Network_peer.Rpc_intf.rpc_response Deferred.t + -> 'r Gossip_net.rpc_response Deferred.t val restart_helper : t -> unit @@ -282,35 +312,11 @@ val ban_notification_reader : t -> Gossip_net.ban_notification Linear_pipe.Reader.t val create : - Config.t + (module CONTEXT) + -> Config.t -> sinks:Sinks.t - -> get_some_initial_peers: - ( Rpcs.Get_some_initial_peers.query Envelope.Incoming.t - -> Rpcs.Get_some_initial_peers.response Deferred.t ) - -> get_staged_ledger_aux_and_pending_coinbases_at_hash: - ( Rpcs.Get_staged_ledger_aux_and_pending_coinbases_at_hash.query - Envelope.Incoming.t - -> Rpcs.Get_staged_ledger_aux_and_pending_coinbases_at_hash.response - Deferred.t ) - -> answer_sync_ledger_query: - ( Rpcs.Answer_sync_ledger_query.query Envelope.Incoming.t - -> Rpcs.Answer_sync_ledger_query.response Deferred.t ) - -> get_ancestry: - ( Rpcs.Get_ancestry.query Envelope.Incoming.t - -> Rpcs.Get_ancestry.response Deferred.t ) - -> get_best_tip: - ( Rpcs.Get_best_tip.query Envelope.Incoming.t - -> Rpcs.Get_best_tip.response Deferred.t ) + -> get_transition_frontier:(unit -> Transition_frontier.t option) -> get_node_status: ( Rpcs.Get_node_status.query Envelope.Incoming.t -> Rpcs.Get_node_status.response Deferred.t ) - -> get_transition_chain_proof: - ( Rpcs.Get_transition_chain_proof.query Envelope.Incoming.t - -> Rpcs.Get_transition_chain_proof.response Deferred.t ) - -> get_transition_chain: - ( Rpcs.Get_transition_chain.query Envelope.Incoming.t - -> Rpcs.Get_transition_chain.response Deferred.t ) - -> get_transition_knowledge: - ( Rpcs.Get_transition_knowledge.query Envelope.Incoming.t - -> Rpcs.Get_transition_knowledge.response Deferred.t ) -> t Deferred.t diff --git a/src/lib/mina_networking/rpcs.ml b/src/lib/mina_networking/rpcs.ml new file mode 100644 index 00000000000..01467fe022c --- /dev/null +++ b/src/lib/mina_networking/rpcs.ml @@ -0,0 +1,1355 @@ +open Async +open Core +open Mina_base +open Mina_ledger +open Network_peer + +(* For versioning of the types here, see + + RFC 0012, and + + https://ocaml.janestreet.com/ocaml-core/latest/doc/async_rpc_kernel/Async_rpc_kernel/Versioned_rpc/ + + The "master" types are the ones used internally in the code base. Each + version has coercions between their query and response types and the master + types. +*) + +(* The common context passed into all rpc handlers. Add new things here to get them into the scope + of an rpc handler. Notably, calls back into Gossip_net need to be expicitly wrapped at this + layer in order to solve a recursive dependency between Gossip_net.Make and this module. *) +(* TODO: we could fix the Gossip_net dependency loop with a better interface. *) + +module type CONTEXT = sig + val logger : Logger.t + + val trust_system : Trust_system.t + + val precomputed_values : Precomputed_values.t + + val constraint_constants : Genesis_constants.Constraint_constants.t + + val consensus_constants : Consensus.Constants.t + + val list_peers : unit -> Peer.t list Deferred.t + + val get_transition_frontier : unit -> Transition_frontier.t option +end + +type ctx = (module CONTEXT) +(* + { logger : Logger.t + ; trust_system : Trust_system.t + ; list_peers : unit -> Peer.t list + ; get_transition_frontier : unit -> Transition_frontier.t option + } +*) + +let validate_protocol_versions ~logger ~trust_system ~rpc_name ~sender blocks = + let version_errors = + let invalid_current_versions = + List.filter blocks ~f:(fun block -> + Mina_block.header block |> Mina_block.Header.current_protocol_version + |> Protocol_version.is_valid |> not ) + in + + let invalid_next_versions = + List.filter blocks ~f:(fun block -> + Mina_block.header block + |> Mina_block.Header.proposed_protocol_version_opt + |> Option.for_all ~f:Protocol_version.is_valid + |> not ) + in + let current_version_mismatches = + List.filter blocks ~f:(fun block -> + Mina_block.header block |> Mina_block.Header.current_protocol_version + |> Protocol_version.compatible_with_daemon |> not ) + in + List.map invalid_current_versions ~f:(fun x -> + (`Invalid_current_version, x) ) + @ List.map invalid_next_versions ~f:(fun x -> (`Invalid_next_version, x)) + @ List.map current_version_mismatches ~f:(fun x -> + (`Current_version_mismatch, x) ) + in + let%map () = + (* TODO: these errors aren't always accurate... sometimes we are calling this when we were + requested to serve an outdated block (requested vs sent) *) + Deferred.List.iter version_errors ~how:`Parallel + ~f:(fun (version_error, block) -> + let header = Mina_block.header block in + let block_protocol_version = + Mina_block.Header.current_protocol_version header + in + let proposed_protocol_version = + Mina_block.Header.proposed_protocol_version_opt header + in + let action, error_msg, error_metadata = + (* TODO: update confusing metadata names *) + match version_error with + | `Invalid_current_version -> + ( Trust_system.Actions.Sent_invalid_protocol_version + , "external transition with invalid current protocol version" + , [ ( "current_protocol_version" + , `String (Protocol_version.to_string block_protocol_version) + ) + ] ) + | `Invalid_next_version -> + ( Trust_system.Actions.Sent_invalid_protocol_version + , "external transition with invalid proposed protocol version" + , [ ( "proposed_protocol_version" + , `String + (Protocol_version.to_string + (Option.value_exn proposed_protocol_version) ) ) + ] ) + | `Current_version_mismatch -> + ( Sent_mismatched_protocol_version + , "current protocol version in external transition does not \ + match daemon current protocol version" + , [ ( "current_protocol_version" + , `String (Protocol_version.to_string block_protocol_version) + ) + ; ( "daemon_current_protocol_version" + , `String Protocol_version.(to_string current) ) + ] ) + in + let msg = + Some + ( Printf.sprintf "$rpc_name: %s" error_msg + , [ ("rpc_name", `String rpc_name) ] @ error_metadata ) + in + Trust_system.record_envelope_sender trust_system logger sender + (action, msg) ) + in + List.is_empty version_errors + +[%%versioned_rpc +module Get_some_initial_peers = struct + type nonrec ctx = ctx + + module Master = struct + let name = "get_some_initial_peers" + + module T = struct + type query = unit [@@deriving sexp, yojson] + + type response = Peer.t list [@@deriving sexp, yojson] + end + + module Caller = T + module Callee = T + end + + include Master.T + + let sent_counter = Mina_metrics.Network.get_some_initial_peers_rpcs_sent + + let received_counter = + Mina_metrics.Network.get_some_initial_peers_rpcs_received + + let failed_request_counter = + Mina_metrics.Network.get_some_initial_peers_rpc_requests_failed + + let failed_response_counter = + Mina_metrics.Network.get_some_initial_peers_rpc_responses_failed + + module M = Versioned_rpc.Both_convert.Plain.Make (Master) + include M + + include Perf_histograms.Rpc.Plain.Extend (struct + include M + include Master + end) + + module V1 = struct + module T = struct + type query = unit + + type response = Network_peer.Peer.Stable.V1.t list + + let query_of_caller_model = Fn.id + + let callee_model_of_query = Fn.id + + let response_of_callee_model = Fn.id + + let caller_model_of_response = Fn.id + end + + module T' = + Perf_histograms.Rpc.Plain.Decorate_bin_io + (struct + include M + include Master + end) + (T) + + include T' + include Register (T') + end + + let receipt_trust_action_message _ = Some ("Get_some_initial_peers query", []) + + let log_request_received ~logger ~sender () = + [%log trace] "Sending some initial peers to $peer" + ~metadata:[ ("peer", Peer.to_yojson sender) ] + + let response_is_successful peer_list = not (List.is_empty peer_list) + + let handle_request (module Context : CONTEXT) ~version:_ _request = + Context.list_peers () + + let rate_limit_budget = (1, `Per Time.Span.minute) + + let rate_limit_cost = Fn.const 1 +end] + +[%%versioned_rpc +module Get_staged_ledger_aux_and_pending_coinbases_at_hash = struct + type nonrec ctx = ctx + + module Master = struct + let name = "get_staged_ledger_aux_and_pending_coinbases_at_hash" + + module T = struct + type query = State_hash.t + + type response = + ( Staged_ledger.Scan_state.t + * Ledger_hash.t + * Pending_coinbase.t + * Mina_state.Protocol_state.value list ) + option + end + + module Caller = T + module Callee = T + end + + include Master.T + + let sent_counter = + Mina_metrics.Network + .get_staged_ledger_aux_and_pending_coinbases_at_hash_rpcs_sent + + let received_counter = + Mina_metrics.Network + .get_staged_ledger_aux_and_pending_coinbases_at_hash_rpcs_received + + let failed_request_counter = + Mina_metrics.Network + .get_staged_ledger_aux_and_pending_coinbases_at_hash_rpc_requests_failed + + let failed_response_counter = + Mina_metrics.Network + .get_staged_ledger_aux_and_pending_coinbases_at_hash_rpc_responses_failed + + module M = Versioned_rpc.Both_convert.Plain.Make (Master) + include M + + include Perf_histograms.Rpc.Plain.Extend (struct + include M + include Master + end) + + module V2 = struct + module T = struct + type query = State_hash.Stable.V1.t + + type response = + ( Staged_ledger.Scan_state.Stable.V2.t + * Ledger_hash.Stable.V1.t + * Pending_coinbase.Stable.V2.t + * Mina_state.Protocol_state.Value.Stable.V2.t list ) + option + + let query_of_caller_model = Fn.id + + let callee_model_of_query = Fn.id + + let response_of_callee_model = Fn.id + + let caller_model_of_response = Fn.id + end + + module T' = + Perf_histograms.Rpc.Plain.Decorate_bin_io + (struct + include M + include Master + end) + (T) + + include T' + include Register (T') + end + + let receipt_trust_action_message hash = + Some + ( "Staged ledger and pending coinbases at hash: $hash" + , [ ("hash", State_hash.to_yojson hash) ] ) + + let log_request_received ~logger:_ ~sender:_ _request = () + + let response_is_successful = Option.is_some + + let handle_request (module Context : CONTEXT) ~version:_ request = + let open Context in + let hash = Envelope.Incoming.data request in + let result = + (* TODO: failure to access frontier should result in different trust system behavior *) + let%bind.Option frontier = get_transition_frontier () in + Sync_handler.get_staged_ledger_aux_and_pending_coinbases_at_hash ~frontier + hash + in + let%map () = + match result with + | Some + (scan_state, expected_merkle_root, pending_coinbases, _protocol_states) + -> + let staged_ledger_hash = + Staged_ledger_hash.of_aux_ledger_and_coinbase_hash + (Staged_ledger.Scan_state.hash scan_state) + expected_merkle_root pending_coinbases + in + [%log debug] + ~metadata: + [ ( "staged_ledger_hash" + , Staged_ledger_hash.to_yojson staged_ledger_hash ) + ] + "sending scan state and pending coinbase" ; + Deferred.unit + | None -> + Trust_system.( + record_envelope_sender trust_system logger + (Envelope.Incoming.sender request) + Actions.(Requested_unknown_item, receipt_trust_action_message hash)) + in + result + + let rate_limit_budget = (4, `Per Time.Span.minute) + + let rate_limit_cost = Fn.const 1 +end] + +[%%versioned_rpc +module Answer_sync_ledger_query = struct + type nonrec ctx = ctx + + module Master = struct + let name = "answer_sync_ledger_query" + + module T = struct + type query = Ledger_hash.t * Sync_ledger.Query.t + + type response = + (( Sync_ledger.Answer.t + , Bounded_types.Wrapped_error.Stable.V1.t ) + Result.t + [@version_asserted] ) + end + + module Caller = T + module Callee = T + end + + include Master.T + + let sent_counter = Mina_metrics.Network.answer_sync_ledger_query_rpcs_sent + + let received_counter = + Mina_metrics.Network.answer_sync_ledger_query_rpcs_received + + let failed_request_counter = + Mina_metrics.Network.answer_sync_ledger_query_rpc_requests_failed + + let failed_response_counter = + Mina_metrics.Network.answer_sync_ledger_query_rpc_responses_failed + + module M = Versioned_rpc.Both_convert.Plain.Make (Master) + include M + + include Perf_histograms.Rpc.Plain.Extend (struct + include M + include Master + end) + + module V3 = struct + module T = struct + type query = Ledger_hash.Stable.V1.t * Sync_ledger.Query.Stable.V1.t + [@@deriving sexp] + + type response = + (( Sync_ledger.Answer.Stable.V2.t + , Bounded_types.Wrapped_error.Stable.V1.t ) + Result.t + [@version_asserted] ) + [@@deriving sexp] + + let query_of_caller_model = Fn.id + + let callee_model_of_query = Fn.id + + let response_of_callee_model = Fn.id + + let caller_model_of_response = Fn.id + end + + module T' = + Perf_histograms.Rpc.Plain.Decorate_bin_io + (struct + include M + include Master + end) + (T) + + include T' + include Register (T') + end + + let receipt_trust_action_message (_, query) = + Some + ( "Answer_sync_ledger_query: $query" + , [ ("query", Sync_ledger.Query.to_yojson query) ] ) + + let log_request_received ~logger:_ ~sender:_ _request = () + + let response_is_successful = Result.is_ok + + let handle_request (module Context : CONTEXT) ~version:_ request = + let open Context in + let ledger_hash, _ = Envelope.Incoming.data request in + let query = Envelope.Incoming.map request ~f:Tuple2.get2 in + let%bind answer = + let%bind.Deferred.Option frontier = return (get_transition_frontier ()) in + Sync_handler.answer_query ~frontier ledger_hash query ~logger + ~trust_system + in + let result = + (* TODO: should we really be returning an error for this RPC if there is only 1 kind of error? + (we could just wrap the error on the client side) *) + Result.of_option answer + ~error: + (Error.createf + (* TODO: improve weird error message *) + !"Refused to answer_query for ledger_hash: %{sexp:Ledger_hash.t}" + ledger_hash ) + in + let%map () = + match result with + | Ok _ -> + return () + | Error err -> + Trust_system.( + record_envelope_sender trust_system logger + (Envelope.Incoming.sender request) + Actions. + ( Requested_unknown_item + , Some + ( "Sync ledger query with hash: $hash, query: $query, with \ + error: $error" + , [ ("hash", Ledger_hash.to_yojson ledger_hash) + ; ( "query" + , Syncable_ledger.Query.to_yojson + Mina_ledger.Ledger.Addr.to_yojson + (Envelope.Incoming.data query) ) + ; ("error", Error_json.error_to_yojson err) + ] ) )) + in + result + + let rate_limit_budget = (Int.pow 2 17, `Per Time.Span.minute) + + let rate_limit_cost = Fn.const 1 +end] + +[%%versioned_rpc +module Get_transition_chain = struct + type nonrec ctx = ctx + + module Master = struct + let name = "get_transition_chain" + + module T = struct + type query = State_hash.t list [@@deriving sexp, to_yojson] + + type response = Mina_block.t list option + end + + module Caller = T + module Callee = T + end + + include Master.T + + let sent_counter = Mina_metrics.Network.get_transition_chain_rpcs_sent + + let received_counter = Mina_metrics.Network.get_transition_chain_rpcs_received + + let failed_request_counter = + Mina_metrics.Network.get_transition_chain_rpc_requests_failed + + let failed_response_counter = + Mina_metrics.Network.get_transition_chain_rpc_responses_failed + + module M = Versioned_rpc.Both_convert.Plain.Make (Master) + include M + + include Perf_histograms.Rpc.Plain.Extend (struct + include M + include Master + end) + + module V2 = struct + module T = struct + type query = State_hash.Stable.V1.t list [@@deriving sexp] + + type response = Mina_block.Stable.V2.t list option + + let query_of_caller_model = Fn.id + + let callee_model_of_query = Fn.id + + let response_of_callee_model = ident + + let caller_model_of_response = ident + end + + module T' = + Perf_histograms.Rpc.Plain.Decorate_bin_io + (struct + include M + include Master + end) + (T) + + include T' + include Register (T') + end + + let receipt_trust_action_message query = + Some + ( "Get_transition_chain query: $query" + , [ ("query", query_to_yojson query) ] ) + + let log_request_received ~logger ~sender _request = + [%log info] "Sending transition_chain to $peer" + ~metadata:[ ("peer", Peer.to_yojson sender) ] + + let response_is_successful = Option.is_some + + let handle_request (module Context : CONTEXT) ~version:_ request = + let open Context in + let hashes = Envelope.Incoming.data request in + let result = + let%bind.Option frontier = get_transition_frontier () in + Sync_handler.get_transition_chain ~frontier hashes + in + match result with + | Some blocks -> + let%map valid_versions = + validate_protocol_versions ~logger ~trust_system + ~rpc_name:"Get_transition_chain" + ~sender:(Envelope.Incoming.sender request) + blocks + in + Option.some_if valid_versions blocks + | None -> + let%map () = + Trust_system.( + record_envelope_sender trust_system logger + (Envelope.Incoming.sender request) + Actions. + (Requested_unknown_item, receipt_trust_action_message hashes)) + in + None + + let rate_limit_budget = (1, `Per Time.Span.second) + + let rate_limit_cost hashes = Int.max 1 (List.length hashes) +end] + +[%%versioned_rpc +module Get_transition_knowledge = struct + type nonrec ctx = ctx + + module Master = struct + let name = "Get_transition_knowledge" + + module T = struct + type query = unit [@@deriving sexp, to_yojson] + + type response = State_hash.t list + end + + module Caller = T + module Callee = T + end + + include Master.T + + let sent_counter = Mina_metrics.Network.get_transition_knowledge_rpcs_sent + + let received_counter = + Mina_metrics.Network.get_transition_knowledge_rpcs_received + + let failed_request_counter = + Mina_metrics.Network.get_transition_knowledge_rpc_requests_failed + + let failed_response_counter = + Mina_metrics.Network.get_transition_knowledge_rpc_responses_failed + + module M = Versioned_rpc.Both_convert.Plain.Make (Master) + include M + + include Perf_histograms.Rpc.Plain.Extend (struct + include M + include Master + end) + + module V1 = struct + module T = struct + type query = unit [@@deriving sexp] + + type response = State_hash.Stable.V1.t list + + let query_of_caller_model = Fn.id + + let callee_model_of_query = Fn.id + + let response_of_callee_model = Fn.id + + let caller_model_of_response = Fn.id + end + + module T' = + Perf_histograms.Rpc.Plain.Decorate_bin_io + (struct + include M + include Master + end) + (T) + + include T' + include Register (T') + end + + let receipt_trust_action_message query = + Some + ( "Get_transition_knowledge query: $query" + , [ ("query", query_to_yojson query) ] ) + + let log_request_received ~logger ~sender _request = + [%log info] "Sending transition_knowledge to $peer" + ~metadata:[ ("peer", Peer.to_yojson sender) ] + + let response_is_successful hashes = not (List.is_empty hashes) + + let handle_request (module Context : CONTEXT) ~version:_ _request = + let open Context in + let result = + let%map.Option frontier = get_transition_frontier () in + Sync_handler.best_tip_path ~frontier + in + return (Option.value result ~default:[]) + + let rate_limit_budget = (1, `Per Time.Span.minute) + + let rate_limit_cost = Fn.const 1 +end] + +[%%versioned_rpc +module Get_transition_chain_proof = struct + type nonrec ctx = ctx + + module Master = struct + let name = "get_transition_chain_proof" + + module T = struct + type query = State_hash.t [@@deriving sexp, to_yojson] + + type response = (State_hash.t * State_body_hash.t list) option + end + + module Caller = T + module Callee = T + end + + include Master.T + + let sent_counter = Mina_metrics.Network.get_transition_chain_proof_rpcs_sent + + let received_counter = + Mina_metrics.Network.get_transition_chain_proof_rpcs_received + + let failed_request_counter = + Mina_metrics.Network.get_transition_chain_proof_rpc_requests_failed + + let failed_response_counter = + Mina_metrics.Network.get_transition_chain_proof_rpc_responses_failed + + module M = Versioned_rpc.Both_convert.Plain.Make (Master) + include M + + include Perf_histograms.Rpc.Plain.Extend (struct + include M + include Master + end) + + module V1 = struct + module T = struct + type query = State_hash.Stable.V1.t [@@deriving sexp] + + type response = + (State_hash.Stable.V1.t * State_body_hash.Stable.V1.t list) option + + let query_of_caller_model = Fn.id + + let callee_model_of_query = Fn.id + + let response_of_callee_model = Fn.id + + let caller_model_of_response = Fn.id + end + + module T' = + Perf_histograms.Rpc.Plain.Decorate_bin_io + (struct + include M + include Master + end) + (T) + + include T' + include Register (T') + end + + let receipt_trust_action_message query = + Some + ( "Get_transition_chain_proof query: $query" + , [ ("query", query_to_yojson query) ] ) + + let log_request_received ~logger ~sender _request = + [%log info] "Sending transition_chain_proof to $peer" + ~metadata:[ ("peer", Peer.to_yojson sender) ] + + let response_is_successful = Option.is_some + + let handle_request (module Context : CONTEXT) ~version:_ request = + let open Context in + let hash = Envelope.Incoming.data request in + let result = + let%bind.Option frontier = get_transition_frontier () in + Transition_chain_prover.prove ~frontier hash + in + let%map () = + if Option.is_none result then + Trust_system.( + record_envelope_sender trust_system logger + (Envelope.Incoming.sender request) + Actions.(Requested_unknown_item, receipt_trust_action_message hash)) + else return () + in + result + + let rate_limit_budget = (3, `Per Time.Span.minute) + + let rate_limit_cost = Fn.const 1 +end] + +(* TODO: remove this unused RPC *) +[%%versioned_rpc +module Get_node_status = struct + type nonrec ctx = ctx + + module Node_status = struct + [%%versioned + module Stable = struct + module V2 = struct + type t = + { node_ip_addr : Network_peer.Peer.Inet_addr.Stable.V1.t + ; node_peer_id : Network_peer.Peer.Id.Stable.V1.t + [@to_yojson fun peer_id -> `String peer_id] + [@of_yojson + function `String s -> Ok s | _ -> Error "expected string"] + ; sync_status : Sync_status.Stable.V1.t + ; peers : Network_peer.Peer.Stable.V1.t list + ; block_producers : + Signature_lib.Public_key.Compressed.Stable.V1.t list + ; protocol_state_hash : State_hash.Stable.V1.t + ; ban_statuses : + ( Network_peer.Peer.Stable.V1.t + * Trust_system.Peer_status.Stable.V1.t ) + list + ; k_block_hashes_and_timestamps : + (State_hash.Stable.V1.t * Bounded_types.String.Stable.V1.t) list + ; git_commit : Bounded_types.String.Stable.V1.t + ; uptime_minutes : int + ; block_height_opt : int option [@default None] + } + [@@deriving to_yojson, of_yojson] + + let to_latest = Fn.id + end + + module V1 = struct + type t = + { node_ip_addr : Network_peer.Peer.Inet_addr.Stable.V1.t + ; node_peer_id : Network_peer.Peer.Id.Stable.V1.t + [@to_yojson fun peer_id -> `String peer_id] + [@of_yojson + function `String s -> Ok s | _ -> Error "expected string"] + ; sync_status : Sync_status.Stable.V1.t + ; peers : Network_peer.Peer.Stable.V1.t list + ; block_producers : + Signature_lib.Public_key.Compressed.Stable.V1.t list + ; protocol_state_hash : State_hash.Stable.V1.t + ; ban_statuses : + ( Network_peer.Peer.Stable.V1.t + * Trust_system.Peer_status.Stable.V1.t ) + list + ; k_block_hashes_and_timestamps : + (State_hash.Stable.V1.t * Bounded_types.String.Stable.V1.t) list + ; git_commit : Bounded_types.String.Stable.V1.t + ; uptime_minutes : int + } + [@@deriving to_yojson, of_yojson] + + let to_latest status : Latest.t = + { node_ip_addr = status.node_ip_addr + ; node_peer_id = status.node_peer_id + ; sync_status = status.sync_status + ; peers = status.peers + ; block_producers = status.block_producers + ; protocol_state_hash = status.protocol_state_hash + ; ban_statuses = status.ban_statuses + ; k_block_hashes_and_timestamps = status.k_block_hashes_and_timestamps + ; git_commit = status.git_commit + ; uptime_minutes = status.uptime_minutes + ; block_height_opt = None + } + end + end] + end + + module Master = struct + let name = "get_node_status" + + module T = struct + type query = unit [@@deriving sexp, to_yojson] + + type response = + (Node_status.t, Bounded_types.Wrapped_error.Stable.V1.t) result + end + + module Caller = T + module Callee = T + end + + include Master.T + + let sent_counter = Mina_metrics.Network.get_node_status_rpcs_sent + + let received_counter = Mina_metrics.Network.get_node_status_rpcs_received + + let failed_request_counter = + Mina_metrics.Network.get_node_status_rpc_requests_failed + + let failed_response_counter = + Mina_metrics.Network.get_node_status_rpc_responses_failed + + module M = Versioned_rpc.Both_convert.Plain.Make (Master) + include M + + let response_to_yojson response = + match response with + | Ok status -> + Node_status.Stable.Latest.to_yojson status + | Error err -> + `Assoc [ ("error", Error_json.error_to_yojson err) ] + + include Perf_histograms.Rpc.Plain.Extend (struct + include M + include Master + end) + + module V2 = struct + module T = struct + type query = unit [@@deriving sexp] + + type response = + (( Node_status.Stable.V2.t + , Bounded_types.Wrapped_error.Stable.V1.t ) + Result.t + [@version_asserted] ) + + let query_of_caller_model = Fn.id + + let callee_model_of_query = Fn.id + + let response_of_callee_model = Fn.id + + let caller_model_of_response = Fn.id + end + + module T' = + Perf_histograms.Rpc.Plain.Decorate_bin_io + (struct + include M + include Master + end) + (T) + + include T' + include Register (T') + end + + module V1 = struct + module T = struct + type query = unit [@@deriving sexp] + + type response = + (( Node_status.Stable.V1.t + , Bounded_types.Wrapped_error.Stable.V1.t ) + Core_kernel.Result.t + [@version_asserted] ) + + let query_of_caller_model = Fn.id + + let callee_model_of_query = Fn.id + + let response_of_callee_model = function + | Error err -> + Error err + | Ok (status : Node_status.Stable.Latest.t) -> + Ok + { Node_status.Stable.V1.node_ip_addr = status.node_ip_addr + ; node_peer_id = status.node_peer_id + ; sync_status = status.sync_status + ; peers = status.peers + ; block_producers = status.block_producers + ; protocol_state_hash = status.protocol_state_hash + ; ban_statuses = status.ban_statuses + ; k_block_hashes_and_timestamps = + status.k_block_hashes_and_timestamps + ; git_commit = status.git_commit + ; uptime_minutes = status.uptime_minutes + } + + let caller_model_of_response = function + | Error err -> + Error err + | Ok (status : Node_status.Stable.V1.t) -> + Ok (Node_status.Stable.V1.to_latest status) + end + + module T' = + Perf_histograms.Rpc.Plain.Decorate_bin_io + (struct + include M + include Master + end) + (T) + + include T' + include Register (T') + end + + let receipt_trust_action_message _request = failwith "TODO" + + let log_request_received ~logger:_ ~sender:_ _request = failwith "TODO" + + let response_is_successful _response = failwith "TODO" + + let handle_request _ctx ~version:_ _request = failwith "TODO" + + let rate_limit_budget = (1, `Per Time.Span.second) + + let rate_limit_cost _ = failwith "TODO" +end] + +[%%versioned_rpc +module Get_ancestry = struct + type nonrec ctx = ctx + + module Master = struct + let name = "get_ancestry" + + module T = struct + (** NB: The state hash sent in this query should not be trusted, as it can be forged. This is ok for how this RPC is implented, as we only use the state hash for tie breaking when checking whether or not the proof is worth serving. *) + type query = + (Consensus.Data.Consensus_state.Value.t, State_hash.t) With_hash.t + [@@deriving sexp, to_yojson] + + type response = + ( Mina_block.t + , State_body_hash.t list * Mina_block.t ) + Proof_carrying_data.t + option + end + + module Caller = T + module Callee = T + end + + include Master.T + + let sent_counter = Mina_metrics.Network.get_ancestry_rpcs_sent + + let received_counter = Mina_metrics.Network.get_ancestry_rpcs_received + + let failed_request_counter = + Mina_metrics.Network.get_ancestry_rpc_requests_failed + + let failed_response_counter = + Mina_metrics.Network.get_ancestry_rpc_responses_failed + + module M = Versioned_rpc.Both_convert.Plain.Make (Master) + include M + + include Perf_histograms.Rpc.Plain.Extend (struct + include M + include Master + end) + + module V2 = struct + module T = struct + type query = + ( Consensus.Data.Consensus_state.Value.Stable.V2.t + , State_hash.Stable.V1.t ) + With_hash.Stable.V1.t + [@@deriving sexp] + + type response = + ( Mina_block.Stable.V2.t + , State_body_hash.Stable.V1.t list * Mina_block.Stable.V2.t ) + Proof_carrying_data.Stable.V1.t + option + + let query_of_caller_model = Fn.id + + let callee_model_of_query = Fn.id + + let response_of_callee_model = ident + + let caller_model_of_response = ident + end + + module T' = + Perf_histograms.Rpc.Plain.Decorate_bin_io + (struct + include M + include Master + end) + (T) + + include T' + include Register (T') + end + + let receipt_trust_action_message query = + Some ("Get_ancestry query: $query", [ ("query", query_to_yojson query) ]) + + let log_request_received ~logger ~sender _request = + [%log debug] "Sending root proof to $peer" + ~metadata:[ ("peer", Peer.to_yojson sender) ] + + let response_is_successful = Option.is_some + + let handle_request (module Context : CONTEXT) ~version:_ request = + let open Context in + let consensus_state_with_hash = Envelope.Incoming.data request in + let result = + let%bind.Option frontier = get_transition_frontier () in + consensus_state_with_hash + |> With_hash.map_hash ~f:(fun state_hash -> + { State_hash.State_hashes.state_hash; state_body_hash = None } ) + |> Sync_handler.Root.prove ~context:(module Context) ~frontier + in + match result with + | None -> + let%map () = + Trust_system.( + record_envelope_sender trust_system logger + (Envelope.Incoming.sender request) + Actions. + ( Requested_unknown_item + , receipt_trust_action_message consensus_state_with_hash )) + in + None + | Some { proof = _, block; _ } -> + let%map valid_versions = + validate_protocol_versions ~logger ~trust_system + ~rpc_name:"Get_ancestry" + ~sender:(Envelope.Incoming.sender request) + [ block ] + in + if valid_versions then result else None + + let rate_limit_budget = (5, `Per Time.Span.minute) + + let rate_limit_cost = Fn.const 1 +end] + +[%%versioned_rpc +module Ban_notify = struct + type nonrec ctx = ctx + + module Master = struct + let name = "ban_notify" + + module T = struct + (* banned until this time *) + type query = Core.Time.t [@@deriving sexp] + + type response = unit + end + + module Caller = T + module Callee = T + end + + include Master.T + + let sent_counter = Mina_metrics.Network.ban_notify_rpcs_sent + + let received_counter = Mina_metrics.Network.ban_notify_rpcs_received + + let failed_request_counter = + Mina_metrics.Network.ban_notify_rpc_requests_failed + + let failed_response_counter = + Mina_metrics.Network.ban_notify_rpc_responses_failed + + module M = Versioned_rpc.Both_convert.Plain.Make (Master) + include M + + include Perf_histograms.Rpc.Plain.Extend (struct + include M + include Master + end) + + module V1 = struct + module T = struct + type query = Core.Time.Stable.V1.t [@@deriving sexp] + + type response = unit + + let query_of_caller_model = Fn.id + + let callee_model_of_query = Fn.id + + let response_of_callee_model = Fn.id + + let caller_model_of_response = Fn.id + end + + module T' = + Perf_histograms.Rpc.Plain.Decorate_bin_io + (struct + include M + include Master + end) + (T) + + include T' + include Register (T') + end + + let receipt_trust_action_message _request = None + + let log_request_received ~logger ~sender ban_until = + [%log warn] "Node banned by peer $peer until $ban_until" + ~metadata: + [ ("peer", Peer.to_yojson sender) + ; ( "ban_until" + , `String (Time.to_string_abs ~zone:Time.Zone.utc ban_until) ) + ] + + let response_is_successful = Fn.const true + + let handle_request _ctx ~version:_ _request = Deferred.unit + + let rate_limit_budget = (1, `Per Time.Span.minute) + + let rate_limit_cost = Fn.const 1 +end] + +[%%versioned_rpc +module Get_best_tip = struct + type nonrec ctx = ctx + + module Master = struct + let name = "get_best_tip" + + module T = struct + type query = unit [@@deriving sexp, to_yojson] + + type response = + ( Mina_block.t + , State_body_hash.t list * Mina_block.t ) + Proof_carrying_data.t + option + end + + module Caller = T + module Callee = T + end + + include Master.T + + let sent_counter = Mina_metrics.Network.get_best_tip_rpcs_sent + + let received_counter = Mina_metrics.Network.get_best_tip_rpcs_received + + let failed_request_counter = + Mina_metrics.Network.get_best_tip_rpc_requests_failed + + let failed_response_counter = + Mina_metrics.Network.get_best_tip_rpc_responses_failed + + module M = Versioned_rpc.Both_convert.Plain.Make (Master) + include M + + include Perf_histograms.Rpc.Plain.Extend (struct + include M + include Master + end) + + module V2 = struct + module T = struct + type query = unit [@@deriving sexp] + + type response = + ( Mina_block.Stable.V2.t + , State_body_hash.Stable.V1.t list * Mina_block.Stable.V2.t ) + Proof_carrying_data.Stable.V1.t + option + + let query_of_caller_model = Fn.id + + let callee_model_of_query = Fn.id + + let response_of_callee_model = ident + + let caller_model_of_response = ident + end + + module T' = + Perf_histograms.Rpc.Plain.Decorate_bin_io + (struct + include M + include Master + end) + (T) + + include T' + include Register (T') + end + + let receipt_trust_action_message _request = Some ("Get_best_tip query", []) + + let log_request_received ~logger ~sender _request = + [%log debug] "Sending best_tip to $peer" + ~metadata:[ ("peer", Peer.to_yojson sender) ] + + let response_is_successful = Option.is_some + + let handle_request (module Context : CONTEXT) ~version:_ request = + let open Context in + let result = + let open Option.Let_syntax in + let%bind frontier = get_transition_frontier () in + let%map proof_with_data = + Best_tip_prover.prove ~context:(module Context) frontier + in + (* strip hash from proof data *) + (* TODO: Best_tip_prover shouldn't return with hash if we don't need it *) + { proof_with_data with data = With_hash.data proof_with_data.data } + in + match result with + | None -> + let%map () = + Trust_system.( + record_envelope_sender trust_system logger + (Envelope.Incoming.sender request) + Actions.(Requested_unknown_item, receipt_trust_action_message ())) + in + None + | Some { data = data_block; proof = _, proof_block } -> + let%map data_valid_versions = + validate_protocol_versions ~logger ~trust_system + ~rpc_name:"Get_best_tip (data)" + ~sender:(Envelope.Incoming.sender request) + [ data_block ] + and proof_valid_versions = + validate_protocol_versions ~logger ~trust_system + ~rpc_name:"Get_best_tip (proof)" + ~sender:(Envelope.Incoming.sender request) + [ proof_block ] + in + if data_valid_versions && proof_valid_versions then result else None + + let rate_limit_budget = (3, `Per Time.Span.minute) + + let rate_limit_cost = Fn.const 1 +end] + +type ('query, 'response) rpc = + | Get_some_initial_peers + : (Get_some_initial_peers.query, Get_some_initial_peers.response) rpc + | Get_staged_ledger_aux_and_pending_coinbases_at_hash + : ( Get_staged_ledger_aux_and_pending_coinbases_at_hash.query + , Get_staged_ledger_aux_and_pending_coinbases_at_hash.response ) + rpc + | Answer_sync_ledger_query + : (Answer_sync_ledger_query.query, Answer_sync_ledger_query.response) rpc + | Get_transition_chain + : (Get_transition_chain.query, Get_transition_chain.response) rpc + | Get_transition_knowledge + : (Get_transition_knowledge.query, Get_transition_knowledge.response) rpc + | Get_transition_chain_proof + : ( Get_transition_chain_proof.query + , Get_transition_chain_proof.response ) + rpc + | Get_node_status : (Get_node_status.query, Get_node_status.response) rpc + | Get_ancestry : (Get_ancestry.query, Get_ancestry.response) rpc + | Ban_notify : (Ban_notify.query, Ban_notify.response) rpc + | Get_best_tip : (Get_best_tip.query, Get_best_tip.response) rpc + +type any_rpc = Rpc : ('q, 'r) rpc -> any_rpc + +(* TODO: we are intentially omitting Get_node_status from this list, since it wasn't being registered previously *) +let all_rpcs = + [ Rpc Get_some_initial_peers + ; Rpc Get_staged_ledger_aux_and_pending_coinbases_at_hash + ; Rpc Answer_sync_ledger_query + ; Rpc Get_best_tip + ; Rpc Get_ancestry + ; Rpc Get_transition_knowledge + ; Rpc Get_transition_chain + ; Rpc Get_transition_chain_proof + ; Rpc Ban_notify + ] + +let implementation : + type q r. (q, r) rpc -> (ctx, q, r) Gossip_net.rpc_implementation = function + | Get_some_initial_peers -> + (module Get_some_initial_peers) + | Get_staged_ledger_aux_and_pending_coinbases_at_hash -> + (module Get_staged_ledger_aux_and_pending_coinbases_at_hash) + | Answer_sync_ledger_query -> + (module Answer_sync_ledger_query) + | Get_transition_chain -> + (module Get_transition_chain) + | Get_transition_knowledge -> + (module Get_transition_knowledge) + | Get_transition_chain_proof -> + (module Get_transition_chain_proof) + | Get_node_status -> + (module Get_node_status) + | Get_ancestry -> + (module Get_ancestry) + | Ban_notify -> + (module Ban_notify) + | Get_best_tip -> + (module Get_best_tip) diff --git a/src/lib/network_peer/network_peer.ml b/src/lib/network_peer/network_peer.ml index f2e37f142c3..d4a3b7c0ce9 100644 --- a/src/lib/network_peer/network_peer.ml +++ b/src/lib/network_peer/network_peer.ml @@ -1,14 +1,14 @@ +open Async_kernel +open Async_rpc_kernel module Peer = Peer module Envelope = Envelope -module Rpc_intf = Rpc_intf +(* TODO: move this out of Network_peer *) type query_peer = { query : 'r 'q. Peer.t - -> ( Async_rpc_kernel.Versioned_rpc.Connection_with_menu.t - -> 'q - -> 'r Async_kernel.Deferred.Or_error.t ) + -> (Versioned_rpc.Connection_with_menu.t -> 'q -> 'r Deferred.Or_error.t) -> 'q - -> 'r Async_kernel.Deferred.Or_error.t + -> 'r Deferred.Or_error.t } diff --git a/src/lib/network_peer/rpc_intf.ml b/src/lib/network_peer/rpc_intf.ml deleted file mode 100644 index d3e913c7695..00000000000 --- a/src/lib/network_peer/rpc_intf.ml +++ /dev/null @@ -1,62 +0,0 @@ -open Async -open Core - -type state = Peer.t - -type ('query, 'response) rpc_fn = - state -> version:int -> 'query -> 'response Deferred.t - -type 'r rpc_response = - | Failed_to_connect of Error.t - | Connected of 'r Or_error.t Envelope.Incoming.t - -module type Rpc_implementation_intf = sig - type query - - type response - - val name : string - - val versions : unit -> Int.Set.t - - val sent_counter : Mina_metrics.Counter.t * Mina_metrics.Gauge.t - - val received_counter : Mina_metrics.Counter.t * Mina_metrics.Gauge.t - - val failed_request_counter : Mina_metrics.Counter.t - - val failed_response_counter : Mina_metrics.Counter.t - - val implement_multi : - ?log_not_previously_seen_version:(name:string -> int -> unit) - -> (query, response) rpc_fn - -> state Rpc.Implementation.t list - - val dispatch_multi : - Versioned_rpc.Connection_with_menu.t - -> query - -> response Deferred.Or_error.t -end - -type ('query, 'response) rpc_implementation = - (module Rpc_implementation_intf - with type query = 'query - and type response = 'response ) - -module type Rpc_interface_intf = sig - type ('query, 'response) rpc - - type rpc_handler = - | Rpc_handler : - { rpc : ('q, 'r) rpc - ; f : ('q, 'r) rpc_fn - ; cost : 'q -> int - ; budget : int * [ `Per of Time.Span.t ] - } - -> rpc_handler - - val implementation_of_rpc : ('q, 'r) rpc -> ('q, 'r) rpc_implementation - - val match_handler : - rpc_handler -> ('q, 'r) rpc -> do_:(('q, 'r) rpc_fn -> 'a) -> 'a option -end From ccc7798e58463b796761d3d1fa93b443a5e74af6 Mon Sep 17 00:00:00 2001 From: Nathan Holland Date: Wed, 21 Aug 2024 20:54:51 -0400 Subject: [PATCH 2/6] Fix Mina_stdlib List module --- src/lib/mina_stdlib/list.ml | 3 +++ src/lib/mina_stdlib/list.mli | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/lib/mina_stdlib/list.ml b/src/lib/mina_stdlib/list.ml index 5fa12faf383..c775f402385 100644 --- a/src/lib/mina_stdlib/list.ml +++ b/src/lib/mina_stdlib/list.ml @@ -1,3 +1,6 @@ +open Core_kernel +include List + module Length = struct type 'a t = ('a list, int) Sigs.predicate2 diff --git a/src/lib/mina_stdlib/list.mli b/src/lib/mina_stdlib/list.mli index 46a5a504e42..7296d0893e7 100644 --- a/src/lib/mina_stdlib/list.mli +++ b/src/lib/mina_stdlib/list.mli @@ -1,3 +1,7 @@ +open Core_kernel + +include module type of List + (** {1 Predicates over list lengths}*) module Length : sig From 7cf3c0ddbebee411ce7396510a8955e33f17cb7c Mon Sep 17 00:00:00 2001 From: Nathan Holland Date: Wed, 21 Aug 2024 21:21:11 -0400 Subject: [PATCH 3/6] Refactor Node_status networking interface (remove old RPC) --- src/app/cli/src/init/client.ml | 2 +- src/app/cli/src/init/dune | 1 - src/app/cli/src/init/mina_run.ml | 2 +- src/dune-project | 1 - src/lib/fake_network/fake_network.ml | 32 ++- src/lib/fake_network/fake_network.mli | 4 - src/lib/ledger_catchup/super_catchup.ml | 6 +- src/lib/mina_networking/mina_networking.ml | 38 ++-- src/lib/mina_networking/mina_networking.mli | 69 +------ src/lib/mina_networking/node_status.ml | 74 +++++++ src/lib/mina_networking/rpcs.ml | 215 -------------------- src/lib/node_status/dune | 18 -- src/lib/node_status/node_status.ml | 23 --- 13 files changed, 122 insertions(+), 363 deletions(-) create mode 100644 src/lib/mina_networking/node_status.ml delete mode 100644 src/lib/node_status/dune delete mode 100644 src/lib/node_status/node_status.ml diff --git a/src/app/cli/src/init/client.ml b/src/app/cli/src/init/client.ml index 5a53f6ba434..cdbe91106e7 100644 --- a/src/app/cli/src/init/client.ml +++ b/src/app/cli/src/init/client.ml @@ -1907,7 +1907,7 @@ let node_status = List.iter all_status_data ~f:(fun peer_status_data -> printf "%s\n%!" ( Yojson.Safe.to_string - @@ Mina_networking.Rpcs.Get_node_status.response_to_yojson + @@ Mina_networking.Node_status.response_to_yojson peer_status_data ) ) | Error err -> printf "Failed to get node status: %s\n%!" diff --git a/src/app/cli/src/init/dune b/src/app/cli/src/init/dune index cf38bfd4e5a..b215ac4d1a4 100644 --- a/src/app/cli/src/init/dune +++ b/src/app/cli/src/init/dune @@ -62,7 +62,6 @@ genesis_constants bash_colors graphql_lib - node_status o1trace_webkit_event generated_graphql_queries itn_crypto diff --git a/src/app/cli/src/init/mina_run.ml b/src/app/cli/src/init/mina_run.ml index e02e561a049..d4b31431b9d 100644 --- a/src/app/cli/src/init/mina_run.ml +++ b/src/app/cli/src/init/mina_run.ml @@ -353,7 +353,7 @@ let setup_local_server ?(client_trustlist = []) ?rest_server_port ; implement Daemon_rpcs.Get_trustlist.rpc (fun () () -> return (Set.to_list !client_trustlist) ) ; implement Daemon_rpcs.Get_node_status.rpc (fun () peers -> - Node_status.get_node_status_from_peers (Mina_lib.net mina) peers ) + Mina_networking.get_node_status_from_peers (Mina_lib.net mina) peers ) ; implement Daemon_rpcs.Get_object_lifetime_statistics.rpc (fun () () -> return (Yojson.Safe.pretty_to_string @@ Allocation_functor.Table.dump ()) ) diff --git a/src/dune-project b/src/dune-project index 3c54d84fe1a..5060ffc6fea 100644 --- a/src/dune-project +++ b/src/dune-project @@ -126,7 +126,6 @@ (package (name network_pool)) (package (name node_addrs_and_ports)) (package (name node_error_service)) -(package (name node_status)) (package (name node_status_service)) (package (name non_zero_curve_point)) (package (name o1trace)) diff --git a/src/lib/fake_network/fake_network.ml b/src/lib/fake_network/fake_network.ml index 05e5dc29348..eb6080b6025 100644 --- a/src/lib/fake_network/fake_network.ml +++ b/src/lib/fake_network/fake_network.ml @@ -148,10 +148,6 @@ include struct ( Rpcs.Get_transition_chain_proof.query , Rpcs.Get_transition_chain_proof.response ) Gossip_net.Fake.rpc_mock - -> ?get_node_status: - ( Rpcs.Get_node_status.query - , Rpcs.Get_node_status.response ) - Gossip_net.Fake.rpc_mock -> ?get_ancestry: ( Rpcs.Get_ancestry.query , Rpcs.Get_ancestry.response ) @@ -170,8 +166,8 @@ include struct fun ?get_some_initial_peers ?get_staged_ledger_aux_and_pending_coinbases_at_hash ?answer_sync_ledger_query ?get_transition_chain ?get_transition_knowledge - ?get_transition_chain_proof ?get_node_status ?get_ancestry ?get_best_tip - ~frontier ~consensus_local_state -> + ?get_transition_chain_proof ?get_ancestry ?get_best_tip ~frontier + ~consensus_local_state -> let rpc_mocks : Gossip_net.Fake.rpc_mocks = let get_mock (type q r) (rpc : (q, r) Rpcs.rpc) : (q, r) Gossip_net.Fake.rpc_mock option = @@ -188,8 +184,6 @@ include struct get_transition_knowledge | Get_transition_chain_proof -> get_transition_chain_proof - | Get_node_status -> - get_node_status | Get_ancestry -> get_ancestry | Ban_notify -> @@ -216,7 +210,7 @@ module Generator = struct let fresh_peer_custom_rpc ?get_some_initial_peers ?get_staged_ledger_aux_and_pending_coinbases_at_hash ?answer_sync_ledger_query ?get_transition_chain ?get_transition_knowledge - ?get_transition_chain_proof ?get_node_status ?get_ancestry ?get_best_tip + ?get_transition_chain_proof ?get_ancestry ?get_best_tip ~context:(module Context : CONTEXT) ~verifier ~max_frontier_length ~use_super_catchup = let open Context in @@ -242,24 +236,23 @@ module Generator = struct make_peer_state ~frontier ~consensus_local_state ?get_staged_ledger_aux_and_pending_coinbases_at_hash ?get_some_initial_peers ?answer_sync_ledger_query ?get_ancestry - ?get_best_tip ?get_node_status ?get_transition_knowledge - ?get_transition_chain_proof ?get_transition_chain + ?get_best_tip ?get_transition_knowledge ?get_transition_chain_proof + ?get_transition_chain let fresh_peer ~context:(module Context : CONTEXT) ~verifier ~max_frontier_length ~use_super_catchup = fresh_peer_custom_rpc ?get_staged_ledger_aux_and_pending_coinbases_at_hash:None ?get_some_initial_peers:None ?answer_sync_ledger_query:None - ?get_ancestry:None ?get_best_tip:None ?get_node_status:None - ?get_transition_knowledge:None ?get_transition_chain_proof:None - ?get_transition_chain:None + ?get_ancestry:None ?get_best_tip:None ?get_transition_knowledge:None + ?get_transition_chain_proof:None ?get_transition_chain:None ~context:(module Context) ~verifier ~max_frontier_length ~use_super_catchup let peer_with_branch_custom_rpc ~frontier_branch_size ?get_some_initial_peers ?get_staged_ledger_aux_and_pending_coinbases_at_hash ?answer_sync_ledger_query ?get_transition_chain ?get_transition_knowledge - ?get_transition_chain_proof ?get_node_status ?get_ancestry ?get_best_tip + ?get_transition_chain_proof ?get_ancestry ?get_best_tip ~context:(module Context : CONTEXT) ~verifier ~max_frontier_length ~use_super_catchup = let open Context in @@ -290,17 +283,16 @@ module Generator = struct make_peer_state ~frontier ~consensus_local_state ?get_staged_ledger_aux_and_pending_coinbases_at_hash ?get_some_initial_peers ?answer_sync_ledger_query ?get_ancestry - ?get_best_tip ?get_node_status ?get_transition_knowledge - ?get_transition_chain_proof ?get_transition_chain + ?get_best_tip ?get_transition_knowledge ?get_transition_chain_proof + ?get_transition_chain let peer_with_branch ~frontier_branch_size ~context:(module Context : CONTEXT) ~verifier ~max_frontier_length ~use_super_catchup = peer_with_branch_custom_rpc ~frontier_branch_size ?get_staged_ledger_aux_and_pending_coinbases_at_hash:None ?get_some_initial_peers:None ?answer_sync_ledger_query:None - ?get_ancestry:None ?get_best_tip:None ?get_node_status:None - ?get_transition_knowledge:None ?get_transition_chain_proof:None - ?get_transition_chain:None + ?get_ancestry:None ?get_best_tip:None ?get_transition_knowledge:None + ?get_transition_chain_proof:None ?get_transition_chain:None ~context:(module Context) ~verifier ~max_frontier_length ~use_super_catchup diff --git a/src/lib/fake_network/fake_network.mli b/src/lib/fake_network/fake_network.mli index 556b490a4e6..cee6df672f8 100644 --- a/src/lib/fake_network/fake_network.mli +++ b/src/lib/fake_network/fake_network.mli @@ -61,10 +61,6 @@ include sig ( Rpcs.Get_transition_chain_proof.query , Rpcs.Get_transition_chain_proof.response ) Gossip_net.Fake.rpc_mock - -> ?get_node_status: - ( Rpcs.Get_node_status.query - , Rpcs.Get_node_status.response ) - Gossip_net.Fake.rpc_mock -> ?get_ancestry: ( Rpcs.Get_ancestry.query , Rpcs.Get_ancestry.response ) diff --git a/src/lib/ledger_catchup/super_catchup.ml b/src/lib/ledger_catchup/super_catchup.ml index 5370e72d3a9..894a62c7de1 100644 --- a/src/lib/ledger_catchup/super_catchup.ml +++ b/src/lib/ledger_catchup/super_catchup.ml @@ -1755,21 +1755,21 @@ let%test_module "Ledger_catchup tests" = ~frontier_branch_size:(max_frontier_length / 2) ?get_staged_ledger_aux_and_pending_coinbases_at_hash:None ?get_some_initial_peers:None ?answer_sync_ledger_query:None - ?get_ancestry:None ?get_best_tip:None ?get_node_status:None + ?get_ancestry:None ?get_best_tip:None ?get_transition_knowledge:None ?get_transition_chain_proof:None ?get_transition_chain:(Some impl_rpc) ; peer_with_branch_custom_rpc ~frontier_branch_size:(max_frontier_length / 2) ?get_staged_ledger_aux_and_pending_coinbases_at_hash:None ?get_some_initial_peers:None ?answer_sync_ledger_query:None - ?get_ancestry:None ?get_best_tip:None ?get_node_status:None + ?get_ancestry:None ?get_best_tip:None ?get_transition_knowledge:None ?get_transition_chain_proof:None ?get_transition_chain:(Some impl_rpc) ; peer_with_branch_custom_rpc ~frontier_branch_size:(max_frontier_length / 2) ?get_staged_ledger_aux_and_pending_coinbases_at_hash:None ?get_some_initial_peers:None ?answer_sync_ledger_query:None - ?get_ancestry:None ?get_best_tip:None ?get_node_status:None + ?get_ancestry:None ?get_best_tip:None ?get_transition_knowledge:None ?get_transition_chain_proof:None ?get_transition_chain:(Some impl_rpc) ]) diff --git a/src/lib/mina_networking/mina_networking.ml b/src/lib/mina_networking/mina_networking.ml index 04dd9269d60..15dbc9ac89b 100644 --- a/src/lib/mina_networking/mina_networking.ml +++ b/src/lib/mina_networking/mina_networking.ml @@ -1,6 +1,7 @@ open Core open Async open Mina_base +open Mina_stdlib module Sync_ledger = Mina_ledger.Sync_ledger open Network_peer open Network_pool @@ -43,10 +44,10 @@ module type CONTEXT = sig val consensus_constants : Consensus.Constants.t end -module Sinks = Sinks +module Node_status = Node_status module Rpcs = Rpcs +module Sinks = Sinks module Gossip_net = Gossip_net.Make (Rpcs) -module Node_status = Rpcs.Get_node_status.Node_status module Config = struct type log_gossip_heard = @@ -71,9 +72,7 @@ type t = let create (module Context : CONTEXT) (config : Config.t) ~sinks ~(get_transition_frontier : unit -> Transition_frontier.t option) - ~(get_node_status : - Rpcs.Get_node_status.query Envelope.Incoming.t - -> Rpcs.Get_node_status.response Deferred.t ) = + ~(get_node_status : unit -> Node_status.t Deferred.Or_error.t) = let open Context in let gossip_net_ref = ref None in let module Rpc_context = struct @@ -100,19 +99,14 @@ let create (module Context : CONTEXT) (config : Config.t) ~sinks (* The node status RPC is implemented directly in go, serving a string which is periodically updated. This is so that one can make this RPC on a node even if that node is at its connection limit. *) - let fake_time = Time.now () in Clock.every' (Time.Span.of_min 1.) (fun () -> O1trace.thread "update_node_status" (fun () -> - match%bind - get_node_status - { data = (); sender = Local; received_at = fake_time } - with + match%bind get_node_status () with | Error _ -> Deferred.unit | Ok data -> Gossip_net.Any.set_node_status gossip_net - ( Rpcs.Get_node_status.Node_status.to_yojson data - |> Yojson.Safe.to_string ) + (Node_status.to_yojson data |> Yojson.Safe.to_string) >>| ignore ) ) ; don't_wait_for (Gossip_net.Any.on_first_connect gossip_net ~f:(fun () -> @@ -145,9 +139,7 @@ include struct let open Deferred.Or_error.Let_syntax in let%bind s = get_peer_node_status t.gossip_net peer in Or_error.try_with (fun () -> - match - Rpcs.Get_node_status.Node_status.of_yojson (Yojson.Safe.from_string s) - with + match Node_status.of_yojson (Yojson.Safe.from_string s) with | Ok x -> x | Error e -> @@ -181,6 +173,22 @@ include struct lift (set_connection_gating ?clean_added_peers) t config end +let get_node_status_from_peers (t : t) + (addrs : Mina_net2.Multiaddr.t list option) = + let run = Deferred.List.map ~how:`Parallel ~f:(get_peer_node_status t) in + match addrs with + | None -> + peers t >>= run + | Some addrs -> ( + match Option.all (List.map ~f:Mina_net2.Multiaddr.to_peer addrs) with + | Some peers -> + run peers + | None -> + Deferred.return + (List.map addrs ~f:(fun _ -> + Or_error.error_string + "Could not parse peers in node status request" ) ) ) + (* TODO: Have better pushback behavior *) let broadcast_state t state = [%str_log' trace t.logger] diff --git a/src/lib/mina_networking/mina_networking.mli b/src/lib/mina_networking/mina_networking.mli index 50fc7040188..d53ae824086 100644 --- a/src/lib/mina_networking/mina_networking.mli +++ b/src/lib/mina_networking/mina_networking.mli @@ -31,56 +31,11 @@ module type CONTEXT = sig val consensus_constants : Consensus.Constants.t end -module Sinks : module type of Sinks +module Node_status = Node_status +module Sinks = Sinks module Gossip_net : Gossip_net.S with module Rpc_interface := Rpcs -module Node_status : sig - [%%versioned: - module Stable : sig - module V2 : sig - type t = Rpcs.Get_node_status.Node_status.Stable.V2.t = - { node_ip_addr : Network_peer.Peer.Inet_addr.Stable.V1.t - ; node_peer_id : Network_peer.Peer.Id.Stable.V1.t - ; sync_status : Sync_status.Stable.V1.t - ; peers : Network_peer.Peer.Stable.V1.t list - ; block_producers : Signature_lib.Public_key.Compressed.Stable.V1.t list - ; protocol_state_hash : State_hash.Stable.V1.t - ; ban_statuses : - ( Network_peer.Peer.Stable.V1.t - * Trust_system.Peer_status.Stable.V1.t ) - list - ; k_block_hashes_and_timestamps : - (State_hash.Stable.V1.t * Bounded_types.String.Stable.V1.t) list - ; git_commit : Bounded_types.String.Stable.V1.t - ; uptime_minutes : int - ; block_height_opt : int option - } - [@@deriving bin_io, yojson] - end - - module V1 : sig - type t = Rpcs.Get_node_status.Node_status.Stable.V1.t = - { node_ip_addr : Network_peer.Peer.Inet_addr.Stable.V1.t - ; node_peer_id : Network_peer.Peer.Id.Stable.V1.t - ; sync_status : Sync_status.Stable.V1.t - ; peers : Network_peer.Peer.Stable.V1.t list - ; block_producers : Signature_lib.Public_key.Compressed.Stable.V1.t list - ; protocol_state_hash : State_hash.Stable.V1.t - ; ban_statuses : - ( Network_peer.Peer.Stable.V1.t - * Trust_system.Peer_status.Stable.V1.t ) - list - ; k_block_hashes_and_timestamps : - (State_hash.Stable.V1.t * Bounded_types.String.Stable.V1.t) list - ; git_commit : Bounded_types.String.Stable.V1.t - ; uptime_minutes : int - } - [@@deriving bin_io, yojson] - end - end] -end - module Rpcs : sig module Get_some_initial_peers : sig type query = unit @@ -124,16 +79,6 @@ module Rpcs : sig type response = State_hash.t list end - module Get_node_status : sig - type query = unit - - type response = - ( Node_status.Stable.Latest.t - , Bounded_types.Wrapped_error.Stable.V1.t ) - result - [@@deriving to_yojson] - end - module Get_ancestry : sig type query = (Consensus.Data.Consensus_state.Value.t, State_hash.t) With_hash.t @@ -183,7 +128,6 @@ module Rpcs : sig : ( Get_transition_chain_proof.query , Get_transition_chain_proof.response ) rpc - | Get_node_status : (Get_node_status.query, Get_node_status.response) rpc | Get_ancestry : (Get_ancestry.query, Get_ancestry.response) rpc | Ban_notify : (Ban_notify.query, Ban_notify.response) rpc | Get_best_tip : (Get_best_tip.query, Get_best_tip.response) rpc @@ -215,6 +159,11 @@ val bandwidth_info : val get_peer_node_status : t -> Network_peer.Peer.t -> Node_status.t Deferred.Or_error.t +val get_node_status_from_peers : + t + -> Mina_net2.Multiaddr.t list option + -> Node_status.t Or_error.t list Deferred.t + val add_peer : t -> Network_peer.Peer.t -> is_seed:bool -> unit Deferred.Or_error.t @@ -316,7 +265,5 @@ val create : -> Config.t -> sinks:Sinks.t -> get_transition_frontier:(unit -> Transition_frontier.t option) - -> get_node_status: - ( Rpcs.Get_node_status.query Envelope.Incoming.t - -> Rpcs.Get_node_status.response Deferred.t ) + -> get_node_status:(unit -> Node_status.t Deferred.Or_error.t) -> t Deferred.t diff --git a/src/lib/mina_networking/node_status.ml b/src/lib/mina_networking/node_status.ml new file mode 100644 index 00000000000..ceb6bc9fe42 --- /dev/null +++ b/src/lib/mina_networking/node_status.ml @@ -0,0 +1,74 @@ +open Core_kernel +open Mina_base +open Network_peer + +[%%versioned +module Stable = struct + module V2 = struct + type t = + { node_ip_addr : Peer.Inet_addr.Stable.V1.t + ; node_peer_id : Peer.Id.Stable.V1.t + [@to_yojson fun peer_id -> `String peer_id] + [@of_yojson + function `String s -> Ok s | _ -> Error "expected string"] + ; sync_status : Sync_status.Stable.V1.t + ; peers : Peer.Stable.V1.t list + ; block_producers : Signature_lib.Public_key.Compressed.Stable.V1.t list + ; protocol_state_hash : State_hash.Stable.V1.t + ; ban_statuses : + (Peer.Stable.V1.t * Trust_system.Peer_status.Stable.V1.t) list + ; k_block_hashes_and_timestamps : + (State_hash.Stable.V1.t * Bounded_types.String.Stable.V1.t) list + ; git_commit : Bounded_types.String.Stable.V1.t + ; uptime_minutes : int + ; block_height_opt : int option [@default None] + } + [@@deriving to_yojson, of_yojson] + + let to_latest = Fn.id + end + + module V1 = struct + type t = + { node_ip_addr : Peer.Inet_addr.Stable.V1.t + ; node_peer_id : Peer.Id.Stable.V1.t + [@to_yojson fun peer_id -> `String peer_id] + [@of_yojson + function `String s -> Ok s | _ -> Error "expected string"] + ; sync_status : Sync_status.Stable.V1.t + ; peers : Peer.Stable.V1.t list + ; block_producers : Signature_lib.Public_key.Compressed.Stable.V1.t list + ; protocol_state_hash : State_hash.Stable.V1.t + ; ban_statuses : + (Peer.Stable.V1.t * Trust_system.Peer_status.Stable.V1.t) list + ; k_block_hashes_and_timestamps : + (State_hash.Stable.V1.t * Bounded_types.String.Stable.V1.t) list + ; git_commit : Bounded_types.String.Stable.V1.t + ; uptime_minutes : int + } + [@@deriving to_yojson, of_yojson] + + let to_latest status : Latest.t = + { node_ip_addr = status.node_ip_addr + ; node_peer_id = status.node_peer_id + ; sync_status = status.sync_status + ; peers = status.peers + ; block_producers = status.block_producers + ; protocol_state_hash = status.protocol_state_hash + ; ban_statuses = status.ban_statuses + ; k_block_hashes_and_timestamps = status.k_block_hashes_and_timestamps + ; git_commit = status.git_commit + ; uptime_minutes = status.uptime_minutes + ; block_height_opt = None + } + end +end] + +type response = (t, Error.t) result + +let response_to_yojson (response : response) : Yojson.Safe.t = + match response with + | Ok status -> + to_yojson status + | Error err -> + `Assoc [ ("error", Error_json.error_to_yojson err) ] diff --git a/src/lib/mina_networking/rpcs.ml b/src/lib/mina_networking/rpcs.ml index 01467fe022c..a752aff25b2 100644 --- a/src/lib/mina_networking/rpcs.ml +++ b/src/lib/mina_networking/rpcs.ml @@ -756,217 +756,6 @@ module Get_transition_chain_proof = struct let rate_limit_cost = Fn.const 1 end] -(* TODO: remove this unused RPC *) -[%%versioned_rpc -module Get_node_status = struct - type nonrec ctx = ctx - - module Node_status = struct - [%%versioned - module Stable = struct - module V2 = struct - type t = - { node_ip_addr : Network_peer.Peer.Inet_addr.Stable.V1.t - ; node_peer_id : Network_peer.Peer.Id.Stable.V1.t - [@to_yojson fun peer_id -> `String peer_id] - [@of_yojson - function `String s -> Ok s | _ -> Error "expected string"] - ; sync_status : Sync_status.Stable.V1.t - ; peers : Network_peer.Peer.Stable.V1.t list - ; block_producers : - Signature_lib.Public_key.Compressed.Stable.V1.t list - ; protocol_state_hash : State_hash.Stable.V1.t - ; ban_statuses : - ( Network_peer.Peer.Stable.V1.t - * Trust_system.Peer_status.Stable.V1.t ) - list - ; k_block_hashes_and_timestamps : - (State_hash.Stable.V1.t * Bounded_types.String.Stable.V1.t) list - ; git_commit : Bounded_types.String.Stable.V1.t - ; uptime_minutes : int - ; block_height_opt : int option [@default None] - } - [@@deriving to_yojson, of_yojson] - - let to_latest = Fn.id - end - - module V1 = struct - type t = - { node_ip_addr : Network_peer.Peer.Inet_addr.Stable.V1.t - ; node_peer_id : Network_peer.Peer.Id.Stable.V1.t - [@to_yojson fun peer_id -> `String peer_id] - [@of_yojson - function `String s -> Ok s | _ -> Error "expected string"] - ; sync_status : Sync_status.Stable.V1.t - ; peers : Network_peer.Peer.Stable.V1.t list - ; block_producers : - Signature_lib.Public_key.Compressed.Stable.V1.t list - ; protocol_state_hash : State_hash.Stable.V1.t - ; ban_statuses : - ( Network_peer.Peer.Stable.V1.t - * Trust_system.Peer_status.Stable.V1.t ) - list - ; k_block_hashes_and_timestamps : - (State_hash.Stable.V1.t * Bounded_types.String.Stable.V1.t) list - ; git_commit : Bounded_types.String.Stable.V1.t - ; uptime_minutes : int - } - [@@deriving to_yojson, of_yojson] - - let to_latest status : Latest.t = - { node_ip_addr = status.node_ip_addr - ; node_peer_id = status.node_peer_id - ; sync_status = status.sync_status - ; peers = status.peers - ; block_producers = status.block_producers - ; protocol_state_hash = status.protocol_state_hash - ; ban_statuses = status.ban_statuses - ; k_block_hashes_and_timestamps = status.k_block_hashes_and_timestamps - ; git_commit = status.git_commit - ; uptime_minutes = status.uptime_minutes - ; block_height_opt = None - } - end - end] - end - - module Master = struct - let name = "get_node_status" - - module T = struct - type query = unit [@@deriving sexp, to_yojson] - - type response = - (Node_status.t, Bounded_types.Wrapped_error.Stable.V1.t) result - end - - module Caller = T - module Callee = T - end - - include Master.T - - let sent_counter = Mina_metrics.Network.get_node_status_rpcs_sent - - let received_counter = Mina_metrics.Network.get_node_status_rpcs_received - - let failed_request_counter = - Mina_metrics.Network.get_node_status_rpc_requests_failed - - let failed_response_counter = - Mina_metrics.Network.get_node_status_rpc_responses_failed - - module M = Versioned_rpc.Both_convert.Plain.Make (Master) - include M - - let response_to_yojson response = - match response with - | Ok status -> - Node_status.Stable.Latest.to_yojson status - | Error err -> - `Assoc [ ("error", Error_json.error_to_yojson err) ] - - include Perf_histograms.Rpc.Plain.Extend (struct - include M - include Master - end) - - module V2 = struct - module T = struct - type query = unit [@@deriving sexp] - - type response = - (( Node_status.Stable.V2.t - , Bounded_types.Wrapped_error.Stable.V1.t ) - Result.t - [@version_asserted] ) - - let query_of_caller_model = Fn.id - - let callee_model_of_query = Fn.id - - let response_of_callee_model = Fn.id - - let caller_model_of_response = Fn.id - end - - module T' = - Perf_histograms.Rpc.Plain.Decorate_bin_io - (struct - include M - include Master - end) - (T) - - include T' - include Register (T') - end - - module V1 = struct - module T = struct - type query = unit [@@deriving sexp] - - type response = - (( Node_status.Stable.V1.t - , Bounded_types.Wrapped_error.Stable.V1.t ) - Core_kernel.Result.t - [@version_asserted] ) - - let query_of_caller_model = Fn.id - - let callee_model_of_query = Fn.id - - let response_of_callee_model = function - | Error err -> - Error err - | Ok (status : Node_status.Stable.Latest.t) -> - Ok - { Node_status.Stable.V1.node_ip_addr = status.node_ip_addr - ; node_peer_id = status.node_peer_id - ; sync_status = status.sync_status - ; peers = status.peers - ; block_producers = status.block_producers - ; protocol_state_hash = status.protocol_state_hash - ; ban_statuses = status.ban_statuses - ; k_block_hashes_and_timestamps = - status.k_block_hashes_and_timestamps - ; git_commit = status.git_commit - ; uptime_minutes = status.uptime_minutes - } - - let caller_model_of_response = function - | Error err -> - Error err - | Ok (status : Node_status.Stable.V1.t) -> - Ok (Node_status.Stable.V1.to_latest status) - end - - module T' = - Perf_histograms.Rpc.Plain.Decorate_bin_io - (struct - include M - include Master - end) - (T) - - include T' - include Register (T') - end - - let receipt_trust_action_message _request = failwith "TODO" - - let log_request_received ~logger:_ ~sender:_ _request = failwith "TODO" - - let response_is_successful _response = failwith "TODO" - - let handle_request _ctx ~version:_ _request = failwith "TODO" - - let rate_limit_budget = (1, `Per Time.Span.second) - - let rate_limit_cost _ = failwith "TODO" -end] - [%%versioned_rpc module Get_ancestry = struct type nonrec ctx = ctx @@ -1311,14 +1100,12 @@ type ('query, 'response) rpc = : ( Get_transition_chain_proof.query , Get_transition_chain_proof.response ) rpc - | Get_node_status : (Get_node_status.query, Get_node_status.response) rpc | Get_ancestry : (Get_ancestry.query, Get_ancestry.response) rpc | Ban_notify : (Ban_notify.query, Ban_notify.response) rpc | Get_best_tip : (Get_best_tip.query, Get_best_tip.response) rpc type any_rpc = Rpc : ('q, 'r) rpc -> any_rpc -(* TODO: we are intentially omitting Get_node_status from this list, since it wasn't being registered previously *) let all_rpcs = [ Rpc Get_some_initial_peers ; Rpc Get_staged_ledger_aux_and_pending_coinbases_at_hash @@ -1345,8 +1132,6 @@ let implementation : (module Get_transition_knowledge) | Get_transition_chain_proof -> (module Get_transition_chain_proof) - | Get_node_status -> - (module Get_node_status) | Get_ancestry -> (module Get_ancestry) | Ban_notify -> diff --git a/src/lib/node_status/dune b/src/lib/node_status/dune deleted file mode 100644 index f6c2dd08b94..00000000000 --- a/src/lib/node_status/dune +++ /dev/null @@ -1,18 +0,0 @@ -(library - (name node_status) - (public_name node_status) - (library_flags -linkall) - (libraries - ;; opam libraries - core - core_kernel - async - async_kernel - ;; local libraries - mina_net2 - mina_networking) - (preprocess - (pps ppx_version ppx_compare ppx_deriving.enum ppx_deriving.ord - ppx_base ppx_bench ppx_let ppx_sexp_conv ppx_bin_prot)) - (instrumentation (backend bisect_ppx)) - (synopsis "Get node status from other nodes")) diff --git a/src/lib/node_status/node_status.ml b/src/lib/node_status/node_status.ml deleted file mode 100644 index 103e79b3c99..00000000000 --- a/src/lib/node_status/node_status.ml +++ /dev/null @@ -1,23 +0,0 @@ -(* node_status.ml *) - -open Core -open Async - -let get_node_status_from_peers (net : Mina_networking.t) - (peers : Mina_net2.Multiaddr.t list option) = - let run = - Deferred.List.map ~how:`Parallel - ~f:(Mina_networking.get_peer_node_status net) - in - match peers with - | None -> - Mina_networking.peers net >>= run - | Some peers -> ( - match Option.all (List.map ~f:Mina_net2.Multiaddr.to_peer peers) with - | Some peers -> - run peers - | None -> - Deferred.return - (List.map peers ~f:(fun _ -> - Or_error.error_string - "Could not parse peers in node status request" ) ) ) From b42e93d47e9de67f0dda552fac63876bcdb79511 Mon Sep 17 00:00:00 2001 From: Nathan Holland Date: Wed, 21 Aug 2024 21:41:56 -0400 Subject: [PATCH 4/6] Enforce trust actions for received rpc requests --- src/lib/gossip_net/intf.ml | 3 +-- src/lib/gossip_net/libp2p.ml | 14 ++++------ src/lib/mina_networking/rpcs.ml | 48 ++++++++++++++++----------------- 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/lib/gossip_net/intf.ml b/src/lib/gossip_net/intf.ml index 59dec2c1141..b291abe24f5 100644 --- a/src/lib/gossip_net/intf.ml +++ b/src/lib/gossip_net/intf.ml @@ -46,9 +46,8 @@ module type RPC_IMPLEMENTATION = sig val log_request_received : logger:Logger.t -> sender:Peer.t -> query -> unit - (* TODO: make this non-optional *) val receipt_trust_action_message : - query -> (string * (string, Yojson.Safe.t) List.Assoc.t) option + query -> string * (string, Yojson.Safe.t) List.Assoc.t val handle_request : ctx -> (query, response) rpc_fn diff --git a/src/lib/gossip_net/libp2p.ml b/src/lib/gossip_net/libp2p.ml index 2bdf77364bf..aa0991e0db6 100644 --- a/src/lib/gossip_net/libp2p.ml +++ b/src/lib/gossip_net/libp2p.ml @@ -174,15 +174,11 @@ module Make (Rpc_interface : RPC_INTERFACE) : in let sender = Envelope.Incoming.sender request_env in let%bind () = - (* TODO: don't have optional rpc actions (only needed because Ban_notify doesn't log an action) *) - match Impl.receipt_trust_action_message request with - | None -> - return () - | Some msg -> - (* TODO: kill trust system *) - Trust_system.( - record_envelope_sender trust_system logger sender - Actions.(Made_request, Some msg)) + let msg = Impl.receipt_trust_action_message request in + (* TODO: kill trust system *) + Trust_system.( + record_envelope_sender trust_system logger sender + Actions.(Made_request, Some msg)) in let%map response = (* TODO: kill this additional thread *) diff --git a/src/lib/mina_networking/rpcs.ml b/src/lib/mina_networking/rpcs.ml index a752aff25b2..9bd8058ddf2 100644 --- a/src/lib/mina_networking/rpcs.ml +++ b/src/lib/mina_networking/rpcs.ml @@ -187,7 +187,7 @@ module Get_some_initial_peers = struct include Register (T') end - let receipt_trust_action_message _ = Some ("Get_some_initial_peers query", []) + let receipt_trust_action_message _ = ("Get_some_initial_peers query", []) let log_request_received ~logger ~sender () = [%log trace] "Sending some initial peers to $peer" @@ -284,9 +284,8 @@ module Get_staged_ledger_aux_and_pending_coinbases_at_hash = struct end let receipt_trust_action_message hash = - Some - ( "Staged ledger and pending coinbases at hash: $hash" - , [ ("hash", State_hash.to_yojson hash) ] ) + ( "Staged ledger and pending coinbases at hash: $hash" + , [ ("hash", State_hash.to_yojson hash) ] ) let log_request_received ~logger:_ ~sender:_ _request = () @@ -322,7 +321,9 @@ module Get_staged_ledger_aux_and_pending_coinbases_at_hash = struct Trust_system.( record_envelope_sender trust_system logger (Envelope.Incoming.sender request) - Actions.(Requested_unknown_item, receipt_trust_action_message hash)) + Actions. + ( Requested_unknown_item + , Some (receipt_trust_action_message hash) )) in result @@ -407,9 +408,8 @@ module Answer_sync_ledger_query = struct end let receipt_trust_action_message (_, query) = - Some - ( "Answer_sync_ledger_query: $query" - , [ ("query", Sync_ledger.Query.to_yojson query) ] ) + ( "Answer_sync_ledger_query: $query" + , [ ("query", Sync_ledger.Query.to_yojson query) ] ) let log_request_received ~logger:_ ~sender:_ _request = () @@ -527,9 +527,7 @@ module Get_transition_chain = struct end let receipt_trust_action_message query = - Some - ( "Get_transition_chain query: $query" - , [ ("query", query_to_yojson query) ] ) + ("Get_transition_chain query: $query", [ ("query", query_to_yojson query) ]) let log_request_received ~logger ~sender _request = [%log info] "Sending transition_chain to $peer" @@ -559,7 +557,8 @@ module Get_transition_chain = struct record_envelope_sender trust_system logger (Envelope.Incoming.sender request) Actions. - (Requested_unknown_item, receipt_trust_action_message hashes)) + ( Requested_unknown_item + , Some (receipt_trust_action_message hashes) )) in None @@ -634,9 +633,8 @@ module Get_transition_knowledge = struct end let receipt_trust_action_message query = - Some - ( "Get_transition_knowledge query: $query" - , [ ("query", query_to_yojson query) ] ) + ( "Get_transition_knowledge query: $query" + , [ ("query", query_to_yojson query) ] ) let log_request_received ~logger ~sender _request = [%log info] "Sending transition_knowledge to $peer" @@ -724,9 +722,8 @@ module Get_transition_chain_proof = struct end let receipt_trust_action_message query = - Some - ( "Get_transition_chain_proof query: $query" - , [ ("query", query_to_yojson query) ] ) + ( "Get_transition_chain_proof query: $query" + , [ ("query", query_to_yojson query) ] ) let log_request_received ~logger ~sender _request = [%log info] "Sending transition_chain_proof to $peer" @@ -746,7 +743,8 @@ module Get_transition_chain_proof = struct Trust_system.( record_envelope_sender trust_system logger (Envelope.Incoming.sender request) - Actions.(Requested_unknown_item, receipt_trust_action_message hash)) + Actions. + (Requested_unknown_item, Some (receipt_trust_action_message hash))) else return () in result @@ -836,7 +834,7 @@ module Get_ancestry = struct end let receipt_trust_action_message query = - Some ("Get_ancestry query: $query", [ ("query", query_to_yojson query) ]) + ("Get_ancestry query: $query", [ ("query", query_to_yojson query) ]) let log_request_received ~logger ~sender _request = [%log debug] "Sending root proof to $peer" @@ -862,7 +860,8 @@ module Get_ancestry = struct (Envelope.Incoming.sender request) Actions. ( Requested_unknown_item - , receipt_trust_action_message consensus_state_with_hash )) + , Some (receipt_trust_action_message consensus_state_with_hash) + )) in None | Some { proof = _, block; _ } -> @@ -944,7 +943,7 @@ module Ban_notify = struct include Register (T') end - let receipt_trust_action_message _request = None + let receipt_trust_action_message _request = ("Ban_notify query", []) let log_request_received ~logger ~sender ban_until = [%log warn] "Node banned by peer $peer until $ban_until" @@ -1035,7 +1034,7 @@ module Get_best_tip = struct include Register (T') end - let receipt_trust_action_message _request = Some ("Get_best_tip query", []) + let receipt_trust_action_message _request = ("Get_best_tip query", []) let log_request_received ~logger ~sender _request = [%log debug] "Sending best_tip to $peer" @@ -1061,7 +1060,8 @@ module Get_best_tip = struct Trust_system.( record_envelope_sender trust_system logger (Envelope.Incoming.sender request) - Actions.(Requested_unknown_item, receipt_trust_action_message ())) + Actions. + (Requested_unknown_item, Some (receipt_trust_action_message ()))) in None | Some { data = data_block; proof = _, proof_block } -> From 0124bcd30c87cbbe6a2ae557bfb5182028933392 Mon Sep 17 00:00:00 2001 From: Nathan Holland Date: Thu, 22 Aug 2024 15:08:16 -0400 Subject: [PATCH 5/6] Fix misc TODOs --- src/lib/gossip_net/libp2p.ml | 15 ++++------ src/lib/mina_networking/rpcs.ml | 30 ++++++++----------- src/lib/network_peer/network_peer.ml | 14 --------- .../proof_carrying_data.ml | 2 +- 4 files changed, 18 insertions(+), 43 deletions(-) delete mode 100644 src/lib/network_peer/network_peer.ml diff --git a/src/lib/gossip_net/libp2p.ml b/src/lib/gossip_net/libp2p.ml index aa0991e0db6..a99ad2bc5c1 100644 --- a/src/lib/gossip_net/libp2p.ml +++ b/src/lib/gossip_net/libp2p.ml @@ -136,20 +136,17 @@ module Make (Rpc_interface : RPC_INTERFACE) : } (* TODO: should we share this with the Fake network impl? *) - let setup_rpc (type query response) ctx trust_system + let setup_rpc (type query response) ctx logger trust_system (rpc : (query, response) Rpc_interface.rpc) = let (module Impl) = Rpc_interface.implementation rpc in - (* TODO: should be passed in from the config.logger instance *) - let logger = Logger.create () in let log_rate_limiter_occasionally rl = let t = Time.Span.of_min 1. in every t (fun () -> - [%log' debug logger] + [%log debug] ~metadata: [ ("rate_limiter", Network_pool.Rate_limiter.summary rl) ] !"%s $rate_limiter" Impl.name ) in - (* TODO: kill rate limit *) let rl = Network_pool.Rate_limiter.create ~capacity:Impl.rate_limit_budget in @@ -175,15 +172,13 @@ module Make (Rpc_interface : RPC_INTERFACE) : let sender = Envelope.Incoming.sender request_env in let%bind () = let msg = Impl.receipt_trust_action_message request in - (* TODO: kill trust system *) + (* TODO: kill trust system (#11723) *) Trust_system.( record_envelope_sender trust_system logger sender Actions.(Made_request, Some msg)) in let%map response = - (* TODO: kill this additional thread *) - O1trace.thread ("handle_request_" ^ Impl.name) (fun () -> - Impl.handle_request ctx ~version request_env ) + Impl.handle_request ctx ~version request_env in if not (Impl.response_is_successful response) then Mina_metrics.Counter.inc_one Impl.failed_response_counter ; @@ -356,7 +351,7 @@ module Make (Rpc_interface : RPC_INTERFACE) : in let implementation_list = List.bind Rpc_interface.all_rpcs ~f:(fun (Rpc rpc) -> - setup_rpc ctx config.trust_system rpc ) + setup_rpc ctx config.logger config.trust_system rpc ) in let implementations = let handle_unknown_rpc conn_state ~rpc_tag ~version = diff --git a/src/lib/mina_networking/rpcs.ml b/src/lib/mina_networking/rpcs.ml index 9bd8058ddf2..9037b2843e6 100644 --- a/src/lib/mina_networking/rpcs.ml +++ b/src/lib/mina_networking/rpcs.ml @@ -18,7 +18,6 @@ open Network_peer (* The common context passed into all rpc handlers. Add new things here to get them into the scope of an rpc handler. Notably, calls back into Gossip_net need to be expicitly wrapped at this layer in order to solve a recursive dependency between Gossip_net.Make and this module. *) -(* TODO: we could fix the Gossip_net dependency loop with a better interface. *) module type CONTEXT = sig val logger : Logger.t @@ -72,8 +71,8 @@ let validate_protocol_versions ~logger ~trust_system ~rpc_name ~sender blocks = (`Current_version_mismatch, x) ) in let%map () = - (* TODO: these errors aren't always accurate... sometimes we are calling this when we were - requested to serve an outdated block (requested vs sent) *) + (* NB: these errors aren't always accurate... sometimes we are calling this when we were + requested to serve an outdated block (requested vs sent) *) Deferred.List.iter version_errors ~how:`Parallel ~f:(fun (version_error, block) -> let header = Mina_block.header block in @@ -84,28 +83,27 @@ let validate_protocol_versions ~logger ~trust_system ~rpc_name ~sender blocks = Mina_block.Header.proposed_protocol_version_opt header in let action, error_msg, error_metadata = - (* TODO: update confusing metadata names *) match version_error with | `Invalid_current_version -> ( Trust_system.Actions.Sent_invalid_protocol_version - , "external transition with invalid current protocol version" - , [ ( "current_protocol_version" + , "block with invalid current protocol version" + , [ ( "block_current_protocol_version" , `String (Protocol_version.to_string block_protocol_version) ) ] ) | `Invalid_next_version -> ( Trust_system.Actions.Sent_invalid_protocol_version - , "external transition with invalid proposed protocol version" - , [ ( "proposed_protocol_version" + , "block with invalid proposed protocol version" + , [ ( "block_proposed_protocol_version" , `String (Protocol_version.to_string (Option.value_exn proposed_protocol_version) ) ) ] ) | `Current_version_mismatch -> ( Sent_mismatched_protocol_version - , "current protocol version in external transition does not \ - match daemon current protocol version" - , [ ( "current_protocol_version" + , "current protocol version in block does not match daemon \ + current protocol version" + , [ ( "block_current_protocol_version" , `String (Protocol_version.to_string block_protocol_version) ) ; ( "daemon_current_protocol_version" @@ -295,7 +293,6 @@ module Get_staged_ledger_aux_and_pending_coinbases_at_hash = struct let open Context in let hash = Envelope.Incoming.data request in let result = - (* TODO: failure to access frontier should result in different trust system behavior *) let%bind.Option frontier = get_transition_frontier () in Sync_handler.get_staged_ledger_aux_and_pending_coinbases_at_hash ~frontier hash @@ -425,13 +422,11 @@ module Answer_sync_ledger_query = struct ~trust_system in let result = - (* TODO: should we really be returning an error for this RPC if there is only 1 kind of error? - (we could just wrap the error on the client side) *) Result.of_option answer ~error: (Error.createf - (* TODO: improve weird error message *) - !"Refused to answer_query for ledger_hash: %{sexp:Ledger_hash.t}" + !"Refusing to answer sync ledger query for ledger_hash: \ + %{sexp:Ledger_hash.t}" ledger_hash ) in let%map () = @@ -1051,8 +1046,7 @@ module Get_best_tip = struct Best_tip_prover.prove ~context:(module Context) frontier in (* strip hash from proof data *) - (* TODO: Best_tip_prover shouldn't return with hash if we don't need it *) - { proof_with_data with data = With_hash.data proof_with_data.data } + Proof_carrying_data.map proof_with_data ~f:With_hash.data in match result with | None -> diff --git a/src/lib/network_peer/network_peer.ml b/src/lib/network_peer/network_peer.ml deleted file mode 100644 index d4a3b7c0ce9..00000000000 --- a/src/lib/network_peer/network_peer.ml +++ /dev/null @@ -1,14 +0,0 @@ -open Async_kernel -open Async_rpc_kernel -module Peer = Peer -module Envelope = Envelope - -(* TODO: move this out of Network_peer *) -type query_peer = - { query : - 'r 'q. - Peer.t - -> (Versioned_rpc.Connection_with_menu.t -> 'q -> 'r Deferred.Or_error.t) - -> 'q - -> 'r Deferred.Or_error.t - } diff --git a/src/lib/proof_carrying_data/proof_carrying_data.ml b/src/lib/proof_carrying_data/proof_carrying_data.ml index b2a90fe3488..bd59372b738 100644 --- a/src/lib/proof_carrying_data/proof_carrying_data.ml +++ b/src/lib/proof_carrying_data/proof_carrying_data.ml @@ -9,4 +9,4 @@ module Stable = struct end end] -let map { data; proof } ~f1 ~f2 = { data = f1 data; proof = f2 proof } +let map { data; proof } ~f = { data = f data; proof } From e4f3e1dff26d59dd9202e5be19d21e31820c1569 Mon Sep 17 00:00:00 2001 From: Nathan Holland Date: Fri, 23 Aug 2024 13:28:51 -0400 Subject: [PATCH 6/6] Fix mina_lib tests --- src/lib/gossip_net/intf.ml | 1 + src/lib/mina_lib/tests/tests.ml | 321 +++++++++++++++----------------- src/lib/mina_networking/rpcs.ml | 7 - 3 files changed, 155 insertions(+), 174 deletions(-) diff --git a/src/lib/gossip_net/intf.ml b/src/lib/gossip_net/intf.ml index b291abe24f5..d3337ec93e1 100644 --- a/src/lib/gossip_net/intf.ml +++ b/src/lib/gossip_net/intf.ml @@ -14,6 +14,7 @@ type ('query, 'response) rpc_fn = type 'r rpc_response = | Failed_to_connect of Error.t | Connected of 'r Or_error.t Envelope.Incoming.t +[@@deriving sexp] module type RPC_IMPLEMENTATION = sig type ctx diff --git a/src/lib/mina_lib/tests/tests.ml b/src/lib/mina_lib/tests/tests.ml index 67af53f7088..a634d4099c2 100644 --- a/src/lib/mina_lib/tests/tests.ml +++ b/src/lib/mina_lib/tests/tests.ml @@ -11,18 +11,23 @@ let%test_module "Epoch ledger sync tests" = module type CONTEXT = sig include Mina_lib.CONTEXT - val trust_system : Trust_system.t + val genesis_ledger : Mina_ledger.Ledger.t Lazy.t end type network_info = - { networking : Mina_networking.t - ; network_peer : Peer.t - ; consensus_local_state : Consensus.Data.Local_state.t - ; no_answer_ivar : unit Ivar.t + { networking : Mina_networking.t; network_peer : Peer.t } + + type test_state = + { name : string + ; network_info1 : network_info + ; network_info2 : network_info + ; staking_epoch_ledger : + Consensus.Data.Local_state.Snapshot.Ledger_snapshot.t + ; next_epoch_ledger : + Consensus.Data.Local_state.Snapshot.Ledger_snapshot.t + ; cleanup : unit -> unit } - exception No_sync_answer - exception Sync_timeout let logger = Logger.create () @@ -76,6 +81,7 @@ let%test_module "Epoch ledger sync tests" = let%bind trust_system = Trust_system.create (make_dirname "trust_system") in + let time_controller = Block_time.Controller.basic ~logger in let module Context = struct let logger = logger @@ -87,22 +93,43 @@ let%test_module "Epoch ledger sync tests" = let trust_system = trust_system + let time_controller = time_controller + let commit_id = "not specified for unit test" end in + let genesis_ledger = + lazy + (Mina_ledger.Ledger.create + ~directory_name:(make_dirname "genesis_ledger") + ~depth:precomputed_values.constraint_constants.ledger_depth () ) + in + let genesis_epoch_data : Consensus.Genesis_epoch_data.t = None in + let genesis_state_hash = Quickcheck.random_value Ledger_hash.gen in + let consensus_local_state = + Consensus.Data.Local_state.create + ~context:(module Context) + ~genesis_ledger ~genesis_epoch_data + ~epoch_ledger_location:(make_dirname "epoch_ledger") + ~genesis_state_hash + (Signature_lib.Public_key.Compressed.Set.of_list []) + in + let module Context = struct + include Context + + let genesis_ledger = genesis_ledger + + let consensus_local_state = consensus_local_state + end in return (module Context : CONTEXT) let pids = Child_processes.Termination.create_pid_table () - let verifier = - Async.Thread_safe.block_on_async_exn (fun () -> - let%bind precomputed_values = - let%map (module Context) = make_context () in - Context.precomputed_values - in - Verifier.create ~logger ~proof_level:precomputed_values.proof_level - ~constraint_constants:precomputed_values.constraint_constants ~pids - ~conf_dir:(Some (make_dirname "verifier")) - ~commit_id:"not specified for unit tests" () ) + let make_verifier (module Context : CONTEXT) = + let open Context in + Verifier.create ~logger ~proof_level:precomputed_values.proof_level + ~constraint_constants:precomputed_values.constraint_constants ~pids + ~conf_dir:(Some (make_dirname "verifier")) + ~commit_id:"not specified for unit tests" () let make_empty_ledger (module Context : CONTEXT) = Mina_ledger.Ledger.create @@ -112,16 +139,11 @@ let%test_module "Epoch ledger sync tests" = Mina_ledger.Ledger.Db.create ~depth:Context.precomputed_values.constraint_constants.ledger_depth () - let peek_frontier frontier_broadcast_pipe = - Broadcast_pipe.Reader.peek frontier_broadcast_pipe - |> Result.of_option - ~error:(Error.of_string "Cannot retrieve transition frontier") - (* [instance] and [test_number] are used to make ports distinct among tests *) let make_mina_network ~context:(module Context : CONTEXT) ~name ~instance - ~test_number ~libp2p_keypair_str ~initial_peers ~genesis_ledger_hashes = + ~test_number ~libp2p_keypair_str ~initial_peers = let open Context in let frontier_broadcast_pipe_r, frontier_broadcast_pipe_w = Broadcast_pipe.create None @@ -134,6 +156,7 @@ let%test_module "Epoch ledger sync tests" = Block_time.Controller.create @@ Block_time.Controller.basic ~logger in let on_remote_push () = Deferred.unit in + let%bind verifier = make_verifier (module Context) in let block_reader, block_sink = let on_push () = Deferred.unit in Transition_handler.Block_sink.create @@ -176,22 +199,6 @@ let%test_module "Epoch ledger sync tests" = snark_remote_sink in let sinks = (block_sink, tx_remote_sink, snark_remote_sink) in - let genesis_ledger = - lazy - (Mina_ledger.Ledger.create - ~directory_name:(make_dirname "genesis_ledger") - ~depth:precomputed_values.constraint_constants.ledger_depth () ) - in - let genesis_epoch_data : Consensus.Genesis_epoch_data.t = None in - let genesis_state_hash = Quickcheck.random_value Ledger_hash.gen in - let consensus_local_state = - Consensus.Data.Local_state.create - ~context:(module Context) - ~genesis_ledger ~genesis_epoch_data - ~epoch_ledger_location:(make_dirname "epoch_ledger") - ~genesis_state_hash - (Signature_lib.Public_key.Compressed.Set.of_list []) - in let genesis_ledger_hash = Mina_ledger.Ledger.merkle_root (Lazy.force genesis_ledger) in @@ -262,58 +269,19 @@ let%test_module "Epoch ledger sync tests" = } in let config : Mina_networking.Config.t = - { logger - ; trust_system - ; time_controller - ; consensus_constants - ; consensus_local_state - ; genesis_ledger_hash - ; constraint_constants - ; precomputed_values - ; creatable_gossip_net - ; is_seed - ; log_gossip_heard - } + { genesis_ledger_hash; creatable_gossip_net; is_seed; log_gossip_heard } in - let no_answer_ivar = Ivar.create () in - let get_best_tip _ = return None in - let answer_sync_ledger_query query_env = - let ledger_hash, _ = Envelope.Incoming.data query_env in - let%bind.Deferred.Or_error frontier = - Deferred.return @@ peek_frontier frontier_broadcast_pipe_r - in - Sync_handler.answer_query ~frontier ledger_hash - (Envelope.Incoming.map ~f:Tuple2.get2 query_env) - ~logger ~trust_system - |> Deferred.map ~f:(function - | Some answer -> - Ok answer - | None -> - if - List.mem genesis_ledger_hashes ledger_hash - ~equal:Frozen_ledger_hash.equal - then - (* should happen only when trying to sync to genesis ledger *) - Ivar.fill_if_empty no_answer_ivar () ; - Error (Error.of_string "No answer to sync query") ) - in - let unimplemented name _ = failwithf "RPC %s unimplemented" name () in let rpc_error name _ = return @@ Or_error.error_string (sprintf "Error for unimplemented RPC %s" name) in let%bind (mina_networking : Mina_networking.t) = - Mina_networking.create config ~sinks ~answer_sync_ledger_query - ~get_best_tip - ~get_some_initial_peers:(unimplemented "get_some_initial_peers") - ~get_staged_ledger_aux_and_pending_coinbases_at_hash: - (unimplemented "get_staged_ledger_aux_and_pending_coinbases_at_hash") - ~get_ancestry:(unimplemented "get_ancestry") + Mina_networking.create + (module Context) + config ~sinks + ~get_transition_frontier:(fun () -> + Broadcast_pipe.Reader.peek frontier_broadcast_pipe_r ) ~get_node_status:(rpc_error "get_node_status") - ~get_transition_chain_proof: - (unimplemented "get_transition_chain_proof") - ~get_transition_chain:(unimplemented "get_transition_chain") - ~get_transition_knowledge:(unimplemented "get_transition_knowledge") in (* create transition frontier *) let tr_tm0 = Unix.gettimeofday () in @@ -336,10 +304,9 @@ let%test_module "Epoch ledger sync tests" = *) Transition_router.run ~sync_local_state:false ~cache_exceptions:true ~context:(module Context) - ~trust_system:config.trust_system ~verifier ~network:mina_networking - ~is_seed:config.is_seed ~is_demo_mode:false - ~time_controller:config.time_controller - ~consensus_local_state:config.consensus_local_state + ~trust_system ~verifier ~network:mina_networking + ~is_seed:config.is_seed ~is_demo_mode:false ~time_controller + ~consensus_local_state ~persistent_root_location:(make_dirname "persistent_root_location") ~persistent_frontier_location: (make_dirname "persistent_frontier_location") @@ -358,19 +325,15 @@ let%test_module "Epoch ledger sync tests" = let peer_id = Mina_net2.Keypair.to_peer_id libp2p_keypair in Peer.create Core.Unix.Inet_addr.localhost ~libp2p_port ~peer_id in - return - { networking = mina_networking - ; network_peer - ; consensus_local_state - ; no_answer_ivar - } + return { networking = mina_networking; network_peer } - let run_test ?(timeout_min = default_timeout_min) (module Context : CONTEXT) - ~name ~staking_epoch_ledger ~next_epoch_ledger ~starting_accounts - ~test_number = + let setup_test ?(timeout_min = default_timeout_min) + (module Context : CONTEXT) ~name ~staking_epoch_ledger + ~next_epoch_ledger ~test_number = let%bind fresh_trust_system = Trust_system.create (make_dirname "trust_system") in + let open Context in let module Context2 = struct include Context @@ -382,25 +345,6 @@ let%test_module "Epoch ledger sync tests" = don't_wait_for (let%map () = after (Time.Span.of_min timeout_min) in if not !test_finished then (cleanup () ; raise Sync_timeout) ) ; - let staking_ledger_root = - Consensus.Data.Local_state.Snapshot.Ledger_snapshot.merkle_root - staking_epoch_ledger - in - let next_epoch_ledger_root = - Consensus.Data.Local_state.Snapshot.Ledger_snapshot.merkle_root - next_epoch_ledger - in - let genesis_ledger_hashes = - match (staking_epoch_ledger, next_epoch_ledger) with - | Genesis_epoch_ledger _, Genesis_epoch_ledger _ -> - [ staking_ledger_root; next_epoch_ledger_root ] - | Genesis_epoch_ledger _, Ledger_db _ -> - [ staking_ledger_root ] - | Ledger_db _, Genesis_epoch_ledger _ -> - [ next_epoch_ledger_root ] - | Ledger_db _, Ledger_db _ -> - [] - in let net_info1_tm0 = Unix.gettimeofday () in let%bind network_info1 = make_mina_network ~name @@ -408,7 +352,7 @@ let%test_module "Epoch ledger sync tests" = ~instance:0 ~test_number ~libp2p_keypair_str: "CAESQFzI5/57gycQ1qumCq00OFo60LArXgbrgV0b5P8tNiSujUZT5Psc+74luHmSSf7kVIZ7w0YObC//UVXPCOgeh4o=,CAESII1GU+T7HPu+Jbh5kkn+5FSGe8NGDmwv/1FVzwjoHoeK,12D3KooWKKqrPfHi4PNkWms5Z9oANjRftE5vueTmkt4rpz9sXM69" - ~initial_peers:[] ~genesis_ledger_hashes + ~initial_peers:[] in let net_info1_tm1 = Unix.gettimeofday () in [%log debug] "(%s) Time to create network 1: %0.02f" name @@ -422,25 +366,33 @@ let%test_module "Epoch ledger sync tests" = next_epoch_ledger in (* store snapshots in local state *) - Consensus.Data.Local_state.For_tests.set_snapshot - network_info1.consensus_local_state Staking_epoch_snapshot - staking_epoch_snapshot ; - Consensus.Data.Local_state.For_tests.set_snapshot - network_info1.consensus_local_state Next_epoch_snapshot - next_epoch_snapshot ; + Consensus.Data.Local_state.For_tests.set_snapshot consensus_local_state + Staking_epoch_snapshot staking_epoch_snapshot ; + Consensus.Data.Local_state.For_tests.set_snapshot consensus_local_state + Next_epoch_snapshot next_epoch_snapshot ; let net_info2_tm0 = Unix.gettimeofday () in - let%bind network_info2 = + let%map network_info2 = make_mina_network ~name ~instance:1 ~test_number ~context:(module Context2) ~libp2p_keypair_str: "CAESQMHCQMQDqPKTFLAjZWwA3vvbkzMJZiVrjvte+bDfUvEeRhjvhsa9IfuFDEmJ721drMJ5cEWAmVmrQYfretz9MUQ=,CAESIEYY74bGvSH7hQxJie9tXazCeXBFgJlZq0GH63rc/TFE,12D3KooWEXzm5pMj1DQqNz6bpMRdJa55bytbawkuHVNhGR3XuTpw" ~initial_peers: [ Mina_net2.Multiaddr.of_peer network_info1.network_peer ] - ~genesis_ledger_hashes in let net_info2_tm1 = Unix.gettimeofday () in [%log debug] "(%s) Time to create network 2: %0.02f" name (net_info2_tm1 -. net_info2_tm0) ; + { name + ; network_info1 + ; network_info2 + ; staking_epoch_ledger + ; next_epoch_ledger + ; cleanup + } + + let both_ledgers_sync_successfully ~starting_accounts + (module Context : CONTEXT) (test : test_state) = + let open Context in let make_sync_ledger () = let db_ledger = make_empty_db_ledger (module Context) in List.iter starting_accounts ~f:(fun (acct : Account.t) -> @@ -459,18 +411,35 @@ let%test_module "Epoch ledger sync tests" = let query_reader = Mina_ledger.Sync_ledger.Db.query_reader sync_ledger in - let response_writer = + let answer_writer = Mina_ledger.Sync_ledger.Db.answer_writer sync_ledger in - Mina_networking.glue_sync_ledger network_info2.networking - ~preferred:[ network_info1.network_peer ] - query_reader response_writer ; + (* + (* setup a proxy response pipe so we can inspect the messages from our test *) + let proxy_answer_reader, proxy_answer_writer = + Linear_pipe.create () + in + don't_wait_for ( + Linear_pipe.Reader.iter proxy_answer_reader ~f:(fun answer -> + if Option.is_none response && is_genesis_state_hash + ( match response with + | None when genesis_state_hash -> + ) + Linear_pipe.write proxy_response_writer response) + *) + Mina_networking.glue_sync_ledger test.network_info2.networking + ~preferred:[ test.network_info1.network_peer ] + query_reader answer_writer ; sync_ledger in - (* should only happen when syncing to a genesis ledger *) - don't_wait_for - (let%bind () = Ivar.read network_info1.no_answer_ivar in - cleanup () ; raise No_sync_answer ) ; + let staking_ledger_root = + Consensus.Data.Local_state.Snapshot.Ledger_snapshot.merkle_root + test.staking_epoch_ledger + in + let next_epoch_ledger_root = + Consensus.Data.Local_state.Snapshot.Ledger_snapshot.merkle_root + test.next_epoch_ledger + in (* sync current staking ledger *) let sync_ledger1_tm0 = Unix.gettimeofday () in let sync_ledger1 = make_sync_ledger () in @@ -481,7 +450,7 @@ let%test_module "Epoch ledger sync tests" = with | `Ok ledger -> let sync_ledger1_tm1 = Unix.gettimeofday () in - [%log debug] "(%s) Time to sync ledger 1: %0.02f" name + [%log debug] "(%s) Time to sync ledger 1: %0.02f" test.name (sync_ledger1_tm1 -. sync_ledger1_tm0) ; let ledger_root = Mina_ledger.Ledger.Db.merkle_root ledger in assert (Ledger_hash.equal ledger_root staking_ledger_root) ; @@ -498,17 +467,45 @@ let%test_module "Epoch ledger sync tests" = with | `Ok ledger -> let sync_ledger2_tm1 = Unix.gettimeofday () in - [%log debug] "(%s) Time to sync ledger 2: %0.02f" name + [%log debug] "(%s) Time to sync ledger 2: %0.02f" test.name (sync_ledger2_tm1 -. sync_ledger2_tm0) ; - cleanup () ; + test.cleanup () ; let ledger_root = Mina_ledger.Ledger.Db.merkle_root ledger in assert (Ledger_hash.equal ledger_root next_epoch_ledger_root) ; [%log debug] "Synced next epoch ledger, sync test succeeded" ; Deferred.unit | `Target_changed _ -> - cleanup () ; + test.cleanup () ; failwith "Target changed when getting next epoch ledger" + let cannot_sync_staking_ledger (test : test_state) = + let staking_ledger_root = + Consensus.Data.Local_state.Snapshot.Ledger_snapshot.merkle_root + test.staking_epoch_ledger + in + let%map response = + Mina_networking.query_peer test.network_info2.networking + test.network_info1.network_peer.peer_id + Mina_networking.Rpcs.Answer_sync_ledger_query + (staking_ledger_root, Num_accounts) + in + match response with + | Connected { data = Ok (Error err); _ } -> + if + not + (String.is_substring (Error.to_string_hum err) + ~substring:"Refusing to answer sync ledger query" ) + then + failwithf "unexpected error returned from sync ledger RPC: %s" + (Error.to_string_hum err) () + | Connected { data = Ok (Ok _); _ } -> + failwith "unexpected successful RPC response" + | Connected { data = Error err; _ } -> + failwithf "unexpected RPC failure: %s" (Error.to_string_hum err) () + | Failed_to_connect err -> + failwithf "unexpected connection failure: %s" + (Error.to_string_hum err) () + let make_genesis_ledger (module Context : CONTEXT) (accounts : Account.t list) = let ledger = make_empty_ledger (module Context) in @@ -549,9 +546,12 @@ let%test_module "Epoch ledger sync tests" = let next_epoch_ledger = make_db_ledger (module Context) (List.take test_accounts 20) in - run_test ~name:"sync to empty ledgers" ~test_number:1 + setup_test ~name:"sync to empty ledgers" ~test_number:1 (module Context) - ~staking_epoch_ledger ~next_epoch_ledger ~starting_accounts:[] ) + ~staking_epoch_ledger ~next_epoch_ledger + >>= both_ledgers_sync_successfully + (module Context) + ~starting_accounts:[] ) let%test_unit "Sync current, next staking ledgers to nonempty ledgers" = Async.Thread_safe.block_on_async_exn (fun () -> @@ -568,9 +568,10 @@ let%test_module "Epoch ledger sync tests" = the ledger to sync to, see issue #12170 *) let starting_accounts = List.take test_accounts 8 in - run_test ~name:"sync to nonempty ledgers" ~test_number:2 + setup_test ~name:"sync to nonempty ledgers" ~test_number:2 (module Context) - ~staking_epoch_ledger ~next_epoch_ledger ~starting_accounts ) + ~staking_epoch_ledger ~next_epoch_ledger + >>= both_ledgers_sync_successfully (module Context) ~starting_accounts ) (* A `fetch` to sync a genesis ledger will just loop, because `get_ledger_by_hash` returns None for genesis ledgers @@ -578,32 +579,18 @@ let%test_module "Epoch ledger sync tests" = In the consensus code, we don't call `fetch` if the requested hash is the genesis ledger hash, so such looping should not occur - In the tests here, we check whether `answer_sync_query` returns None, reflecting - the None returned by `get_ledger_by_hash`, and fill an ivar; if we detect - that has been filled, we raise an exception, No_sync_answer - - That exception should be raised only in the following test + In the tests here, we send a single `Answer_sync_ledger_query` RPC to determine + that the other peer will not serve us a genesis ledger. *) let%test_unit "Sync genesis ledgers to empty ledgers, should fail" = - let f () = - Monitor.try_with ~here:[%here] (fun () -> - let%bind (module Context) = make_context () in - let staking_epoch_ledger = - make_genesis_ledger (module Context) (List.take test_accounts 10) - in - let next_epoch_ledger = staking_epoch_ledger in - run_test ~name:"fail to sync genesis ledgers" ~test_number:3 - (module Context) - ~staking_epoch_ledger ~next_epoch_ledger ~starting_accounts:[] ) - in - match Async.Thread_safe.block_on_async_exn f with - | Ok () -> - failwith "Ledgers synced to a genesis ledger, unexpectedly" - | Error exn -> ( - match Monitor.extract_exn exn with - | No_sync_answer -> - [%log debug] "Did not sync to genesis ledger, sync test succeeded" ; - () - | exn' -> - failwithf "Unexpected exception: %s" (Exn.to_string exn') () ) + Async.Thread_safe.block_on_async_exn (fun () -> + let%bind (module Context) = make_context () in + let staking_epoch_ledger = + make_genesis_ledger (module Context) (List.take test_accounts 10) + in + let next_epoch_ledger = staking_epoch_ledger in + setup_test ~name:"fail to sync genesis ledgers" ~test_number:3 + (module Context) + ~staking_epoch_ledger ~next_epoch_ledger + >>= cannot_sync_staking_ledger ) end ) diff --git a/src/lib/mina_networking/rpcs.ml b/src/lib/mina_networking/rpcs.ml index 9037b2843e6..9b4cca901d4 100644 --- a/src/lib/mina_networking/rpcs.ml +++ b/src/lib/mina_networking/rpcs.ml @@ -36,13 +36,6 @@ module type CONTEXT = sig end type ctx = (module CONTEXT) -(* - { logger : Logger.t - ; trust_system : Trust_system.t - ; list_peers : unit -> Peer.t list - ; get_transition_frontier : unit -> Transition_frontier.t option - } -*) let validate_protocol_versions ~logger ~trust_system ~rpc_name ~sender blocks = let version_errors =