Skip to content

Commit 9378ffe

Browse files
BertBert
authored andcommitted
GH-598: merged master in
2 parents 2d7c20e + ded8ac0 commit 9378ffe

68 files changed

Lines changed: 3381 additions & 1786 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

masq/src/command_factory.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::commands::descriptor_command::DescriptorCommand;
1111
use crate::commands::exit_location_command::SetExitLocationCommand;
1212
use crate::commands::financials_command::FinancialsCommand;
1313
use crate::commands::generate_wallets_command::GenerateWalletsCommand;
14+
use crate::commands::neighborhood_graph_command::GetNeighborhoodGraphCommand;
1415
use crate::commands::recover_wallets_command::RecoverWalletsCommand;
1516
use crate::commands::scan_command::ScanCommand;
1617
use crate::commands::set_configuration_command::SetConfigurationCommand;
@@ -65,6 +66,10 @@ impl CommandFactory for CommandFactoryReal {
6566
Ok(command) => Box::new(command),
6667
Err(msg) => return Err(CommandSyntax(msg)),
6768
},
69+
"neighborhood-graph" => match GetNeighborhoodGraphCommand::new(pieces) {
70+
Ok(command) => Box::new(command),
71+
Err(msg) => return Err(CommandSyntax(msg)),
72+
},
6873
"recover-wallets" => match RecoverWalletsCommand::new(pieces) {
6974
Ok(command) => Box::new(command),
7075
Err(msg) => return Err(CommandSyntax(msg)),
@@ -292,6 +297,21 @@ mod tests {
292297
);
293298
}
294299

