Skip to content

Commit b3af3de

Browse files
committed
Add http/ipc jsonrpc testing to harness
1 parent 2c59872 commit b3af3de

File tree

9 files changed

+334
-31
lines changed

9 files changed

+334
-31
lines changed

CONTRIBUTING.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ through github via pull requests.
2222
* Mark unfinished pull requests with the "Work in Progress" label.
2323
* Before submitting a pr for review, you should run the following commands
2424
locally and make sure they are passing, otherwise CI will raise an error.
25-
* `cargo fmt` and `cargo clippy` for linting checks
26-
* `cargo test --workspace` to run all tests
25+
* `cargo fmt -- --check` and `cargo clippy -- --deny warnings` for linting checks
26+
* `RUSTFLAGS='-D warnings' cargo test --workspace` to run all tests
27+
* Run the `ethportal-peertest` harness against a locally running node. Instructions
28+
can be found in [README](ethportal-peertest/README.md).
2729
* Pull requests **should** always be reviewed by another member of the team
2830
prior to being merged.
2931
* Obvious exceptions include very small pull requests.

Cargo.lock

Lines changed: 116 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ethportal-peertest/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ edition = "2018"
77

88
[dependencies]
99
clap = "2.33.3"
10+
hyper = { version = "0.14", features = ["full"] }
1011
log = "0.4.14"
1112
rocksdb = "0.16.0"
13+
serde_json = "1.0.59"
1214
structopt = "0.3"
1315
tokio = {version = "1.8.0", features = ["full"]}
1416
tracing = "0.1.26"

ethportal-peertest/README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ Run a portal network node that you want to test and pass node's Enr as a target
55
```sh
66
cd ethportal-peertest
77

8-
RUST_LOG=debug cargo run -p ethportal-peertest -- --target_node enr:-IS4QBDHCSMoYoC5UziAwKSyTmMPrhMaEpaE52L8DDAkipqvZQe9fgLy2wVuuEJwO9l1KsYrRoFGCsNjylbd0CDNw60BgmlkgnY0gmlwhMCoXUSJc2VjcDI1NmsxoQJPAZUFErHK1DZYRTLjk3SCNgye9sS-MxoQI-gLiUdwc4N1ZHCCIyk
8+
RUST_LOG=debug cargo run -p ethportal-peertest -- --target-node enr:-IS4QBDHCSMoYoC5UziAwKSyTmMPrhMaEpaE52L8DDAkipqvZQe9fgLy2wVuuEJwO9l1KsYrRoFGCsNjylbd0CDNw60BgmlkgnY0gmlwhMCoXUSJc2VjcDI1NmsxoQJPAZUFErHK1DZYRTLjk3SCNgye9sS-MxoQI-gLiUdwc4N1ZHCCIyk
9+
```
10+
11+
## Transport selection
12+
Running the test harness will by default test all jsonrpc endpoints over IPC to the target node. To make sure these pass, please make sure that the target node is running with `--web3-transport ipc`. To test jsonrpc over http, use the `--target-transport http` cli argument for the harness, and make sure the target node is running with `--web3-transport http`. Ideally, both transport methods are tested before PRs.
913

