Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: enr signature changes on restart even if enr content is the same #952

Merged
merged 5 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 29 additions & 32 deletions portalnet/src/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use crate::socket;
use anyhow::anyhow;
use async_trait::async_trait;
use bytes::BytesMut;
use discv5::enr::EnrPublicKey;
use discv5::{
enr::{CombinedKey, EnrBuilder, NodeId},
ConfigBuilder, Discv5, Event, ListenConfig, RequestError, TalkRequest,
Expand All @@ -17,6 +16,7 @@ use rlp::RlpStream;
use serde_json::{json, Value};
use std::hash::{Hash, Hasher};
use std::net::Ipv4Addr;
use std::path::PathBuf;
use std::str::FromStr;
use std::{convert::TryFrom, fmt, fs, io, net::SocketAddr, sync::Arc};
use tokio::sync::mpsc;
Expand Down Expand Up @@ -69,7 +69,7 @@ impl fmt::Debug for Discovery {
}

impl Discovery {
pub fn new(portal_config: PortalnetConfig) -> Result<Self, String> {
pub fn new(portal_config: PortalnetConfig, node_data_dir: PathBuf) -> Result<Self, String> {
let listen_all_ips = SocketAddr::new(
"0.0.0.0"
.parse()
Expand Down Expand Up @@ -112,27 +112,28 @@ impl Discovery {
};

// Check if we have an old version of our Enr and if we do, increase our sequence number
if let Some(enr_file_location) = portal_config.enr_file_location {
let trin_enr_file_location = enr_file_location.join(ENR_FILE_NAME);
if trin_enr_file_location.is_file() {
let data = fs::read_to_string(trin_enr_file_location.clone())
.expect("Unable to read Trin Enr from file");
let old_enr = Enr::from_str(&data).expect("Expected read trin.enr to be valid");

enr.set_seq(old_enr.seq(), &enr_key)
.expect("Unable to set Enr sequence number");

// If the old Enr's signature is different then the new one
if !verify_by_signature(&enr, old_enr.signature())
&& !verify_by_signature(&old_enr, enr.signature())
{
enr.set_seq(old_enr.seq() + 1, &enr_key)
.expect("Unable to increase Enr sequence number");
}
let trin_enr_path = node_data_dir.join(ENR_FILE_NAME);
if trin_enr_path.is_file() {
let data = fs::read_to_string(trin_enr_path.clone())
.expect("Unable to read Trin Enr from file");
let old_enr = Enr::from_str(&data).expect("Expected read trin.enr to be valid");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit "Expected to read valid Trin Enr from file"

enr.set_seq(old_enr.seq(), &enr_key)
.expect("Unable to set Enr sequence number");

// If the content is different then increase the sequence number
if get_enr_rlp_content(&enr) != get_enr_rlp_content(&old_enr) {
enr.set_seq(old_enr.seq() + 1, &enr_key)
.expect("Unable to increase Enr sequence number");
fs::write(trin_enr_path, enr.to_base64())
.expect("Unable to update Trin Enr to file");
} else {
// the content is the same, we don't want to change signatures on restart
// so set enr to old one to keep the same signature per sequence number
enr = old_enr;
}
} else {
// Write enr to disk
fs::write(trin_enr_file_location, enr.to_base64())
.expect("Unable to write Trin Enr to file");
fs::write(trin_enr_path, enr.to_base64()).expect("Unable to write Trin Enr to file");
}

let listen_config = ListenConfig::Ipv4 {
Expand Down Expand Up @@ -425,7 +426,7 @@ impl AsyncUdpSocket<UtpEnr> for Discv5UdpSocket {

// todo: remove this once sigp/enr implements this for enr's
// we need this because signatures can be different for the same data but still valid
fn verify_by_signature(enr: &Enr, signature: &[u8]) -> bool {
fn get_enr_rlp_content(enr: &Enr) -> BytesMut {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@KolbyML I don't think there is any harm in exposing rlp_content from Enr

Copy link
Member Author

@KolbyML KolbyML Oct 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we get the update Enr version we can just use compare_content. If we want to expose rlp_content() that is fine too. It just looked like rlp_content() was intentionally privated so that is why I tried to get a compare/verify_by_signature* function added. Cause I didn't want to manually calculate rlp_content in multiple codebases.

Anyways up to you. I didn't know making the rlp_content() function public was an option tbh.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do think compare_content is cleaner though
enr1.compare_content(&enr2)
vs
enr1.rlp_content() == enr2.rlp_content()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neither is a problem. Nothing wrong with exposing it, this does not change the enr and it's not private info in any way

match enr.id() {
Some(ref id) if id == "v4" => {
let mut stream = RlpStream::new_with_buffer(BytesMut::with_capacity(300));
Expand All @@ -439,10 +440,10 @@ fn verify_by_signature(enr: &Enr, signature: &[u8]) -> bool {
stream.append_raw(v, 1);
}

enr.public_key().verify_v4(&stream.out(), signature)
stream.out()
}
// unsupported identity schemes
_ => false,
_ => BytesMut::with_capacity(0),
}
}

Expand All @@ -463,7 +464,6 @@ mod tests {
let mut portalnet_config = PortalnetConfig {
private_key,
bootnodes: Bootnodes::None,
enr_file_location: Some(node_data_dir.clone()),
..Default::default()
};

Expand All @@ -472,15 +472,15 @@ mod tests {
assert!(!trin_enr_file_location.is_file());

// test trin.enr is made on first run
let discovery = Discovery::new(portalnet_config.clone()).unwrap();
let discovery = Discovery::new(portalnet_config.clone(), node_data_dir.clone()).unwrap();
let data = fs::read_to_string(trin_enr_file_location.clone()).unwrap();
let old_enr = Enr::from_str(&data).unwrap();
assert_eq!(discovery.local_enr(), old_enr);
assert_eq!(old_enr.seq(), 1);

// test if Enr changes the Enr sequence is increased and if it is written to disk
portalnet_config.listen_port = 2424;
let discovery = Discovery::new(portalnet_config.clone()).unwrap();
let discovery = Discovery::new(portalnet_config.clone(), node_data_dir.clone()).unwrap();
assert_ne!(discovery.local_enr(), old_enr);
let data = fs::read_to_string(trin_enr_file_location.clone()).unwrap();
let old_enr = Enr::from_str(&data).unwrap();
Expand All @@ -489,11 +489,8 @@ mod tests {
assert_eq!(discovery.local_enr(), old_enr);

// test if the enr isn't changed that it's sequence stays the same
let discovery = Discovery::new(portalnet_config).unwrap();
assert!(
verify_by_signature(&discovery.local_enr(), old_enr.signature())
&& verify_by_signature(&old_enr, discovery.local_enr().signature())
);
let discovery = Discovery::new(portalnet_config, node_data_dir).unwrap();
assert_eq!(discovery.local_enr(), old_enr);
let data = fs::read_to_string(trin_enr_file_location).unwrap();
let old_enr = Enr::from_str(&data).unwrap();
assert_eq!(discovery.local_enr().seq(), 2);
Expand Down
11 changes: 6 additions & 5 deletions portalnet/src/overlay_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2695,17 +2695,17 @@ mod tests {
types::messages::PortalnetConfig,
};

use crate::utils::db::setup_temp_dir;
use discv5::kbucket::Entry;
use ethereum_types::U256;
use ethportal_api::types::content_key::overlay::IdentityContentKey;
use ethportal_api::types::distance::XorMetric;
use ethportal_api::types::enr::generate_random_remote_enr;
use trin_validation::validator::MockValidator;

use discv5::kbucket::Entry;
use ethereum_types::U256;
use serial_test::serial;
use std::net::SocketAddr;
use tokio::sync::mpsc::unbounded_channel;
use tokio_test::{assert_pending, assert_ready, task};
use trin_validation::validator::MockValidator;

macro_rules! poll_command_rx {
($service:ident) => {
Expand All @@ -2719,7 +2719,8 @@ mod tests {
no_stun: true,
..Default::default()
};
let discovery = Arc::new(Discovery::new(portal_config).unwrap());
let temp_dir = setup_temp_dir().unwrap().into_path();
let discovery = Arc::new(Discovery::new(portal_config, temp_dir).unwrap());

let (_utp_talk_req_tx, utp_talk_req_rx) = unbounded_channel();
let discv5_utp =
Expand Down
3 changes: 0 additions & 3 deletions portalnet/src/types/messages.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::path::PathBuf;
use std::{
convert::{TryFrom, TryInto},
fmt,
Expand Down Expand Up @@ -169,7 +168,6 @@ pub struct PortalnetConfig {
pub internal_ip: bool,
pub no_stun: bool,
pub node_addr_cache_capacity: usize,
pub enr_file_location: Option<PathBuf>,
}

impl Default for PortalnetConfig {
Expand All @@ -182,7 +180,6 @@ impl Default for PortalnetConfig {
data_radius: Distance::MAX,
internal_ip: false,
no_stun: false,
enr_file_location: None,
node_addr_cache_capacity: NODE_ADDR_CACHE_CAPACITY,
}
}
Expand Down
13 changes: 9 additions & 4 deletions portalnet/tests/overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use tokio::{
use utp_rs::socket::UtpSocket;

use ethportal_api::utils::bytes::hex_encode_upper;
use portalnet::utils::db::setup_temp_dir;

async fn init_overlay(
discovery: Arc<Discovery>,
Expand Down Expand Up @@ -102,7 +103,8 @@ async fn overlay() {
external_addr: Some(SocketAddr::new(ip_addr, 8001)),
..PortalnetConfig::default()
};
let mut discovery_one = Discovery::new(portal_config_one).unwrap();
let temp_dir_one = setup_temp_dir().unwrap().into_path();
let mut discovery_one = Discovery::new(portal_config_one, temp_dir_one).unwrap();
let talk_req_rx_one = discovery_one.start().await.unwrap();
let discovery_one = Arc::new(discovery_one);
let overlay_one = Arc::new(init_overlay(Arc::clone(&discovery_one), protocol.clone()).await);
Expand All @@ -115,7 +117,8 @@ async fn overlay() {
external_addr: Some(SocketAddr::new(ip_addr, 8002)),
..PortalnetConfig::default()
};
let mut discovery_two = Discovery::new(portal_config_two).unwrap();
let temp_dir_two = setup_temp_dir().unwrap().into_path();
let mut discovery_two = Discovery::new(portal_config_two, temp_dir_two).unwrap();
let talk_req_rx_two = discovery_two.start().await.unwrap();
let discovery_two = Arc::new(discovery_two);
let overlay_two = Arc::new(init_overlay(Arc::clone(&discovery_two), protocol.clone()).await);
Expand All @@ -128,7 +131,8 @@ async fn overlay() {
external_addr: Some(SocketAddr::new(ip_addr, 8003)),
..PortalnetConfig::default()
};
let mut discovery_three = Discovery::new(portal_config_three).unwrap();
let temp_dir_three = setup_temp_dir().unwrap().into_path();
let mut discovery_three = Discovery::new(portal_config_three, temp_dir_three).unwrap();
let talk_req_rx_three = discovery_three.start().await.unwrap();
let discovery_three = Arc::new(discovery_three);
let overlay_three =
Expand Down Expand Up @@ -247,7 +251,8 @@ async fn overlay_event_stream() {
no_stun: true,
..Default::default()
};
let discovery = Arc::new(Discovery::new(portal_config).unwrap());
let temp_dir = setup_temp_dir().unwrap().into_path();
let discovery = Arc::new(Discovery::new(portal_config, temp_dir).unwrap());
let overlay = init_overlay(discovery, ProtocolId::Beacon).await;

overlay.event_stream().await.unwrap();
Expand Down
4 changes: 3 additions & 1 deletion rpc/src/rpc_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,7 @@ mod tests {
use crate::builder::RpcModuleSelection;
use crate::{PortalRpcModule, RpcModuleBuilder};
use portalnet::discovery::Discovery;
use portalnet::utils::db::setup_temp_dir;
use std::io;
use std::sync::Arc;

Expand All @@ -633,7 +634,8 @@ mod tests {
pub fn test_rpc_builder() -> RpcModuleBuilder {
let (history_tx, _) = tokio::sync::mpsc::unbounded_channel();
let (beacon_tx, _) = tokio::sync::mpsc::unbounded_channel();
let discv5 = Arc::new(Discovery::new(Default::default()).unwrap());
let temp_dir = setup_temp_dir().unwrap().into_path();
let discv5 = Arc::new(Discovery::new(Default::default(), temp_dir).unwrap());
RpcModuleBuilder::new(discv5)
.with_history(history_tx)
.with_beacon(beacon_tx)
Expand Down
3 changes: 1 addition & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,11 @@ pub async fn run_trin(
listen_port: trin_config.discovery_port,
no_stun: trin_config.no_stun,
bootnodes: trin_config.bootnodes.clone(),
enr_file_location: Some(node_data_dir.clone()),
..Default::default()
};

// Initialize base discovery protocol
let mut discovery = Discovery::new(portalnet_config.clone())?;
let mut discovery = Discovery::new(portalnet_config.clone(), node_data_dir.clone())?;
let talk_req_rx = discovery.start().await?;
let discovery = Arc::new(discovery);

Expand Down
4 changes: 3 additions & 1 deletion utp-testing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use jsonrpsee::proc_macros::rpc;
use jsonrpsee::server::{Server, ServerHandle};
use portalnet::discovery::{Discovery, UtpEnr};
use portalnet::types::messages::{PortalnetConfig, ProtocolId};
use portalnet::utils::db::setup_temp_dir;
use std::net::SocketAddr;
use std::str::FromStr;
use std::sync::Arc;
Expand Down Expand Up @@ -148,7 +149,8 @@ pub async fn run_test_app(
..Default::default()
};

let mut discovery = Discovery::new(config).unwrap();
let temp_dir = setup_temp_dir().unwrap().into_path();
let mut discovery = Discovery::new(config, temp_dir).unwrap();
let talk_req_rx = discovery.start().await.unwrap();
let enr = discovery.local_enr();
let discovery = Arc::new(discovery);
Expand Down