11pub mod dashboard;
22use clap:: { Args , Parser , Subcommand } ;
33
4- #[ cfg( unix) ]
5- use std:: os:: unix:: net:: UnixStream ;
6- #[ cfg( windows) ]
7- use uds_windows:: UnixStream ;
8-
9- use std:: path:: { Path , PathBuf } ;
4+ use std:: path:: Path ;
105
116use ethereum_types:: H256 ;
12- use serde_json:: value:: RawValue ;
137use thiserror:: Error ;
148
159use dashboard:: grafana:: { GrafanaAPI , DASHBOARD_TEMPLATES } ;
1610use ethportal_api:: { BlockBodyKey , BlockHeaderKey , BlockReceiptsKey , HistoryContentKey } ;
17- use trin_types:: cli:: DEFAULT_WEB3_IPC_PATH ;
1811use trin_utils:: bytes:: hex_encode;
1912
2013#[ derive( Parser , Debug , PartialEq ) ]
@@ -25,28 +18,11 @@ use trin_utils::bytes::hex_encode;
2518 about = "Portal Network command line utilities"
2619) ]
2720enum Trin {
28- JsonRpc ( JsonRpc ) ,
2921 #[ command( subcommand) ]
3022 EncodeKey ( EncodeKey ) ,
3123 CreateDashboard ( DashboardConfig ) ,
3224}
3325
34- #[ derive( Args , Debug , PartialEq ) ]
35- #[ command( name = "json-rpc" , about = "Run JSON-RPC commands against a trin node" ) ]
36- struct JsonRpc {
37- /// IPC path of target JSON-RPC endpoint.
38- #[ arg( default_value = DEFAULT_WEB3_IPC_PATH , long) ]
39- ipc : PathBuf ,
40-
41- /// JSON-RPC method (e.g. discv5_routingTableInfo).
42- #[ arg( required = true ) ]
43- endpoint : String ,
44-
45- /// Comma-separated list of JSON-RPC parameters.
46- #[ arg( long, value_delimiter = ',' ) ]
47- params : Option < Vec < String > > ,
48- }
49-
5026// TODO: Remove clippy allow once a variant with non-"Block" prefix is added.
5127/// Encode a content key.
5228#[ derive( Subcommand , Debug , PartialEq ) ]
@@ -91,32 +67,11 @@ struct DashboardConfig {
9167
9268fn main ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
9369 match Trin :: parse ( ) {
94- Trin :: JsonRpc ( rpc) => json_rpc ( rpc) ,
9570 Trin :: EncodeKey ( content_key) => encode_content_key ( content_key) ,
9671 Trin :: CreateDashboard ( dashboard_config) => create_dashboard ( dashboard_config) ,
9772 }
9873}
9974
100- fn json_rpc ( rpc : JsonRpc ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
101- let params: Option < Vec < Box < RawValue > > > = rpc
102- . params
103- . map ( |param| param. into_iter ( ) . map ( jsonrpc:: arg) . collect ( ) ) ;
104- eprintln ! (
105- "Attempting RPC. endpoint={} params={:?} file={}" ,
106- rpc. endpoint,
107- params,
108- rpc. ipc. to_string_lossy( )
109- ) ;
110- let mut client = TrinClient :: from_ipc ( & rpc. ipc ) ?;
111-
112- let req = client. build_request ( rpc. endpoint . as_str ( ) , & params) ;
113- let resp = client. make_request ( req) ?;
114-
115- println ! ( "{}" , serde_json:: to_string_pretty( & resp) . unwrap( ) ) ;
116-
117- Ok ( ( ) )
118- }
119-
12075fn encode_content_key ( content_key : EncodeKey ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
12176 let key = match content_key {
12277 EncodeKey :: BlockHeader { block_hash } => {
@@ -161,137 +116,11 @@ fn create_dashboard(dashboard_config: DashboardConfig) -> Result<(), Box<dyn std
161116 Ok ( ( ) )
162117}
163118
164- fn build_request < ' a > (
165- method : & ' a str ,
166- raw_params : & ' a Option < Vec < Box < RawValue > > > ,
167- request_id : u64 ,
168- ) -> jsonrpc:: Request < ' a > {
169- match raw_params {
170- Some ( val) => jsonrpc:: Request {
171- method,
172- params : val,
173- id : serde_json:: json!( request_id) ,
174- jsonrpc : Some ( "2.0" ) ,
175- } ,
176- None => jsonrpc:: Request {
177- method,
178- params : & [ ] ,
179- id : serde_json:: json!( request_id) ,
180- jsonrpc : Some ( "2.0" ) ,
181- } ,
182- }
183- }
184-
185- pub trait TryClone {
186- fn try_clone ( & self ) -> std:: io:: Result < Self >
187- where
188- Self : Sized ;
189- }
190-
191- impl TryClone for UnixStream {
192- fn try_clone ( & self ) -> std:: io:: Result < Self > {
193- UnixStream :: try_clone ( self )
194- }
195- }
196-
197- pub struct TrinClient < S >
198- where
199- S : std:: io:: Read + std:: io:: Write + TryClone ,
200- {
201- stream : S ,
202- request_id : u64 ,
203- }
204-
205- impl TrinClient < UnixStream > {
206- fn from_ipc ( path : & Path ) -> std:: io:: Result < Self > {
207- // TODO: a nice error if this file does not exist
208- Ok ( Self {
209- stream : UnixStream :: connect ( path) ?,
210- request_id : 0 ,
211- } )
212- }
213- }
214-
215- #[ derive( Error , Debug ) ]
216- pub enum JsonRpcError {
217- #[ error( "Received malformed response: {0}" ) ]
218- Malformed ( serde_json:: Error ) ,
219-
220- #[ error( "Received empty response" ) ]
221- Empty ,
222- }
223-
224- // TryClone is used because JSON-RPC responses are not followed by EOF. We must read bytes
225- // from the stream until a complete object is detected, and the simplest way of doing that
226- // with available APIs is to give ownership of a Read to a serde_json::Deserializer. If we
227- // gave it exclusive ownership that would require us to open a new connection for every
228- // command we wanted to send! By making a clone (or, by trying to) we can have our cake
229- // and eat it too.
230- //
231- // TryClone is not necessary if TrinClient stays in this file forever; this script only
232- // needs to make a single request before it exits. However, in a future where TrinClient
233- // becomes the mechanism other parts of the codebase (such as peertester) use to act as
234- // JSON-RPC clients then this becomes necessary. So, this is slightly over-engineered but
235- // with an eye to future growth.
236- impl < ' a , S > TrinClient < S >
237- where
238- S : std:: io:: Read + std:: io:: Write + TryClone ,
239- {
240- fn build_request (
241- & mut self ,
242- method : & ' a str ,
243- params : & ' a Option < Vec < Box < RawValue > > > ,
244- ) -> jsonrpc:: Request < ' a > {
245- let result = build_request ( method, params, self . request_id ) ;
246- self . request_id += 1 ;
247-
248- result
249- }
250-
251- fn make_request ( & mut self , req : jsonrpc:: Request ) -> Result < serde_json:: Value , JsonRpcError > {
252- let data = serde_json:: to_vec ( & req) . unwrap ( ) ;
253-
254- self . stream . write_all ( & data) . unwrap ( ) ;
255- self . stream . flush ( ) . unwrap ( ) ;
256-
257- let clone = self . stream . try_clone ( ) . unwrap ( ) ;
258- let deser = serde_json:: Deserializer :: from_reader ( clone) ;
259-
260- if let Some ( obj) = deser. into_iter :: < serde_json:: Value > ( ) . next ( ) {
261- return obj. map_err ( JsonRpcError :: Malformed ) ;
262- }
263-
264- // this should only happen when they immediately send EOF
265- Err ( JsonRpcError :: Empty )
266- }
267- }
268-
269119#[ cfg( test) ]
270120mod tests {
271121 use super :: * ;
272122 use std:: str:: FromStr ;
273123
274- #[ test]
275- fn test_trin_with_json_rpc ( ) {
276- let trin = Trin :: parse_from ( [
277- "test" ,
278- "json-rpc" ,
279- "--ipc" ,
280- "/tmp/trin.ipc" ,
281- "--params" ,
282- "p1,p2" ,
283- "discv5_routingTableInfo" ,
284- ] ) ;
285- assert_eq ! (
286- trin,
287- Trin :: JsonRpc ( JsonRpc {
288- ipc: PathBuf :: from( "/tmp/trin.ipc" ) ,
289- endpoint: "discv5_routingTableInfo" . to_string( ) ,
290- params: Some ( vec![ "p1" . to_string( ) , "p2" . to_string( ) ] ) ,
291- } )
292- ) ;
293- }
294-
295124 #[ test]
296125 fn test_trin_with_encode_key ( ) {
297126 const BLOCK_HASH : & str =
0 commit comments