10-
```

ethportal-peertest/src/cli.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,32 @@ pub struct PeertestConfig {
1515
#[structopt(
1616
default_value(DEFAULT_LISTEN_PORT),
1717
short = "p",
18-
long = "listen_port",
18+
long = "listen-port",
1919
help = "The UDP port to listen on."
2020
)]
2121
pub listen_port: u16,
2222

2323
#[structopt(
2424
default_value(DEFAULT_WEB3_IPC_PATH),
25-
long = "web3_ipc_path",
25+
long = "web3-ipc-path",
2626
help = "path to json-rpc socket address over IPC"
2727
)]
2828
pub web3_ipc_path: String,
2929

3030
#[structopt(
3131
short,
32-
long = "target_node",
33-
help = "Base64-encoded ENR's of the nodes under test"
32+
long = "target-node",
33+
help = "Base64-encoded ENR of the node under test"
3434
)]
3535
pub target_node: String,
36+
37+
#[structopt(
38+
default_value = "ipc",
39+
possible_values(&["http", "ipc"]),
40+
long = "target-transport",
41+
help = "Transport type of the node under test"
42+
)]
43+
pub target_transport: String,
3644
}
3745

3846
impl PeertestConfig {

ethportal-peertest/src/jsonrpc.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
use std::io::prelude::*;
2+
use std::os::unix::net::UnixStream;
3+
use std::slice::Iter;
4+
5+
use hyper::{self, Body, Client, Method, Request};
6+
use log::info;
7+
use serde_json::{self, Value};
8+
9+
use trin_core::portalnet::U256;
10+
11+
#[derive(Copy, Clone)]
12+
pub struct JsonRpcEndpoint {
13+
pub method: &'static str,
14+
pub id: &'static u8,
15+
}
16+
17+
const ALL_ENDPOINTS: [JsonRpcEndpoint; 6] = [
18+
JsonRpcEndpoint {
19+
method: "web3_clientVersion",
20+
id: &0,
21+
},
22+
JsonRpcEndpoint {
23+
method: "discv5_nodeInfo",
24+
id: &1,
25+
},
26+
JsonRpcEndpoint {
27+
method: "discv5_routingTableInfo",
28+
id: &2,
29+
},
30+
JsonRpcEndpoint {
31+
method: "eth_blockNumber",
32+
id: &3,
33+
},
34+
JsonRpcEndpoint {
35+
method: "portalHistory_dataRadius",
36+
id: &4,
37+
},
38+
JsonRpcEndpoint {
39+
method: "portalState_dataRadius",
40+
id: &5,
41+
},
42+
];
43+
44+
fn validate_endpoint_response(method: &str, result: &Value) {
45+
match method {
46+
"web3_clientVersion" => {
47+
assert_eq!(result.as_str().unwrap(), "trin 0.0.1-alpha");
48+
}
49+
"discv5_nodeInfo" => {
50+
let enr = result.get("enr").unwrap();
51+
assert!(enr.is_string());
52+
assert!(enr.as_str().unwrap().contains("enr:"));
53+
assert!(result.get("nodeId").unwrap().is_string());
54+
}
55+
"discv5_routingTableInfo" => {
56+
let local_key = result.get("localKey").unwrap();
57+
assert!(local_key.is_string());
58+
assert!(local_key.as_str().unwrap().contains("0x"));
59+
assert!(result.get("buckets").unwrap().is_array());
60+
}
61+
"eth_blockNumber" => {
62+
assert!(result.is_string());
63+
assert!(result.as_str().unwrap().contains("0x"));
64+
}
65+
"portalHistory_dataRadius" => {
66+
assert_eq!(result.as_str().unwrap(), U256::from(u64::MAX).to_string());
67+
}
68+
"portalState_dataRadius" => {
69+
assert_eq!(result.as_str().unwrap(), U256::from(u64::MAX).to_string());
70+
}
71+
_ => panic!("Unsupported endpoint"),
72+
};
73+
info!("{:?} returned a valid response.", method);
74+
}
75+
76+
impl JsonRpcEndpoint {
77+
pub fn all_endpoints() -> Iter<'static, Self> {
78+
ALL_ENDPOINTS.iter()
79+
}
80+
81+
pub fn to_jsonrpc(self) -> Vec<u8> {
82+
let data = format!(
83+
r#"
84+
{{
85+
"jsonrpc":"2.0",
86+
"id": {},
87+
"method": "{}"
88+
}}"#,
89+
self.id, self.method
90+
);
91+
let v: Value = serde_json::from_str(&data).unwrap();
92+
serde_json::to_vec(&v).unwrap()
93+
}
94+
}
95+
96+
pub async fn test_jsonrpc_endpoints_over_ipc() {
97+
for endpoint in JsonRpcEndpoint::all_endpoints() {
98+
info!("Testing over IPC: {:?}", endpoint.method);
99+
let mut stream = UnixStream::connect("/tmp/trin-jsonrpc.ipc").unwrap();
100+
stream.write_all(&endpoint.to_jsonrpc()).unwrap();
101+
stream.flush().unwrap();
102+
let deser = serde_json::Deserializer::from_reader(stream);
103+
for obj in deser.into_iter::<Value>() {
104+
let response_obj = obj.unwrap();
105+
let result = response_obj.get("result").unwrap();
106+
validate_endpoint_response(endpoint.method, result);
107+
}
108+
}
109+
}
110+
111+
pub async fn test_jsonrpc_endpoints_over_http() {
112+
let client = Client::new();
113+
for endpoint in JsonRpcEndpoint::all_endpoints() {
114+
info!("Testing over HTTP: {:?}", endpoint.method);
115+
let json_string = format!(
116+
r#"{{"jsonrpc":"2.0","id":0,"method":"{}","params":[]}}"#,
117+
endpoint.method
118+
);
119+
let req = Request::builder()
120+
.method(Method::POST)
121+
.uri("http://127.0.0.1:8545")
122+
.header("content-type", "application/json")
123+
.body(Body::from(json_string))
124+
.unwrap();
125+
let resp = client.request(req).await.unwrap();
126+
let body = hyper::body::to_bytes(resp.into_body()).await.unwrap();
127+
let body_json: Value = serde_json::from_slice(&body).unwrap();
128+
let result = body_json.get("result").unwrap();
129+
validate_endpoint_response(endpoint.method, result);
130+
}
131+
}

0 commit comments

Comments
 (0)