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

Add EIP 7636 support #81

Merged
merged 7 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
16 changes: 16 additions & 0 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,22 @@ impl<K: EnrKey> Builder<K> {
self
}

/// Adds a [EIP-7636](https://eips.ethereum.org/EIPS/eip-7636) `client` field to the `ENRBuilder`.
pub fn client_info(
&mut self,
name: String,
version: String,
build: Option<String>,
) -> &mut Self {
if build.is_none() {
self.add_value("client", &vec![name, version]);
} else {
self.add_value("client", &vec![name, version, build.unwrap()]);
}

self
}

/// Generates the rlp-encoded form of the ENR specified by the builder config.
fn rlp_content(&self) -> BytesMut {
let mut list = Vec::<u8>::with_capacity(MAX_ENR_SIZE);
Expand Down
120 changes: 120 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,36 @@ impl<K: EnrKey> Enr<K> {
None
}

/// Returns [EIP-7636](https://eips.ethereum.org/EIPS/eip-7636) entry if it is defined.
#[must_use]
pub fn client_info(&self) -> Option<(String, String, Option<String>)> {
if let Some(Ok(client_list)) = self.get_decodable::<Vec<Bytes>>("client") {
match client_list.len() {
2 => {
let client_name = String::from_utf8_lossy(client_list[0].as_ref()).to_string();

let client_version =
String::from_utf8_lossy(client_list[1].as_ref()).to_string();

return Some((client_name, client_version, None));
}
3 => {
let client_name = String::from_utf8_lossy(client_list[0].as_ref()).to_string();

let client_version =
String::from_utf8_lossy(client_list[1].as_ref()).to_string();

let client_additional =
String::from_utf8_lossy(client_list[2].as_ref()).to_string();
return Some((client_name, client_version, Some(client_additional)));
}
_ => {}
}
}

None
}

/// The TCP port of ENR record if it is defined.
#[must_use]
pub fn tcp4(&self) -> Option<u16> {
Expand Down Expand Up @@ -620,6 +650,23 @@ impl<K: EnrKey> Enr<K> {
Ok(None)
}

/// Sets the [EIP-7636](https://eips.ethereum.org/EIPS/eip-7636) `client` field in the record.
pub fn set_client_info(
&mut self,
name: String,
version: String,
build: Option<String>,
key: &K,
) -> Result<(), Error> {
if build.is_none() {
self.insert("client", &vec![name, version], key)?;
} else {
self.insert("client", &vec![name, version, build.unwrap()], key)?;
}

Ok(())
}

/// Sets the IP and UDP port in a single update with a single increment in sequence number.
pub fn set_udp_socket(&mut self, socket: SocketAddr, key: &K) -> Result<(), Error> {
self.set_socket(socket, key, false)
Expand Down Expand Up @@ -1969,6 +2016,79 @@ mod tests {
assert_eq!(record.seq(), 30);
}

#[test]
fn test_set_client_eip7636() {
let key = k256::ecdsa::SigningKey::random(&mut rand::thread_rng());
let mut enr = Enr::empty(&key).unwrap();

enr.set_client_info(
"Test".to_string(),
"v1.0.0".to_string(),
Some("Test".to_string()),
&key,
)
.unwrap();
assert!(enr.verify());

enr.set_client_info("Test".to_string(), "v1.0.0".to_string(), None, &key)
.unwrap();
assert!(enr.verify());
}

#[test]
fn test_get_eip7636() {
let example_eip = "enr:-MO4QBn4OF-y-dqULg4WOIlc8gQAt-arldNFe0_YQ4HNX28jDtg41xjDyKfCXGfZaPN97I-MCfogeK91TyqmWTpb0_AChmNsaWVudNqKTmV0aGVybWluZIYxLjkuNTOHN2ZjYjU2N4JpZIJ2NIJpcIR_AAABg2lwNpAAAAAAAAAAAAAAAAAAAAABiXNlY3AyNTZrMaECn-TTdCwfZP4XgJyq8Lxoj-SgEoIFgDLVBEUqQk4HnAqDdWRwgiMshHVkcDaCIyw";
let enr = example_eip.parse::<DefaultEnr>().unwrap();

let info = enr.client_info().unwrap();

assert_eq!(info.0, "Nethermind");
assert_eq!(info.1, "1.9.53");
assert_eq!(info.2.unwrap(), "7fcb567");

let key = k256::ecdsa::SigningKey::random(&mut rand::thread_rng());
let mut enr = Enr::empty(&key).unwrap();

enr.set_client_info("Test".to_string(), "v1.0.0".to_string(), None, &key)
.unwrap();

let info = enr.client_info().unwrap();
assert_eq!(info.0, "Test");
assert_eq!(info.1, "v1.0.0");
assert_eq!(info.2, None);
}

#[test]
fn test_builder_eip7636() {
let key = k256::ecdsa::SigningKey::random(&mut rand::thread_rng());
let enr = Enr::builder()
.ip4(Ipv4Addr::new(127, 0, 0, 1))
.tcp4(30303)
.client_info(
"Test".to_string(),
"v1.0.0".to_string(),
Some("Test".to_string()),
)
.build(&key)
.unwrap();

let info = enr.client_info().unwrap();
assert_eq!(info.0, "Test");
assert_eq!(info.1, "v1.0.0");
assert_eq!(info.2.unwrap(), "Test");

let enr = Enr::builder()
.ip4(Ipv4Addr::new(127, 0, 0, 1))
.tcp4(30303)
.client_info("Test".to_string(), "v1.0.0".to_string(), None)
.build(&key)
.unwrap();

let info = enr.client_info().unwrap();
assert_eq!(info.0, "Test");
assert_eq!(info.1, "v1.0.0");
assert_eq!(info.2, None);
}
/// Tests a common ENR which uses RLP encoded values without the header
#[test]
fn test_common_rlp_convention() {
Expand Down