300+
#[test]
301+
fn factory_produces_neighborhood_graph() {
302+
let subject = CommandFactoryReal::new();
303+
304+
let command = subject.make(&["neighborhood-graph".to_string()]).unwrap();
305+
306+
assert_eq!(
307+
command
308+
.as_any()
309+
.downcast_ref::<GetNeighborhoodGraphCommand>()
310+
.unwrap(),
311+
&GetNeighborhoodGraphCommand {}
312+
);
313+
}
314+
295315
#[test]
296316
fn complains_about_set_configuration_command_with_no_parameters() {
297317
let subject = CommandFactoryReal::new();

masq/src/commands/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ pub mod setup_command;
1717
pub mod shutdown_command;
1818
pub mod start_command;
1919
pub mod wallet_addresses_command;
20+
21+
pub mod neighborhood_graph_command;
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use clap::{App, SubCommand};
2+
use masq_lib::{as_any_ref_in_trait_impl, short_writeln};
3+
4+
use crate::command_context::CommandContext;
5+
use crate::commands::commands_common::CommandError::Payload;
6+
use crate::commands::commands_common::{
7+
transaction, Command, CommandError, STANDARD_COMMAND_TIMEOUT_MILLIS,
8+
};
9+
use masq_lib::messages::{UiGetNeighborhoodGraphRequest, UiGetNeighborhoodGraphResponse};
10+
11+
const NEIGHBORHOOD_GRAPH_HELP: &str = "Use this command plainly, without any flags or arguments. The result will be delivered in digraph format, documentation at https://graphviz.org/documentation/";
12+
13+
pub fn get_neighborhood_graph_subcommand() -> App<'static, 'static> {
14+
SubCommand::with_name("neighborhood-graph").about(NEIGHBORHOOD_GRAPH_HELP)
15+
}
16+
17+
#[derive(Debug, PartialEq, Eq)]
18+
pub struct GetNeighborhoodGraphCommand {}
19+
20+
impl GetNeighborhoodGraphCommand {
21+
pub fn new(pieces: &[String]) -> Result<Self, String> {
22+
match get_neighborhood_graph_subcommand().get_matches_from_safe(pieces) {
23+
Ok(_) => Ok(GetNeighborhoodGraphCommand {}),
24+
Err(e) => Err(format!("GetNeighborhoodGraphCommand {}", e)),
25+
}
26+
}
27+
}
28+
29+
impl Command for GetNeighborhoodGraphCommand {
30+
fn execute(&self, context: &mut dyn CommandContext) -> Result<(), CommandError> {
31+
let input = UiGetNeighborhoodGraphRequest {};
32+
let output: Result<UiGetNeighborhoodGraphResponse, CommandError> =
33+
transaction(input, context, STANDARD_COMMAND_TIMEOUT_MILLIS);
34+
match output {
35+
Ok(neighborhood_graph) => {
36+
short_writeln!(
37+
context.stdout(),
38+
"Graph of the Node's neighborhood database: {}",
39+
neighborhood_graph.graph.as_str()
40+
);
41+
Ok(())
42+
}
43+
Err(Payload(code, message)) => {
44+
short_writeln!(context.stderr(), "code: {}\nmessage: {}", code, message);
45+
Err(Payload(code, message))
46+
}
47+
Err(err) => {
48+
short_writeln!(context.stderr(), "Error: {}", err);
49+
Err(err)
50+
}
51+
}
52+
}
53+
54+
as_any_ref_in_trait_impl!();
55+
}
56+
57+
#[cfg(test)]
58+
pub mod tests {
59+
use super::*;
60+
use crate::test_utils::mocks::CommandContextMock;
61+
use masq_lib::messages::ToMessageBody;
62+
use std::sync::{Arc, Mutex};
63+
64+
#[test]
65+
fn can_deserialize_ui_get_neighborhood_graph() {
66+
let transact_params_arc = Arc::new(Mutex::new(vec![]));
67+
let mut context = CommandContextMock::new()
68+
.transact_params(&transact_params_arc)
69+
.transact_result(Ok(UiGetNeighborhoodGraphResponse {
70+
graph: "digraph db { \"AQIDBA\" [label=\"AR v0 AU\\nAQIDBA\\n1.2.3.4:1234\"]; \"HZ5vwwJPhfUZVy85E76GZUUam9SMgyaw+QaZvAMuizo\" [label=\"AR v0 ZZ\\nHZ5vwwJP\\n9.9.9.9:9999\"] [style=filled]; \"AgMEBQ\" [label=\"AR v0 FR\\nAgMEBQ\\n2.3.4.5:2345\"]; \"AwQFBg\" [label=\"AR v0 CN\\nAwQFBg\\n3.4.5.6:3456\"]; \"BAUGBw\" [label=\"AR v0 US\\nBAUGBw\\n4.5.6.7:4567\"]; \"AQIDBA\" -> \"HZ5vwwJPhfUZVy85E76GZUUam9SMgyaw+QaZvAMuizo\"; \"AQIDBA\" -> \"AgMEBQ\"; \"HZ5vwwJPhfUZVy85E76GZUUam9SMgyaw+QaZvAMuizo\" -> \"AQIDBA\"; \"AgMEBQ\" -> \"AwQFBg\"; \"AgMEBQ\" -> \"AQIDBA\"; \"AwQFBg\" -> \"BAUGBw\"; \"AwQFBg\" -> \"AgMEBQ\"; \"BAUGBw\" -> \"AwQFBg\"; }".to_string()
71+
}.tmb(0)));
72+
let stderr_arc = context.stderr_arc();
73+
let stdout_arc = context.stdout_arc();
74+
let subject =
75+
GetNeighborhoodGraphCommand::new(&["neighborhood-graph".to_string()]).unwrap();
76+
77+
let result = subject.execute(&mut context);
78+
79+
assert_eq!(result, Ok(()));
80+
let expected_request = UiGetNeighborhoodGraphRequest {};
81+
let transact_params = transact_params_arc.lock().unwrap();
82+
let expected_message_body = expected_request.tmb(0);
83+
assert_eq!(
84+
transact_params.as_slice(),
85+
&[(expected_message_body, STANDARD_COMMAND_TIMEOUT_MILLIS)]
86+
);
87+
let stdout = stdout_arc.lock().unwrap();
88+
let graph_str = "Graph of the Node's neighborhood database: digraph db { \"AQIDBA\" [label=\"AR v0 AU\\nAQIDBA\\n1.2.3.4:1234\"]; \"HZ5vwwJPhfUZVy85E76GZUUam9SMgyaw+QaZvAMuizo\" [label=\"AR v0 ZZ\\nHZ5vwwJP\\n9.9.9.9:9999\"] [style=filled]; \"AgMEBQ\" [label=\"AR v0 FR\\nAgMEBQ\\n2.3.4.5:2345\"]; \"AwQFBg\" [label=\"AR v0 CN\\nAwQFBg\\n3.4.5.6:3456\"]; \"BAUGBw\" [label=\"AR v0 US\\nBAUGBw\\n4.5.6.7:4567\"]; \"AQIDBA\" -> \"HZ5vwwJPhfUZVy85E76GZUUam9SMgyaw+QaZvAMuizo\"; \"AQIDBA\" -> \"AgMEBQ\"; \"HZ5vwwJPhfUZVy85E76GZUUam9SMgyaw+QaZvAMuizo\" -> \"AQIDBA\"; \"AgMEBQ\" -> \"AwQFBg\"; \"AgMEBQ\" -> \"AQIDBA\"; \"AwQFBg\" -> \"BAUGBw\"; \"AwQFBg\" -> \"AgMEBQ\"; \"BAUGBw\" -> \"AwQFBg\"; }\n";
89+
assert_eq!(&stdout.get_string(), graph_str);
90+
let stderr = stderr_arc.lock().unwrap();
91+
assert_eq!(&stderr.get_string(), "");
92+
}
93+
}

masq/src/schema.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::commands::descriptor_command::descriptor_subcommand;
1111
use crate::commands::exit_location_command::exit_location_subcommand;
1212
use crate::commands::financials_command::args_validation::financials_subcommand;
1313
use crate::commands::generate_wallets_command::generate_wallets_subcommand;
14+
use crate::commands::neighborhood_graph_command::get_neighborhood_graph_subcommand;
1415
use crate::commands::recover_wallets_command::recover_wallets_subcommand;
1516
use crate::commands::scan_command::scan_subcommand;
1617
use crate::commands::set_configuration_command::set_configuration_subcommand;
@@ -71,6 +72,7 @@ pub fn app() -> App<'static, 'static> {
7172
.subcommand(exit_location_subcommand())
7273
.subcommand(financials_subcommand())
7374
.subcommand(generate_wallets_subcommand())
75+
.subcommand(get_neighborhood_graph_subcommand())
7476
.subcommand(recover_wallets_subcommand())
7577
.subcommand(scan_subcommand())
7678
.subcommand(set_configuration_subcommand())

masq_lib/src/blockchains/chains.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::constants::{
77
POLYGON_AMOY_FULL_IDENTIFIER, POLYGON_MAINNET_FULL_IDENTIFIER,
88
};
99
use serde_derive::{Deserialize, Serialize};
10+
use std::fmt::{Display, Formatter};
1011

1112
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
1213
pub enum Chain {
@@ -47,6 +48,21 @@ impl From<&str> for Chain {
4748
}
4849
}
4950

51+
impl Display for Chain {
52+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
53+
let chain_name = match self {
54+
Chain::EthMainnet => ETH_MAINNET_FULL_IDENTIFIER,
55+
Chain::EthRopsten => ETH_ROPSTEN_FULL_IDENTIFIER,
56+
Chain::PolyMainnet => POLYGON_MAINNET_FULL_IDENTIFIER,
57+
Chain::PolyAmoy => POLYGON_AMOY_FULL_IDENTIFIER,
58+
Chain::BaseMainnet => BASE_MAINNET_FULL_IDENTIFIER,
59+
Chain::BaseSepolia => BASE_SEPOLIA_FULL_IDENTIFIER,
60+
Chain::Dev => DEV_CHAIN_FULL_IDENTIFIER,
61+
};
62+
write!(f, "{}", chain_name)
63+
}
64+
}
65+
5066
impl Chain {
5167
pub fn rec(&self) -> &BlockchainRecord {
5268
CHAINS
@@ -160,6 +176,37 @@ mod tests {
160176
})
161177
}
162178

179+
#[test]
180+
fn display_is_properly_implemented() {
181+
let chains = [
182+
Chain::EthMainnet,
183+
Chain::EthRopsten,
184+
Chain::PolyMainnet,
185+
Chain::PolyAmoy,
186+
Chain::BaseMainnet,
187+
Chain::BaseSepolia,
188+
Chain::Dev,
189+
];
190+
191+
let strings = chains
192+
.iter()
193+
.map(|chain| chain.to_string())
194+
.collect::<Vec<_>>();
195+
196+
assert_eq!(
197+
strings,
198+
vec![
199+
ETH_MAINNET_FULL_IDENTIFIER.to_string(),
200+
ETH_ROPSTEN_FULL_IDENTIFIER.to_string(),
201+
POLYGON_MAINNET_FULL_IDENTIFIER.to_string(),
202+
POLYGON_AMOY_FULL_IDENTIFIER.to_string(),
203+
BASE_MAINNET_FULL_IDENTIFIER.to_string(),
204+
BASE_SEPOLIA_FULL_IDENTIFIER.to_string(),
205+
DEV_CHAIN_FULL_IDENTIFIER.to_string(),
206+
]
207+
);
208+
}
209+
163210
fn assert_mainnet_exist() {
164211
assert!(CHAINS
165212
.iter()

masq_lib/src/constants.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::data_version::DataVersion;
55
use const_format::concatcp;
66

77
pub const DEFAULT_CHAIN: Chain = Chain::PolyMainnet;
8-
pub const CURRENT_SCHEMA_VERSION: usize = 11;
8+
pub const CURRENT_SCHEMA_VERSION: usize = 12;
99

1010
pub const HIGHEST_RANDOM_CLANDESTINE_PORT: u16 = 9999;
1111
pub const HTTP_PORT: u16 = 80;

masq_lib/src/messages.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,18 @@ pub struct UiSetExitLocationResponse {
911911
}
912912
conversation_message!(UiSetExitLocationResponse, "exitLocation");
913913

914+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
915+
pub struct UiGetNeighborhoodGraphRequest {}
916+
917+
conversation_message!(UiGetNeighborhoodGraphRequest, "neighborhoodGraph");
918+
919+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
920+
pub struct UiGetNeighborhoodGraphResponse {
921+
pub graph: String,
922+
}
923+
924+
conversation_message!(UiGetNeighborhoodGraphResponse, "neighborhoodGraph");
925+
914926
#[cfg(test)]
915927
mod tests {
916928
use super::*;

masq_lib/src/test_utils/environment_guard.rs

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
use lazy_static::lazy_static;
44
use std::ffi::OsString;
55
use std::sync::{Mutex, MutexGuard};
6+
use std::thread::ThreadId;
67

78
lazy_static! {
89
static ref ENVIRONMENT_GUARD_MUTEX: Mutex<()> = Mutex::new(());
10+
static ref ENVIRONMENT_GUARD_THREAD_ID: Mutex<Option<ThreadId>> = Mutex::new(None);
911
static ref CLAP_GUARD_MUTEX: Mutex<()> = Mutex::new(());
1012
static ref LOGFILE_NAME_GUARD_MUTEX: Mutex<()> = Mutex::new(());
1113
}
@@ -17,10 +19,9 @@ pub struct ConcurrencyPreventer<'a> {
1719
impl<'a> ConcurrencyPreventer<'a> {
1820
pub fn new(mutex: &'a Mutex<()>) -> ConcurrencyPreventer<'a> {
1921
ConcurrencyPreventer {
20-
_lock: match mutex.lock() {
21-
Ok(guard) => guard,
22-
Err(poisoned) => poisoned.into_inner(),
23-
},
22+
_lock: mutex
23+
.lock()
24+
.unwrap_or_else(|poisoned| poisoned.into_inner()),
2425
}
2526
}
2627
}
@@ -43,16 +44,50 @@ impl<'a> Drop for EnvironmentGuard<'a> {
4344
self.environment
4445
.iter()
4546
.for_each(|(name, value)| std::env::set_var(name, value));
47+
let mut thread_id_guard = ENVIRONMENT_GUARD_THREAD_ID
48+
.lock()
49+
.unwrap_or_else(|e| e.into_inner());
50+
*thread_id_guard = None; // Clear the thread ID guard
4651
}
4752
}
4853

4954
impl<'a> EnvironmentGuard<'a> {
5055
pub fn new() -> EnvironmentGuard<'a> {
51-
EnvironmentGuard {
52-
_preventer: ConcurrencyPreventer::new(&ENVIRONMENT_GUARD_MUTEX),
53-
environment: std::env::vars_os().collect(),
56+
// TODO: Consider a #[cfg(not(test))] line here to panic if production code tries this
57+
loop {
58+
{
59+
let mut thread_id_guard = Self::thread_id_guard();
60+
let current_thread_id = std::thread::current().id();
61+
match *thread_id_guard {
62+
Some(id) => {
63+
if id == current_thread_id {
64+
panic!(
65+
"Thread {:?} is trying to claim multiple EnvironmentGuards",
66+
current_thread_id
67+
);
68+
}
69+
}
70+
None => {
71+
// Set thread ID, claim environment guard, release thread ID lock
72+
*thread_id_guard = Some(current_thread_id);
73+
return EnvironmentGuard {
74+
_preventer: ConcurrencyPreventer::new(&ENVIRONMENT_GUARD_MUTEX),
75+
environment: std::env::vars_os().collect(),
76+
};
77+
}
78+
}
79+
}
80+
// Somebody else has the EnvironmentGuard. We've released the thread ID lock; now
81+
// wait for a little while and try again.
82+
std::thread::sleep(std::time::Duration::from_millis(10));
5483
}
5584
}
85+
86+
pub fn thread_id_guard() -> MutexGuard<'a, Option<ThreadId>> {
87+
ENVIRONMENT_GUARD_THREAD_ID
88+
.lock()
89+
.unwrap_or_else(|poisoned| poisoned.into_inner())
90+
}
5691
}
5792

5893
impl<'a> Default for EnvironmentGuard<'a> {

multinode_integration_tests/src/masq_node_server.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,24 @@
22

33
use crate::masq_node_cluster::DockerHostSocketAddr;
44
use crate::utils;
5-
use std::io;
5+
use crossbeam_channel::{unbounded, Receiver, Sender};
66
use std::net::{Shutdown, SocketAddr, TcpListener, TcpStream};
77
use std::time::Duration;
8+
use std::{io, thread};
89

910
pub struct MASQNodeServer {
11+
docker_host_addr: Option<DockerHostSocketAddr>,
1012
local_addr: SocketAddr,
11-
listener: TcpListener,
1213
stream_opt: Option<TcpStream>,
1314
}
1415

1516
impl MASQNodeServer {
1617
pub fn new(port: u16) -> MASQNodeServer {
17-
let socket_addr = DockerHostSocketAddr::new(port);
18-
let listener = TcpListener::bind(socket_addr).unwrap();
18+
let dummy_listener = TcpListener::bind(DockerHostSocketAddr::new(port)).unwrap();
19+
let local_addr = dummy_listener.local_addr().unwrap();
1920
MASQNodeServer {
20-
local_addr: listener.local_addr().unwrap(),
21-
listener,
21+
docker_host_addr: Some(DockerHostSocketAddr::new(port)),
22+
local_addr,
2223
stream_opt: None,
2324
}
2425
}
@@ -37,10 +38,17 @@ impl MASQNodeServer {
3738
pub fn wait_for_chunk(&mut self, duration: Duration) -> Result<Vec<u8>, io::Error> {
3839
match &mut self.stream_opt {
3940
None => {
40-
let (stream, _) = self.listener.accept().unwrap();
41-
stream
42-
.set_read_timeout(Some(Duration::from_millis(250)))
43-
.unwrap();
41+
let (tx, rx): (Sender<TcpStream>, Receiver<TcpStream>) = unbounded();
42+
let local_addr = self.docker_host_addr.take().unwrap();
43+
let listener = TcpListener::bind(local_addr).unwrap();
44+
thread::spawn(move || {
45+
let (stream, _) = listener.accept().unwrap();
46+
stream
47+
.set_read_timeout(Some(Duration::from_millis(250)))
48+
.unwrap();
49+
tx.send(stream).unwrap();
50+
});
51+
let stream = rx.recv_timeout(duration).unwrap();
4452
self.stream_opt = Some(stream);
4553
self.wait_for_chunk(duration)
4654
}

0 commit comments

Comments
 (0)