Skip to content

Commit 60f2fb9

Browse files
KolbyMLEikix
authored andcommitted
simulators/portal/history/trin-bridge: add portal network history trin-bridge simulator (ethereum#989)
1 parent 3d5fa64 commit 60f2fb9

File tree

4 files changed

+242
-0
lines changed

4 files changed

+242
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "trin-bridge"
3+
version = "0.1.0"
4+
authors = ["Kolby ML (Moroz Liebl) <[email protected]>"]
5+
edition = "2021"
6+
7+
[dependencies]
8+
ethportal-api = { git = "https://github.com/ethereum/trin", rev = "b530dc3d40d51c21c800089eacb2852ebd8c4d45" }
9+
portal-spec-test-utils-rs = { git = "https://github.com/ethereum/portal-spec-tests", rev = "d1e996d0d4dc2136b3cd38d9e25cdc3a6b74dcd9" }
10+
hivesim = { git = "https://github.com/ethereum/portal-hive", rev = "8ff1e3d3c941dd00d56dacd777a5dfb71edf402f" }
11+
itertools = "0.10.5"
12+
serde_yaml = "0.9"
13+
tokio = { version = "1", features = ["full"] }
14+
tracing = "0.1.37"
15+
tracing-subscriber = "0.3.16"
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
FROM rust:1.71.1 AS builder
2+
3+
# create a new empty shell project
4+
RUN USER=root cargo new --bin trin-bridge
5+
WORKDIR /trin-bridge
6+
7+
RUN apt-get update && apt-get install clang -y
8+
9+
# copy over manifests and source to build image
10+
COPY Cargo.toml ./Cargo.toml
11+
COPY src ./src
12+
13+
# build for release
14+
RUN cargo build --release
15+
16+
# final base
17+
FROM ubuntu:22.04
18+
19+
RUN apt update && apt install wget -y
20+
21+
# Use these for amd-based devices
22+
RUN wget http://nz2.archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.0g-2ubuntu4_amd64.deb
23+
RUN dpkg -i libssl1.1_1.1.0g-2ubuntu4_amd64.deb
24+
# Use these for arm-based devices
25+
#RUN wget http://ports.ubuntu.com/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_arm64.deb
26+
#RUN dpkg -i libssl1.1_1.1.1f-1ubuntu2_arm64.deb
27+
28+
# copy build artifacts from build stage
29+
COPY --from=builder /trin-bridge/target/release/trin-bridge .
30+
ADD https://raw.githubusercontent.com/ethereum/portal-spec-tests/master/tests/mainnet/history/hive/test_data_collection_of_forks_blocks.yaml ./test-data/test_data_collection_of_forks_blocks.yaml
31+
32+
RUN apt-get update && apt-get install libcurl4 -y
33+
34+
ENV RUST_LOG=debug
35+
36+
# Fake secrets for Trin bridge activation, not actually used in the used `BridgeMode::Test` mode
37+
ENV PANDAOPS_CLIENT_ID=xxx
38+
ENV PANDAOPS_CLIENT_SECRET=xxx
39+
40+
ENTRYPOINT ["./trin-bridge"]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pub const TRIN_BRIDGE_CLIENT_TYPE: &str = "trin-bridge";
2+
pub const BOOTNODES_ENVIRONMENT_VARIABLE: &str = "HIVE_BOOTNODES";
3+
pub const HIVE_CHECK_LIVE_PORT: &str = "HIVE_CHECK_LIVE_PORT";
4+
pub const TEST_DATA_FILE_PATH: &str = "./test-data/test_data_collection_of_forks_blocks.yaml";
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
mod constants;
2+
3+
use crate::constants::{
4+
BOOTNODES_ENVIRONMENT_VARIABLE, HIVE_CHECK_LIVE_PORT, TEST_DATA_FILE_PATH,
5+
TRIN_BRIDGE_CLIENT_TYPE,
6+
};
7+
use ethportal_api::HistoryContentKey;
8+
use ethportal_api::HistoryContentValue;
9+
use ethportal_api::PossibleHistoryContentValue;
10+
use ethportal_api::{Discv5ApiClient, HistoryNetworkApiClient};
11+
use hivesim::types::ClientDefinition;
12+
use hivesim::{dyn_async, Client, NClientTestSpec, Simulation, Suite, Test, TestSpec};
13+
use itertools::Itertools;
14+
use portal_spec_test_utils_rs::get_flair;
15+
use serde_yaml::Value;
16+
use std::collections::HashMap;
17+
use tokio::time::Duration;
18+
19+
fn process_content(content: Vec<(HistoryContentKey, HistoryContentValue)>) -> Vec<String> {
20+
let mut last_header = content.first().unwrap().clone();
21+
22+
let mut result: Vec<String> = vec![];
23+
for history_content in content.into_iter() {
24+
if let HistoryContentKey::BlockHeaderWithProof(_) = &history_content.0 {
25+
last_header = history_content.clone();
26+
}
27+
let comment =
28+
if let HistoryContentValue::BlockHeaderWithProof(header_with_proof) = &last_header.1 {
29+
let content_type = match &history_content.0 {
30+
HistoryContentKey::BlockHeaderWithProof(_) => "header".to_string(),
31+
HistoryContentKey::BlockBody(_) => "body".to_string(),
32+
HistoryContentKey::BlockReceipts(_) => "receipt".to_string(),
33+
HistoryContentKey::EpochAccumulator(_) => "epoch accumulator".to_string(),
34+
};
35+
format!(
36+
"{}{} {}",
37+
header_with_proof.header.number,
38+
get_flair(header_with_proof.header.number),
39+
content_type
40+
)
41+
} else {
42+
unreachable!("History test dated is formatted incorrectly")
43+
};
44+
result.push(comment)
45+
}
46+
result
47+
}
48+
49+
#[tokio::main]
50+
async fn main() {
51+
tracing_subscriber::fmt::init();
52+
53+
let mut suite = Suite {
54+
name: "trin-bridge-tests".to_string(),
55+
description: "The portal bridge test suite".to_string(),
56+
tests: vec![],
57+
};
58+
59+
suite.add(TestSpec {
60+
name: "Trin bridge tests".to_string(),
61+
description: "".to_string(),
62+
always_run: false,
63+
run: test_portal_bridge,
64+
client: None,
65+
});
66+
67+
let sim = Simulation::new();
68+
run_suite(sim, suite).await;
69+
}
70+
71+
async fn run_suite(host: Simulation, suite: Suite) {
72+
let name = suite.clone().name;
73+
let description = suite.clone().description;
74+
75+
let suite_id = host.start_suite(name, description, "".to_string()).await;
76+
77+
for test in &suite.tests {
78+
test.run_test(host.clone(), suite_id, suite.clone()).await;
79+
}
80+
81+
host.end_suite(suite_id).await;
82+
}
83+
84+
dyn_async! {
85+
async fn test_portal_bridge<'a> (test: &'a mut Test, _client: Option<Client>) {
86+
// Get all available portal clients
87+
let clients = test.sim.client_types().await;
88+
if !clients.iter().any(|client_definition| client_definition.name == *TRIN_BRIDGE_CLIENT_TYPE) {
89+
panic!("This simulator is required to be ran with client `trin-bridge`")
90+
}
91+
let clients: Vec<ClientDefinition> = clients.into_iter().filter(|client| client.name != *TRIN_BRIDGE_CLIENT_TYPE).collect();
92+
93+
// Iterate over all possible pairings of clients and run the tests (including self-pairings)
94+
for (client_a, client_b) in clients.iter().cartesian_product(clients.iter()) {
95+
test.run(
96+
NClientTestSpec {
97+
name: format!("Bridge test. A:{} --> B:{}", client_a.name, client_b.name),
98+
description: "".to_string(),
99+
always_run: false,
100+
run: test_bridge,
101+
environments: None,
102+
test_data: None,
103+
clients: vec![client_a.clone(), client_b.clone()],
104+
}
105+
).await;
106+
}
107+
}
108+
}
109+
110+
dyn_async! {
111+
async fn test_bridge<'a>(clients: Vec<Client>, _: Option<Vec<(String, String)>>) {
112+
let (client_a, client_b) = match clients.iter().collect_tuple() {
113+
Some((client_a, client_b)) => (client_a, client_b),
114+
None => {
115+
panic!("Unable to get expected amount of clients from NClientTestSpec");
116+
}
117+
};
118+
119+
let client_b_enr = match client_b.rpc.node_info().await {
120+
Ok(node_info) => node_info.enr,
121+
Err(err) => {
122+
panic!("Error getting node info: {err:?}");
123+
}
124+
};
125+
match HistoryNetworkApiClient::add_enr(&client_a.rpc, client_b_enr.clone()).await {
126+
Ok(response) => if !response {
127+
panic!("AddEnr expected to get true and instead got false")
128+
},
129+
Err(err) => panic!("{}", &err.to_string()),
130+
}
131+
132+
let client_a_enr = match client_a.rpc.node_info().await {
133+
Ok(node_info) => node_info.enr,
134+
Err(err) => {
135+
panic!("Error getting node info: {err:?}");
136+
}
137+
};
138+
client_a.test.start_client(TRIN_BRIDGE_CLIENT_TYPE.to_string(), Some(HashMap::from([(BOOTNODES_ENVIRONMENT_VARIABLE.to_string(), client_a_enr.to_base64()), (HIVE_CHECK_LIVE_PORT.to_string(), 0.to_string())]))).await;
139+
140+
141+
142+
// With default node settings nodes should be storing all content
143+
let values = std::fs::read_to_string(TEST_DATA_FILE_PATH)
144+
.expect("cannot find test asset");
145+
let values: Value = serde_yaml::from_str(&values).unwrap();
146+
let content_vec: Vec<(HistoryContentKey, HistoryContentValue)> = values.as_sequence().unwrap().iter().map(|value| {
147+
let content_key: HistoryContentKey =
148+
serde_yaml::from_value(value.get("content_key").unwrap().clone()).unwrap();
149+
let content_value: HistoryContentValue =
150+
serde_yaml::from_value(value.get("content_value").unwrap().clone()).unwrap();
151+
(content_key, content_value)
152+
}).collect();
153+
let comments = process_content(content_vec.clone());
154+
155+
// wait content_vec.len() seconds for data to propagate, giving more time if more items are propagating
156+
tokio::time::sleep(Duration::from_secs(content_vec.len() as u64)).await;
157+
158+
let mut result = vec![];
159+
for (index, (content_key, content_value)) in content_vec.into_iter().enumerate() {
160+
match client_b.rpc.local_content(content_key.clone()).await {
161+
Ok(possible_content) => {
162+
match possible_content {
163+
PossibleHistoryContentValue::ContentPresent(content) => {
164+
if content != content_value {
165+
result.push(format!("Error content received for block {} was different then expected", comments[index]));
166+
}
167+
}
168+
PossibleHistoryContentValue::ContentAbsent => {
169+
result.push(format!("Error content for block {} was absent", comments[index]));
170+
}
171+
}
172+
}
173+
Err(err) => {
174+
panic!("Unable to get received content: {err:?}");
175+
}
176+
}
177+
}
178+
179+
if !result.is_empty() {
180+
panic!("Client B: {:?}", result);
181+
}
182+
}
183+
}

0 commit comments

Comments
 (0)