Skip to content

Commit f5b0316

Browse files
committed
Addition of initial seed check logic
1 parent 6c01204 commit f5b0316

File tree

5 files changed

+226
-0
lines changed

5 files changed

+226
-0
lines changed

Cargo.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ path = "src/bin/grin.rs"
2222
[dependencies]
2323
blake2-rfc = "0.2"
2424
chrono = "0.4.11"
25+
thiserror = "1"
2526
clap = { version = "2.33", features = ["yaml"] }
2627
ctrlc = { version = "3.1", features = ["termination"] }
2728
cursive_table_view = "0.15.0"
2829
humansize = "1.1.0"
2930
serde = "1"
31+
serde_derive = "1"
3032
futures = "0.3.19"
3133
serde_json = "1"
3234
log = "0.4"
@@ -40,6 +42,7 @@ grin_keychain = { path = "./keychain", version = "5.4.0-alpha.0" }
4042
grin_p2p = { path = "./p2p", version = "5.4.0-alpha.0" }
4143
grin_servers = { path = "./servers", version = "5.4.0-alpha.0" }
4244
grin_util = { path = "./util", version = "5.4.0-alpha.0" }
45+
grin_store = { path = "./store", version = "5.4.0-alpha.0" }
4346

4447
[dependencies.cursive]
4548
version = "0.21"

src/bin/grin.rs

+5
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ use grin_util as util;
3434
use grin_util::logger::LogEntry;
3535
use std::sync::mpsc;
3636

37+
#[macro_use]
38+
extern crate serde_derive;
39+
extern crate serde_json;
40+
3741
mod cmd;
42+
mod tools;
3843
pub mod tui;
3944

4045
// include build information

src/bin/tools/mod.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2024 The Grin Developers
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
/// Grin tools
16+
mod seedcheck;

src/bin/tools/seedcheck.rs

