node: New relay configuration for nodes

Relay now has three values: "always", "never" and "auto".

The first two operate like the previous `true`/`false`. The
new (default) value turns relaying on for nodes with public (external)
addresses and off for nodes without. This should reduce redundant traffic on
the network, that was causing the rate limiters to trigger.
This commit is contained in:
cloudhead 2024-05-01 16:06:23 +02:00
parent 1708ddf777
commit a9b94b0ad6
No known key found for this signature in database
8 changed files with 165 additions and 112 deletions

View File

@ -29,7 +29,7 @@ $ rad config
"tor": null,
"network": "main",
"log": "INFO",
"relay": true,
"relay": "auto",
"limits": {
"routingMaxSize": 1000,
"routingMaxAge": 604800,

View File

@ -11,13 +11,13 @@ If we check the sync status, we see that our peers are out of sync.
```
$ rad sync status --sort-by alias
╭──────────────────────────────────────────────────────────────────────────────────────────╮
│ ● Node Address Status Tip Timestamp │
├──────────────────────────────────────────────────────────────────────────────────────────┤
│ ● alice (you) alice.radicle.xyz:8776 f209c9f [ ... ] │
│ ● bob z6Mkt67…v4N1tRk bob.radicle.xyz:8776 out-of-sync f209c9f [ ... ] │
│ ● eve z6Mkux1…nVhib7Z eve.radicle.xyz:8776 out-of-sync f209c9f [ ... ] │
╰──────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────
│ ● Node Address Status Tip Timestamp │
├──────────────────────────────────────────────────────────────────────────────────────────────
│ ● alice (you) alice.radicle.example:8776 f209c9f [ ... ] │
│ ● bob z6Mkt67…v4N1tRk bob.radicle.example:8776 out-of-sync f209c9f [ ... ] │
│ ● eve z6Mkux1…nVhib7Z eve.radicle.example:8776 out-of-sync f209c9f [ ... ] │
╰──────────────────────────────────────────────────────────────────────────────────────────────
```
Now let's run `rad sync`. This will announce the issue refs to the network and
@ -33,13 +33,13 @@ Now, when we run `rad sync status` again, we can see that `bob` and
```
$ rad sync status --sort-by alias
╭─────────────────────────────────────────────────────────────────────────────────────╮
│ ● Node Address Status Tip Timestamp │
├─────────────────────────────────────────────────────────────────────────────────────┤
│ ● alice (you) alice.radicle.xyz:8776 a9ce0d1 [ ... ] │
│ ● bob z6Mkt67…v4N1tRk bob.radicle.xyz:8776 synced a9ce0d1 [ ... ] │
│ ● eve z6Mkux1…nVhib7Z eve.radicle.xyz:8776 synced a9ce0d1 [ ... ] │
╰─────────────────────────────────────────────────────────────────────────────────────╯
╭─────────────────────────────────────────────────────────────────────────────────────────
│ ● Node Address Status Tip Timestamp │
├─────────────────────────────────────────────────────────────────────────────────────────
│ ● alice (you) alice.radicle.example:8776 a9ce0d1 [ ... ] │
│ ● bob z6Mkt67…v4N1tRk bob.radicle.example:8776 synced a9ce0d1 [ ... ] │
│ ● eve z6Mkux1…nVhib7Z eve.radicle.example:8776 synced a9ce0d1 [ ... ] │
╰─────────────────────────────────────────────────────────────────────────────────────────
```
If we try to sync again after the nodes have synced, we will already

View File

@ -39,6 +39,7 @@ mod config {
pub fn seed(alias: &'static str) -> Config {
Config {
network: Network::Test,
relay: node::config::Relay::Always,
limits: Limits {
rate: RateLimits {
inbound: RateLimit {
@ -52,18 +53,25 @@ mod config {
},
..Limits::default()
},
external_addresses: vec![node::Address::from_str(&format!(
"{alias}.radicle.example:8776"
))
.unwrap()],
..node(alias)
}
}
/// Relay node config.
pub fn relay(alias: &'static str) -> Config {
Config {
relay: node::config::Relay::Always,
..node(alias)
}
}
/// Test node config.
pub fn node(alias: &'static str) -> Config {
Config {
external_addresses: vec![
node::Address::from_str(&format!("{alias}.radicle.xyz:8776")).unwrap(),
],
..Config::test(Alias::new(alias))
}
Config::test(Alias::new(alias))
}
/// Test profile config.
@ -965,9 +973,9 @@ fn rad_review_by_hunk() {
#[test]
fn rad_patch_delete() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let seed = environment.node(Config::test(Alias::new("seed")));
let alice = environment.node(config::relay("alice"));
let bob = environment.node(config::relay("bob"));
let seed = environment.node(config::relay("seed"));
let working = environment.tmp().join("working");
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
@ -1332,9 +1340,9 @@ fn rad_clone_connect() {
#[test]
fn rad_sync_without_node() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let mut eve = environment.node(Config::test(Alias::new("eve")));
let alice = environment.node(config::seed("alice"));
let bob = environment.node(config::seed("bob"));
let mut eve = environment.node(config::seed("eve"));
let rid = RepoId::from_urn("rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5").unwrap();
eve.policies.seed(&rid, Scope::All).unwrap();
@ -1738,9 +1746,9 @@ fn test_cob_deletion() {
fn rad_sync() {
let mut environment = Environment::new();
let working = environment.tmp().join("working");
let alice = environment.node(config::node("alice"));
let bob = environment.node(config::node("bob"));
let eve = environment.node(config::node("eve"));
let alice = environment.node(config::seed("alice"));
let bob = environment.node(config::seed("bob"));
let eve = environment.node(config::seed("eve"));
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
fixtures::repository(working.join("acme"));
@ -1784,12 +1792,12 @@ fn rad_sync() {
//
fn test_replication_via_seed() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let alice = environment.node(config::relay("alice"));
let bob = environment.node(config::relay("bob"));
let seed = environment.node(Config {
policy: Policy::Allow,
scope: Scope::All,
..Config::test(Alias::new("seed"))
..config::relay("seed")
});
let working = environment.tmp().join("working");
let rid = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
@ -1874,9 +1882,9 @@ fn test_replication_via_seed() {
#[test]
fn rad_remote() {
let mut environment = Environment::new();
let alice = environment.node(Config::test(Alias::new("alice")));
let bob = environment.node(Config::test(Alias::new("bob")));
let eve = environment.node(Config::test(Alias::new("eve")));
let alice = environment.node(config::relay("alice"));
let bob = environment.node(config::relay("bob"));
let eve = environment.node(config::relay("eve"));
let working = environment.tmp().join("working");
let home = alice.home.clone();
let rid = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();

View File

@ -92,7 +92,7 @@ mod routes {
"tor": null,
"network": "main",
"log": "INFO",
"relay": true,
"relay": "auto",
"limits": {
"routingMaxSize": 1000,
"routingMaxAge": 604800,

View File

@ -25,7 +25,7 @@ use radicle::node;
use radicle::node::address;
use radicle::node::address::Store as _;
use radicle::node::address::{AddressBook, KnownAddress};
use radicle::node::config::PeerConfig;
use radicle::node::config::{PeerConfig, Relay};
use radicle::node::refs::Store as _;
use radicle::node::routing::Store as _;
use radicle::node::seed;
@ -1334,7 +1334,7 @@ where
&mut self,
relayer_addr: &Address,
announcement: &Announcement,
) -> Result<bool, session::Error> {
) -> Result<Relay, session::Error> {
if !announcement.verify() {
return Err(session::Error::Misbehavior);
}
@ -1346,7 +1346,7 @@ where
// Ignore our own announcements, in case the relayer sent one by mistake.
if announcer == self.nid() {
return Ok(false);
return Ok(Relay::Never);
}
let now = self.clock;
let timestamp = message.timestamp();
@ -1358,7 +1358,7 @@ where
{
self.config.relay
} else {
false
Relay::Never
};
// Don't allow messages from too far in the future.
@ -1379,12 +1379,12 @@ where
Ok(node) => {
if node.is_none() {
debug!(target: "service", "Ignoring announcement from unknown node {announcer} (t={timestamp})");
return Ok(false);
return Ok(Relay::Never);
}
}
Err(e) => {
error!(target: "service", "Error looking up node in address book: {e}");
return Ok(false);
return Ok(Relay::Never);
}
}
}
@ -1394,12 +1394,12 @@ where
Ok(fresh) => {
if !fresh {
debug!(target: "service", "Ignoring stale announcement from {announcer} (t={timestamp})");
return Ok(false);
return Ok(Relay::Never);
}
}
Err(e) => {
error!(target: "service", "Error updating gossip entry from {announcer}: {e}");
return Ok(false);
return Ok(Relay::Never);
}
}
@ -1419,12 +1419,12 @@ where
Ok(synced) => {
if synced.is_empty() {
trace!(target: "service", "No routes updated by inventory announcement from {announcer}");
return Ok(false);
return Ok(Relay::Never);
}
}
Err(e) => {
error!(target: "service", "Error processing inventory from {announcer}: {e}");
return Ok(false);
return Ok(Relay::Never);
}
}
@ -1473,7 +1473,7 @@ where
// Empty announcements can be safely ignored.
let Some(refs) = NonEmpty::from_vec(message.refs.to_vec()) else {
debug!(target: "service", "Skipping fetch, no refs in announcement for {} (t={timestamp})", message.rid);
return Ok(false);
return Ok(Relay::Never);
};
// We update inventories when receiving ref announcements, as these could come
// from a new repository being initialized.
@ -1526,7 +1526,7 @@ where
"Ignoring refs announcement from {announcer}: repository {} isn't seeded (t={timestamp})",
message.rid
);
return Ok(false);
return Ok(Relay::Never);
}
// Refs can be relayed by peers who don't have the data in storage,
// therefore we only check whether we are connected to the *announcer*,
@ -1600,7 +1600,7 @@ where
}
}
}
Ok(false)
Ok(Relay::Never)
}
pub fn handle_info(&mut self, remote: NodeId, info: &Info) -> Result<(), session::Error> {
@ -1652,8 +1652,15 @@ where
let relayer_addr = peer.addr.clone();
let announcer = ann.node;
// Returning true here means that the message should be relayed.
if self.handle_announcement(&relayer_addr, &ann)? {
let relay = match self.handle_announcement(&relayer_addr, &ann)? {
// In "auto" mode, we only relay if we're a public seed node.
// This reduces traffic for private nodes, as well as message redundancy.
Relay::Auto => !self.config.external_addresses.is_empty(),
Relay::Never => false,
Relay::Always => true,
};
if relay {
// Choose peers we should relay this message to.
// 1. Don't relay to the peer who sent us this message.
// 2. Don't relay to the peer who signed this announcement.

View File

@ -18,6 +18,19 @@ use crate::storage::git::transport;
use crate::test::environment::{converge, Environment, Node};
use crate::test::logger;
mod config {
use super::*;
use radicle::node::config::{Config, Relay};
/// Relay node config.
pub fn relay(alias: &'static str) -> Config {
Config {
relay: Relay::Always,
..Config::test(Alias::new(alias))
}
}
}
#[test]
//
// alice -- bob
@ -27,8 +40,8 @@ fn test_inventory_sync_basic() {
let tmp = tempfile::tempdir().unwrap();
let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let mut alice = Node::init(tmp.path(), config::relay("alice"));
let mut bob = Node::init(tmp.path(), config::relay("bob"));
alice.project("alice", "");
bob.project("bob", "");
@ -51,9 +64,9 @@ fn test_inventory_sync_bridge() {
let tmp = tempfile::tempdir().unwrap();
let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let mut eve = Node::init(tmp.path(), Config::test(Alias::new("eve")));
let mut alice = Node::init(tmp.path(), config::relay("alice"));
let mut bob = Node::init(tmp.path(), config::relay("bob"));
let mut eve = Node::init(tmp.path(), config::relay("eve"));
alice.project("alice", "");
bob.project("bob", "");
@ -81,9 +94,9 @@ fn test_inventory_sync_ring() {
let tmp = tempfile::tempdir().unwrap();
let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let mut eve = Node::init(tmp.path(), Config::test(Alias::new("eve")));
let mut alice = Node::init(tmp.path(), config::relay("alice"));
let mut bob = Node::init(tmp.path(), config::relay("bob"));
let mut eve = Node::init(tmp.path(), config::relay("eve"));
let mut carol = Node::init(tmp.path(), Config::test(Alias::new("carol")));
alice.project("alice", "");
@ -118,9 +131,9 @@ fn test_inventory_sync_star() {
let tmp = tempfile::tempdir().unwrap();
let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let mut eve = Node::init(tmp.path(), Config::test(Alias::new("eve")));
let mut alice = Node::init(tmp.path(), config::relay("alice"));
let mut bob = Node::init(tmp.path(), config::relay("bob"));
let mut eve = Node::init(tmp.path(), config::relay("eve"));
let mut carol = Node::init(tmp.path(), Config::test(Alias::new("carol")));
let mut dave = Node::init(tmp.path(), Config::test(Alias::new("dave")));
@ -150,8 +163,8 @@ fn test_replication() {
logger::init(log::Level::Debug);
let tmp = tempfile::tempdir().unwrap();
let alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let alice = Node::init(tmp.path(), config::relay("alice"));
let mut bob = Node::init(tmp.path(), config::relay("bob"));
let acme = bob.project("acme", "");
let mut alice = alice.spawn();
@ -210,8 +223,8 @@ fn test_replication_ref_in_sigrefs() {
logger::init(log::Level::Debug);
let tmp = tempfile::tempdir().unwrap();
let alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let alice = Node::init(tmp.path(), config::relay("alice"));
let mut bob = Node::init(tmp.path(), config::relay("bob"));
let acme = bob.project("acme", "");
// Delete one of the signed refs.
@ -251,8 +264,8 @@ fn test_replication_ref_in_sigrefs() {
#[test]
fn test_replication_invalid() {
let tmp = tempfile::tempdir().unwrap();
let alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let alice = Node::init(tmp.path(), config::relay("alice"));
let mut bob = Node::init(tmp.path(), config::relay("bob"));
let carol = MockSigner::default();
let acme = bob.project("acme", "");
let repo = bob.storage.repository_mut(acme).unwrap();
@ -304,8 +317,8 @@ fn test_migrated_clone() {
logger::init(log::Level::Debug);
let tmp = tempfile::tempdir().unwrap();
let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let mut alice = Node::init(tmp.path(), config::relay("alice"));
let bob = Node::init(tmp.path(), config::relay("bob"));
let acme = alice.project("acme", "");
let mut alice = alice.spawn();
@ -357,8 +370,8 @@ fn test_dont_fetch_owned_refs() {
logger::init(log::Level::Debug);
let tmp = tempfile::tempdir().unwrap();
let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let mut alice = Node::init(tmp.path(), config::relay("alice"));
let bob = Node::init(tmp.path(), config::relay("bob"));
let acme = alice.project("acme", "");
let mut alice = alice.spawn();
@ -384,8 +397,8 @@ fn test_fetch_followed_remotes() {
logger::init(log::Level::Debug);
let tmp = tempfile::tempdir().unwrap();
let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let mut alice = Node::init(tmp.path(), config::relay("alice"));
let bob = Node::init(tmp.path(), config::relay("bob"));
let acme = alice.project("acme", "");
let mut signers = Vec::with_capacity(5);
{
@ -439,8 +452,8 @@ fn test_missing_remote() {
logger::init(log::Level::Debug);
let tmp = tempfile::tempdir().unwrap();
let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let mut alice = Node::init(tmp.path(), config::relay("alice"));
let bob = Node::init(tmp.path(), config::relay("bob"));
let acme = alice.project("acme", "");
let mut alice = alice.spawn();
@ -468,8 +481,8 @@ fn test_fetch_preserve_owned_refs() {
logger::init(log::Level::Debug);
let tmp = tempfile::tempdir().unwrap();
let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let mut alice = Node::init(tmp.path(), config::relay("alice"));
let bob = Node::init(tmp.path(), config::relay("bob"));
let acme = alice.project("acme", "");
let mut alice = alice.spawn();
let mut bob = bob.spawn();
@ -514,8 +527,8 @@ fn test_clone() {
logger::init(log::Level::Debug);
let tmp = tempfile::tempdir().unwrap();
let alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let alice = Node::init(tmp.path(), config::relay("alice"));
let mut bob = Node::init(tmp.path(), config::relay("bob"));
let acme = bob.project("acme", "");
let mut alice = alice.spawn();
@ -572,8 +585,8 @@ fn test_fetch_up_to_date() {
logger::init(log::Level::Debug);
let tmp = tempfile::tempdir().unwrap();
let alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let alice = Node::init(tmp.path(), config::relay("alice"));
let mut bob = Node::init(tmp.path(), config::relay("bob"));
let acme = bob.project("acme", "");
let mut alice = alice.spawn();
@ -601,8 +614,8 @@ fn test_fetch_unseeded() {
logger::init(log::Level::Debug);
let tmp = tempfile::tempdir().unwrap();
let alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let alice = Node::init(tmp.path(), config::relay("alice"));
let mut bob = Node::init(tmp.path(), config::relay("bob"));
let acme = bob.project("acme", "");
let mut alice = alice.spawn();
@ -631,8 +644,8 @@ fn test_large_fetch() {
let env = Environment::new();
let scale = env.scale();
let mut alice = Node::init(&env.tmp(), Config::test(Alias::new("alice")));
let bob = Node::init(&env.tmp(), Config::test(Alias::new("bob")));
let mut alice = Node::init(&env.tmp(), config::relay("alice"));
let bob = Node::init(&env.tmp(), config::relay("bob"));
let tmp = tempfile::tempdir().unwrap();
let (repo, _) = fixtures::repository(tmp.path());
@ -681,14 +694,16 @@ fn test_concurrent_fetches() {
&env.tmp(),
service::Config {
limits: limits.clone(),
..service::Config::test(Alias::new("alice"))
relay: radicle::node::config::Relay::Always,
..config::relay("alice")
},
);
let mut bob = Node::init(
&env.tmp(),
service::Config {
limits,
..service::Config::test(Alias::new("bob"))
relay: radicle::node::config::Relay::Always,
..config::relay("bob")
},
);
@ -773,8 +788,8 @@ fn test_connection_crossing() {
logger::init(log::Level::Debug);
let tmp = tempfile::tempdir().unwrap();
let alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let alice = Node::init(tmp.path(), config::relay("alice"));
let bob = Node::init(tmp.path(), config::relay("bob"));
let alice = alice.spawn();
let bob = bob.spawn();
@ -841,9 +856,9 @@ fn test_non_fastforward_sigrefs() {
let tmp = tempfile::tempdir().unwrap();
let alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let eve = Node::init(tmp.path(), Config::test(Alias::new("eve")));
let alice = Node::init(tmp.path(), config::relay("alice"));
let mut bob = Node::init(tmp.path(), config::relay("bob"));
let eve = Node::init(tmp.path(), config::relay("eve"));
let rid = bob.project("acme", "");
@ -949,9 +964,9 @@ fn test_outdated_sigrefs() {
let tmp = tempfile::tempdir().unwrap();
let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let eve = Node::init(tmp.path(), Config::test(Alias::new("eve")));
let mut alice = Node::init(tmp.path(), config::relay("alice"));
let bob = Node::init(tmp.path(), config::relay("bob"));
let eve = Node::init(tmp.path(), config::relay("eve"));
let rid = alice.project("acme", "");
@ -1045,9 +1060,9 @@ fn test_outdated_delegate_sigrefs() {
let tmp = tempfile::tempdir().unwrap();
let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let eve = Node::init(tmp.path(), Config::test(Alias::new("eve")));
let mut alice = Node::init(tmp.path(), config::relay("alice"));
let bob = Node::init(tmp.path(), config::relay("bob"));
let eve = Node::init(tmp.path(), config::relay("eve"));
let rid = alice.project("acme", "");
@ -1133,8 +1148,8 @@ fn missing_default_branch() {
let tmp = tempfile::tempdir().unwrap();
let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let mut alice = Node::init(tmp.path(), config::relay("alice"));
let bob = Node::init(tmp.path(), config::relay("bob"));
let rid = alice.project("acme", "");
@ -1185,9 +1200,9 @@ fn test_background_foreground_fetch() {
let tmp = tempfile::tempdir().unwrap();
let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let eve = Node::init(tmp.path(), Config::test(Alias::new("eve")));
let mut alice = Node::init(tmp.path(), config::relay("alice"));
let bob = Node::init(tmp.path(), config::relay("bob"));
let eve = Node::init(tmp.path(), config::relay("eve"));
let rid = alice.project("acme", "");
@ -1273,10 +1288,10 @@ fn test_catchup_on_refs_announcements() {
logger::init(log::Level::Debug);
let tmp = tempfile::tempdir().unwrap();
let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
let mut alice = Node::init(tmp.path(), config::relay("alice"));
let bob = Node::init(tmp.path(), config::relay("bob"));
let bob_id = bob.id;
let seed = Node::init(tmp.path(), Config::test(Alias::new("seed")));
let seed = Node::init(tmp.path(), config::relay("seed"));
let acme = alice.project("acme", "");
let mut alice = alice.spawn();

View File

@ -234,6 +234,19 @@ impl Default for PeerConfig {
}
}
/// Relay configuration.
#[derive(Debug, Copy, Clone, Default, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Relay {
/// Always relay messages.
Always,
/// Never relay messages.
Never,
/// Relay messages when applicable.
#[default]
Auto,
}
/// Tor configuration.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase", tag = "mode")]
@ -276,9 +289,9 @@ pub struct Config {
#[serde(default = "defaults::log")]
#[serde(with = "crate::serde_ext::string")]
pub log: log::Level,
/// Whether or not our node should relay inventories.
#[serde(default = "crate::serde_ext::bool::yes")]
pub relay: bool,
/// Whether or not our node should relay messages.
#[serde(default, deserialize_with = "crate::serde_ext::ok_or_default")]
pub relay: Relay,
/// Configured service limits.
#[serde(default)]
pub limits: Limits,
@ -310,7 +323,7 @@ impl Config {
external_addresses: vec![],
network: Network::default(),
tor: None,
relay: true,
relay: Relay::default(),
limits: Limits::default(),
workers: DEFAULT_WORKERS,
log: defaults::log(),

View File

@ -110,6 +110,16 @@ pub fn is_default<T: Default + PartialEq>(t: &T) -> bool {
t == &T::default()
}
/// Deserialize a value, but if it fails, return the default value.
pub fn ok_or_default<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: serde::Deserialize<'de> + Default,
D: serde::Deserializer<'de>,
{
let v: serde_json::Value = serde::Deserialize::deserialize(deserializer)?;
Ok(T::deserialize(v).unwrap_or_default())
}
#[cfg(test)]
mod test {
use super::*;