GithubHelp home page GithubHelp logo

enr's Introduction

enr

Build Status Doc Status Crates Status

Documentation at docs.rs

This crate contains an implementation of an Ethereum Node Record (ENR) as specified by EIP-778 extended to allow for the use of ed25519 keys.

An ENR is a signed, key-value record which has an associated NodeId (a 32-byte identifier). Updating/modifying an ENR requires an EnrKey in order to re-sign the record with the associated key-pair.

ENR's are identified by their sequence number. When updating an ENR, the sequence number is increased.

Different identity schemes can be used to define the node id and signatures. Currently only the "v4" identity is supported and is set by default.

Signing Algorithms

User's wishing to implement their own signing algorithms simply need to implement the EnrKey trait and apply it to an Enr.

By default, k256::SigningKey implement EnrKey and can be used to sign and verify ENR records. This library also implements EnrKey for ed25519_dalek::Keypair via the ed25519 feature flag.

Furthermore, a CombinedKey is provided if the ed25519 feature flag is set, which provides an ENR type that can support both secp256k1 and ed25519 signed ENR records. Examples of the use of each of these key types is given below.

Features

This crate supports a number of features.

  • serde: Allows for serde serialization and deserialization for ENRs.
  • ed25519: Provides support for ed25519_dalek keypair types.
  • rust-secp256k1: Uses c-secp256k1 for secp256k1 keys.

These can be enabled via adding the feature flag in your Cargo.toml

enr = { version = "*", features = ["serde", "ed25519", "rust-secp256k1"] }

Examples

Building an ENR with the default k256 key type

use enr::{Enr, k256};
use std::net::Ipv4Addr;
use rand::thread_rng;

// generate a random secp256k1 key
let mut rng = thread_rng();
let key = k256::ecdsa::SigningKey::random(&mut rng);

let ip = Ipv4Addr::new(192,168,0,1);
let enr = Enr::builder().ip4(ip).tcp4(8000).build(&key).unwrap();

assert_eq!(enr.ip4(), Some("192.168.0.1".parse().unwrap()));
assert_eq!(enr.id(), Some("v4".into()));

Building an ENR with the CombinedKey type (support for multiple signing algorithms).

Note the ed25519 feature flag must be set. This makes use of the builder::Builder struct.

use enr::{Enr, CombinedKey};
use std::net::Ipv4Addr;

// create a new secp256k1 key
let key = CombinedKey::generate_secp256k1();

// or create a new ed25519 key
let key = CombinedKey::generate_ed25519();

let ip = Ipv4Addr::new(192,168,0,1);
let enr = Enr::builder().ip4(ip).tcp4(8000).build(&key).unwrap();

assert_eq!(enr.ip4(), Some("192.168.0.1".parse().unwrap()));
assert_eq!(enr.id(), Some("v4".into()));

Modifying an ENR

ENR fields can be added and modified using the getters/setters on Enr. A custom field can be added using insert and retrieved with get.

use enr::{k256::ecdsa::SigningKey, Enr};
use std::net::Ipv4Addr;
use rand::thread_rng;

// specify the type of ENR
type DefaultEnr = Enr<SigningKey>;

// generate a random secp256k1 key
let mut rng = thread_rng();
let key = SigningKey::random(&mut rng);

let ip = Ipv4Addr::new(192,168,0,1);
let mut enr = Enr::builder().ip4(ip).tcp4(8000).build(&key).unwrap();

enr.set_tcp4(8001, &key);
// set a custom key
enr.insert("custom_key", &[0,0,1], &key);

// encode to base64
let base_64_string = enr.to_base64();

// decode from base64
let decoded_enr: DefaultEnr = base_64_string.parse().unwrap();

assert_eq!(decoded_enr.ip4(), Some("192.168.0.1".parse().unwrap()));
assert_eq!(decoded_enr.id(), Some("v4".into()));
assert_eq!(decoded_enr.tcp4(), Some(8001));
assert_eq!(decoded_enr.get("custom_key").as_ref().map(AsRef::as_ref), Some(vec![0,0,1]).as_deref());

Encoding/Decoding ENR's of various key types

use enr::{ed25519_dalek as ed25519, k256::ecdsa, CombinedKey, Enr};
use std::net::Ipv4Addr;
use rand::thread_rng;

// generate a random secp256k1 key
let mut rng = thread_rng();
let key = ecdsa::SigningKey::random(&mut rng);
let ip = Ipv4Addr::new(192, 168, 0, 1);
let enr_secp256k1 = Enr::builder().ip4(ip).tcp4(8000).build(&key).unwrap();

// encode to base64
let base64_string_secp256k1 = enr_secp256k1.to_base64();

// generate a random ed25519 key
let key = ed25519::SigningKey::generate(&mut rng);
let enr_ed25519 = Enr::builder().ip4(ip).tcp4(8000).build(&key).unwrap();

// encode to base64
let base64_string_ed25519 = enr_ed25519.to_base64();

// decode base64 strings of varying key types
// decode the secp256k1 with default Enr
let decoded_enr_secp256k1: Enr<ecdsa::SigningKey> = base64_string_secp256k1.parse().unwrap();
// decode ed25519 ENRs
let decoded_enr_ed25519: Enr<ed25519::SigningKey> = base64_string_ed25519.parse().unwrap();