+200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// Copyright 2024 The Grin Developers
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
/// Relatively self-contained seed health checker
16+
use std::sync::Arc;
17+
18+
use grin_core::core::hash::Hashed;
19+
use grin_core::pow::Difficulty;
20+
use grin_core::{genesis, global};
21+
use grin_p2p as p2p;
22+
use grin_servers::{resolve_dns_to_addrs, MAINNET_DNS_SEEDS, TESTNET_DNS_SEEDS};
23+
use std::net::{SocketAddr, TcpStream};
24+
use std::time::Duration;
25+
26+
use thiserror::Error;
27+
28+
use chrono::*;
29+
30+
#[derive(Error, Debug)]
31+
pub enum SeedCheckError {
32+
#[error("Seed Connect Error {0}")]
33+
SeedConnectError(String),
34+
#[error("Grin Store Error {0}")]
35+
StoreError(String),
36+
}
37+
38+
impl From<p2p::Error> for SeedCheckError {
39+
fn from(e: p2p::Error) -> Self {
40+
SeedCheckError::SeedConnectError(format!("{:?}", e))
41+
}
42+
}
43+
44+
impl From<grin_store::lmdb::Error> for SeedCheckError {
45+
fn from(e: grin_store::lmdb::Error) -> Self {
46+
SeedCheckError::StoreError(format!("{:?}", e))
47+
}
48+
}
49+
50+
#[derive(Serialize, Deserialize, Debug)]
51+
pub struct SeedCheckResults {
52+
pub mainnet: Vec<SeedCheckResult>,
53+
pub testnet: Vec<SeedCheckResult>,
54+
}
55+
56+
impl Default for SeedCheckResults {
57+
fn default() -> Self {
58+
Self {
59+
mainnet: vec![],
60+
testnet: vec![],
61+
}
62+
}
63+
}
64+
65+
#[derive(Debug, Serialize, Deserialize)]
66+
pub struct SeedCheckResult {
67+
pub url: String,
68+
pub dns_resolutions_found: bool,
69+
pub success: bool,
70+
pub successful_attempts: Vec<SeedCheckConnectAttempt>,
71+
pub unsuccessful_attempts: Vec<SeedCheckConnectAttempt>,
72+
}
73+
74+
impl Default for SeedCheckResult {
75+
fn default() -> Self {
76+
Self {
77+
url: "".into(),
78+
dns_resolutions_found: false,
79+
success: false,
80+
successful_attempts: vec![],
81+
unsuccessful_attempts: vec![],
82+
}
83+
}
84+
}
85+
86+
#[derive(Debug, Serialize, Deserialize)]
87+
pub struct SeedCheckConnectAttempt {
88+
pub ip_addr: String,
89+
pub handshake_success: bool,
90+
pub user_agent: Option<String>,
91+
pub capabilities: Option<String>,
92+
}
93+
94+
pub fn check_seeds(is_testnet: bool) -> Vec<SeedCheckResult> {
95+
let mut result = vec![];
96+
let (default_seeds, port) = match is_testnet {
97+
true => (TESTNET_DNS_SEEDS, "13414"),
98+
false => (MAINNET_DNS_SEEDS, "3414"),
99+
};
100+
101+
if is_testnet {
102+
global::set_local_chain_type(global::ChainTypes::Testnet);
103+
}
104+
105+
for s in default_seeds.iter() {
106+
info!("Checking seed health for {}", s);
107+
let mut seed_result = SeedCheckResult::default();
108+
seed_result.url = s.to_string();
109+
let resolved_dns_entries = resolve_dns_to_addrs(&vec![format!("{}:{}", s, port)]);
110+
if resolved_dns_entries.is_empty() {
111+
info!("FAIL - No dns entries found for {}", s);
112+
result.push(seed_result);
113+
continue;
114+
}
115+
seed_result.dns_resolutions_found = true;
116+
// Check backwards, last contains the latest (at least on my machine!)
117+
for r in resolved_dns_entries.iter().rev() {
118+
let res = check_seed_health(*r, is_testnet);
119+
if let Ok(p) = res {
120+
info!(
121+
"SUCCESS - Performed Handshake with seed for {} at {}. {} - {:?}",
122+
s, r, p.info.user_agent, p.info.capabilities
123+
);
124+
//info!("{:?}", p);
125+
seed_result.success = true;
126+
seed_result
127+
.successful_attempts
128+
.push(SeedCheckConnectAttempt {
129+
ip_addr: r.to_string(),
130+
handshake_success: true,
131+
user_agent: Some(p.info.user_agent),
132+
capabilities: Some(format!("{:?}", p.info.capabilities)),
133+
});
134+
} else {
135+
seed_result
136+
.unsuccessful_attempts
137+
.push(SeedCheckConnectAttempt {
138+
ip_addr: r.to_string(),
139+
handshake_success: false,
140+
user_agent: None,
141+
capabilities: None,
142+
});
143+
}
144+
}
145+
146+
if !seed_result.success {
147+
info!(
148+
"FAIL - Unable to handshake at any known DNS resolutions for {}",
149+
s
150+
);
151+
}
152+
153+
result.push(seed_result);
154+
}
155+
result
156+
}
157+
158+
fn check_seed_health(addr: p2p::PeerAddr, is_testnet: bool) -> Result<p2p::Peer, SeedCheckError> {
159+
let capabilities = p2p::types::Capabilities::default();
160+
let config = p2p::types::P2PConfig::default();
161+
let adapter = Arc::new(p2p::DummyAdapter {});
162+
let peers = Arc::new(p2p::Peers::new(
163+
p2p::store::PeerStore::new("peer_store_root")?,
164+
adapter,
165+
config.clone(),
166+
));
167+
let genesis_hash = match is_testnet {
168+
true => genesis::genesis_test().hash(),
169+
false => genesis::genesis_main().hash(),
170+
};
171+
172+
let handshake = p2p::handshake::Handshake::new(genesis_hash, config.clone());
173+
174+
match TcpStream::connect_timeout(&addr.0, Duration::from_secs(5)) {
175+
Ok(stream) => {
176+
let addr = SocketAddr::new(config.host, config.port);
177+
let total_diff = Difficulty::from_num(1);
178+
179+
let peer = p2p::Peer::connect(
180+
stream,
181+
capabilities,
182+
total_diff,
183+
p2p::PeerAddr(addr),
184+
&handshake,
185+
peers,
186+
)?;
187+
Ok(peer)
188+
}
189+
Err(e) => {
190+
trace!(
191+
"connect_peer: on {}:{}. Could not connect to {}: {:?}",
192+
config.host,
193+
config.port,
194+
addr,
195+
e
196+
);
197+
Err(p2p::Error::Connection(e).into())
198+
}
199+
}
200+
}

0 commit comments

Comments
 (0)