// use the combined key to be able to decode either
let decoded_enr: Enr<CombinedKey> = base64_string_secp256k1.parse().unwrap();
let decoded_enr: Enr<CombinedKey> = base64_string_ed25519.parse().unwrap();

enr's People

Contributors

agemanning avatar armaganyildirak avatar codemonkey23333 avatar danipopes avatar dapplion avatar detailyang avatar divagant-martian avatar e3243eric avatar emhane avatar jkincorperated avatar jxs avatar kolbyml avatar ltfschoen avatar mattsse avatar morph-dev avatar paulhauner avatar pawanjay176 avatar rjected avatar tarcieri avatar vorot93 avatar weipin avatar zedt3ster avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

enr's Issues

Enr Debug implementation is unhelpful

The current debug impl of Enr

enr/src/lib.rs

Lines 864 to 868 in e59dcb4

impl<K: EnrKey> std::fmt::Debug for Enr<K> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.to_base64())
}
}

is unhelpful for debugging.

The display impl is more helpful for debugging but lacks content info

enr/src/lib.rs

Lines 852 to 862 in e59dcb4

impl<K: EnrKey> std::fmt::Display for Enr<K> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"ENR: NodeId: {}, IpV4 Socket: {:?} IpV6 Socket: {:?}",
self.node_id(),
self.udp4_socket(),
self.udp6_socket()
)
}
}

Suggest flipping them and to include k,v pairs as well.

Failing to deserialize from `serde_json::Value`

The NodeId can't deserialize from serde_json::Value object.

Here is a simple test (unwrap crashes because deserialization returns None)

#[cfg(feature = "serde")]
#[test]
fn test_serde_value() {
    let node = NodeId::random();
    let value = serde_json::to_value(&node).unwrap();
    assert_eq!(node, serde_json::from_value::<NodeId>(value).unwrap());
}

One way to fix it is to change deserialize function to:

let raw: String = serde::Deserialize::deserialize(deserializer)?;
let src = raw.strip_prefix("0x").unwrap_or(&raw);
hex::FromHex::from_hex(src).map_err(serde::de::Error::custom)

but this is not optimal in other cases.

If you are fine with this fix, I can create a PR.

Clarify enr content value format

This states:

enr/src/builder.rs

Lines 18 to 20 in 83cd989

/// The key-value pairs for the ENR record.
/// Values are stored as RLP encoded bytes.
content: BTreeMap<Key, Bytes>,

This will add any encodable value encoded as rlp to the map:

enr/src/builder.rs

Lines 45 to 56 in 83cd989

/// Adds an arbitrary key-value to the `ENRBuilder`.
pub fn add_value<T: Encodable>(&mut self, key: impl AsRef<[u8]>, value: &T) -> &mut Self {
let mut out = BytesMut::new();
value.encode(&mut out);
self.add_value_rlp(key, out.freeze())
}
/// Adds an arbitrary key-value where the value is raw RLP encoded bytes.
pub fn add_value_rlp(&mut self, key: impl AsRef<[u8]>, rlp: Bytes) -> &mut Self {
self.content.insert(key.as_ref().to_vec(), rlp);
self
}

on build this check ensures that all values are rlp(bytes):

enr/src/builder.rs

Lines 159 to 162 in 83cd989

// Sanitize all data, ensuring all RLP data is correctly formatted.
for value in self.content.values() {
Bytes::decode(&mut value.as_ref())?;
}

on reth, this test currently fails because the value is rlp(forkId)

https://github.com/paradigmxyz/reth/blob/bc2634981e2fdca2d968b1dc63d3ca9a716c93e4/crates/net/discv4/src/proto.rs#L660-L664

If all values should be rlp(bytes) where bytes itself can be rlp(forkid), should add_value then insert self.add_value_rlp(key, alloy_rlp::encode(&out)) ?

enr/src/builder.rs

Lines 45 to 50 in 83cd989

/// Adds an arbitrary key-value to the `ENRBuilder`.
pub fn add_value<T: Encodable>(&mut self, key: impl AsRef<[u8]>, value: &T) -> &mut Self {
let mut out = BytesMut::new();
value.encode(&mut out);
self.add_value_rlp(key, out.freeze())
}

enforce right types for spec-reserved keys

There is right now an easy way to overwrite spec-keys with corrupted data via the raw_rpl methods. We have now checks in place for most integer types but the rest should be checked as well

RLP buffer is not advanced after decoding

In impl<K: EnrKey> alloy_rlp::Decodable for Enr<K>:

enr/src/lib.rs

Line 978 in 83cd989

impl<K: EnrKey> Decodable for Enr<K> {

The buffer reference is copied but it's not advanced after success

This makes reth tests fail:
https://github.com/paradigmxyz/reth/blob/7ed23e213f0255ac4f8c73c29372671e3addf300/crates/net/discv4/src/proto.rs#L712

In this assertion:
https://github.com/paradigmxyz/reth/blob/7ed23e213f0255ac4f8c73c29372671e3addf300/crates/net/discv4/src/proto.rs#L726

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.