mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(eth-wire): fuzzing wire encoding roundtrip (#350)
* move hello to separate file * cargo fmt * wip: actual fuzz test * should probably also take advantage of test-fuzz to generate benchmarks like impl_fuzzer_with_input * impl generic roundtrip method * generate test with macro * change testname to fuzzname * add reth-eth-wire to fuzz in ci * add other message types to fuzz * remove unused_crate_dependencies * was causing test issues, may want to revisit whether or not we can include this warning and still use test_fuzz * more afl debugging ci * use more explicit imports in fuzz_rlp * impl Default for types and fuzz ping/pong * Default is necessary for test-fuzz to auto generate a corpus for each type we are fuzz testing * enable AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES * not sure if we should do this in the workflow instead: echo core >/proc/sys/kernel/core_pattern we may miss crashes if we keep this enabled? * remove reth-interfaces from fuzzing * add secp256k1 to reth-db dev deps
This commit is contained in:
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@ -8,6 +8,7 @@ env:
|
|||||||
RUSTFLAGS: -D warnings
|
RUSTFLAGS: -D warnings
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
GETH_BUILD: 1.10.26-e5eb32ac
|
GETH_BUILD: 1.10.26-e5eb32ac
|
||||||
|
AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: 1
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
@ -70,11 +71,16 @@ jobs:
|
|||||||
command: install
|
command: install
|
||||||
args: cargo-test-fuzz afl
|
args: cargo-test-fuzz afl
|
||||||
|
|
||||||
|
- name: check for cargo afl
|
||||||
|
run: |
|
||||||
|
cargo install --force afl
|
||||||
|
cargo afl --version
|
||||||
|
|
||||||
- name: Run fuzz tests
|
- name: Run fuzz tests
|
||||||
run: |
|
run: |
|
||||||
./.github/scripts/fuzz.sh reth-primitives
|
./.github/scripts/fuzz.sh reth-primitives
|
||||||
./.github/scripts/fuzz.sh reth-db
|
./.github/scripts/fuzz.sh reth-db
|
||||||
./.github/scripts/fuzz.sh reth-interfaces
|
./.github/scripts/fuzz.sh reth-eth-wire
|
||||||
./.github/scripts/fuzz.sh reth-codecs
|
./.github/scripts/fuzz.sh reth-codecs
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
|
|||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -3342,6 +3342,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"smol_str",
|
"smol_str",
|
||||||
"snap",
|
"snap",
|
||||||
|
"test-fuzz",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
@ -3394,7 +3395,6 @@ dependencies = [
|
|||||||
"reth-rpc-types",
|
"reth-rpc-types",
|
||||||
"secp256k1",
|
"secp256k1",
|
||||||
"serde",
|
"serde",
|
||||||
"test-fuzz",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
|
|||||||
@ -33,7 +33,6 @@ modular-bitfield = "0.11.2"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
reth-db = { path = "../storage/db", features = ["test-utils"] }
|
reth-db = { path = "../storage/db", features = ["test-utils"] }
|
||||||
test-fuzz = "3.0.4"
|
|
||||||
tokio = { version = "1.21.2", features = ["full"] }
|
tokio = { version = "1.21.2", features = ["full"] }
|
||||||
tokio-stream = { version = "0.1.11", features = ["sync"] }
|
tokio-stream = { version = "0.1.11", features = ["sync"] }
|
||||||
arbitrary = { version = "1.1.7", features = ["derive"]}
|
arbitrary = { version = "1.1.7", features = ["derive"]}
|
||||||
|
|||||||
@ -29,6 +29,7 @@ snap = "1.0.5"
|
|||||||
smol_str = { version = "0.1", features = ["serde"] }
|
smol_str = { version = "0.1", features = ["serde"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
test-fuzz = "3.0.4"
|
||||||
reth-ecies = { path = "../ecies" }
|
reth-ecies = { path = "../ecies" }
|
||||||
ethers-core = { git = "https://github.com/gakonst/ethers-rs", default-features = false }
|
ethers-core = { git = "https://github.com/gakonst/ethers-rs", default-features = false }
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,7 @@
|
|||||||
//! messages.
|
//! messages.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
capability::Capability,
|
capability::Capability, hello::HelloMessage, p2pstream::ProtocolVersion, EthVersion, Status,
|
||||||
p2pstream::{HelloMessage, ProtocolVersion},
|
|
||||||
EthVersion, Status,
|
|
||||||
};
|
};
|
||||||
use reth_primitives::{Chain, ForkId, PeerId, H256, U256};
|
use reth_primitives::{Chain, ForkId, PeerId, H256, U256};
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,9 @@ pub enum CapabilityMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A message indicating a supported capability and capability version.
|
/// A message indicating a supported capability and capability version.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize)]
|
#[derive(
|
||||||
|
Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize, Default,
|
||||||
|
)]
|
||||||
pub struct Capability {
|
pub struct Capability {
|
||||||
/// The name of the subprotocol
|
/// The name of the subprotocol
|
||||||
pub name: SmolStr,
|
pub name: SmolStr,
|
||||||
|
|||||||
@ -65,6 +65,12 @@ impl Display for DisconnectReason {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for DisconnectReason {
|
||||||
|
fn default() -> Self {
|
||||||
|
DisconnectReason::DisconnectRequested
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This represents an unknown disconnect reason with the given code.
|
/// This represents an unknown disconnect reason with the given code.
|
||||||
#[derive(Debug, Clone, Error)]
|
#[derive(Debug, Clone, Error)]
|
||||||
#[error("unknown disconnect reason: {0}")]
|
#[error("unknown disconnect reason: {0}")]
|
||||||
|
|||||||
@ -243,7 +243,8 @@ mod tests {
|
|||||||
use super::UnauthedEthStream;
|
use super::UnauthedEthStream;
|
||||||
use crate::{
|
use crate::{
|
||||||
capability::Capability,
|
capability::Capability,
|
||||||
p2pstream::{HelloMessage, ProtocolVersion, UnauthedP2PStream},
|
hello::HelloMessage,
|
||||||
|
p2pstream::{ProtocolVersion, UnauthedP2PStream},
|
||||||
types::{broadcast::BlockHashNumber, EthMessage, EthVersion, Status},
|
types::{broadcast::BlockHashNumber, EthMessage, EthVersion, Status},
|
||||||
EthStream, PassthroughCodec,
|
EthStream, PassthroughCodec,
|
||||||
};
|
};
|
||||||
|
|||||||
131
crates/net/eth-wire/src/hello.rs
Normal file
131
crates/net/eth-wire/src/hello.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
use crate::{capability::Capability, ProtocolVersion};
|
||||||
|
use reth_primitives::PeerId;
|
||||||
|
use reth_rlp::{RlpDecodable, RlpEncodable};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
// TODO: determine if we should allow for the extra fields at the end like EIP-706 suggests
|
||||||
|
/// Message used in the `p2p` handshake, containing information about the supported RLPx protocol
|
||||||
|
/// version and capabilities.
|
||||||
|
#[derive(
|
||||||
|
Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize, Default,
|
||||||
|
)]
|
||||||
|
pub struct HelloMessage {
|
||||||
|
/// The version of the `p2p` protocol.
|
||||||
|
pub protocol_version: ProtocolVersion,
|
||||||
|
/// Specifies the client software identity, as a human-readable string (e.g.
|
||||||
|
/// "Ethereum(++)/1.0.0").
|
||||||
|
pub client_version: String,
|
||||||
|
/// The list of supported capabilities and their versions.
|
||||||
|
pub capabilities: Vec<Capability>,
|
||||||
|
/// The port that the client is listening on, zero indicates the client is not listening.
|
||||||
|
pub port: u16,
|
||||||
|
/// The secp256k1 public key corresponding to the node's private key.
|
||||||
|
pub id: PeerId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use reth_ecies::util::pk2id;
|
||||||
|
use reth_rlp::{Decodable, Encodable, EMPTY_STRING_CODE};
|
||||||
|
use secp256k1::{SecretKey, SECP256K1};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
capability::Capability,
|
||||||
|
p2pstream::{P2PMessage, P2PMessageID},
|
||||||
|
EthVersion, HelloMessage, ProtocolVersion,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pong_snappy_encoding_parity() {
|
||||||
|
// encode pong using our `Encodable` implementation
|
||||||
|
let pong = P2PMessage::Pong;
|
||||||
|
let mut pong_encoded = Vec::new();
|
||||||
|
pong.encode(&mut pong_encoded);
|
||||||
|
|
||||||
|
// the definition of pong is 0x80 (an empty rlp string)
|
||||||
|
let pong_raw = vec![EMPTY_STRING_CODE];
|
||||||
|
let mut snappy_encoder = snap::raw::Encoder::new();
|
||||||
|
let pong_compressed = snappy_encoder.compress_vec(&pong_raw).unwrap();
|
||||||
|
let mut pong_expected = vec![P2PMessageID::Pong as u8];
|
||||||
|
pong_expected.extend(&pong_compressed);
|
||||||
|
|
||||||
|
// ensure that the two encodings are equal
|
||||||
|
assert_eq!(
|
||||||
|
pong_expected, pong_encoded,
|
||||||
|
"left: {pong_expected:#x?}, right: {pong_encoded:#x?}"
|
||||||
|
);
|
||||||
|
|
||||||
|
// also ensure that the length is correct
|
||||||
|
assert_eq!(pong_expected.len(), P2PMessage::Pong.length());
|
||||||
|
|
||||||
|
// try to decode using Decodable
|
||||||
|
let p2p_message = P2PMessage::decode(&mut &pong_expected[..]).unwrap();
|
||||||
|
assert_eq!(p2p_message, P2PMessage::Pong);
|
||||||
|
|
||||||
|
// finally decode the encoded message with snappy
|
||||||
|
let mut snappy_decoder = snap::raw::Decoder::new();
|
||||||
|
|
||||||
|
// the message id is not compressed, only compress the latest bits
|
||||||
|
let decompressed = snappy_decoder.decompress_vec(&pong_encoded[1..]).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(decompressed, pong_raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hello_encoding_round_trip() {
|
||||||
|
let secret_key = SecretKey::new(&mut rand::thread_rng());
|
||||||
|
let id = pk2id(&secret_key.public_key(SECP256K1));
|
||||||
|
let hello = P2PMessage::Hello(HelloMessage {
|
||||||
|
protocol_version: ProtocolVersion::V5,
|
||||||
|
client_version: "reth/0.1.0".to_string(),
|
||||||
|
capabilities: vec![Capability::new("eth".into(), EthVersion::Eth67 as usize)],
|
||||||
|
port: 30303,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut hello_encoded = Vec::new();
|
||||||
|
hello.encode(&mut hello_encoded);
|
||||||
|
|
||||||
|
let hello_decoded = P2PMessage::decode(&mut &hello_encoded[..]).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(hello, hello_decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hello_encoding_length() {
|
||||||
|
let secret_key = SecretKey::new(&mut rand::thread_rng());
|
||||||
|
let id = pk2id(&secret_key.public_key(SECP256K1));
|
||||||
|
let hello = P2PMessage::Hello(HelloMessage {
|
||||||
|
protocol_version: ProtocolVersion::V5,
|
||||||
|
client_version: "reth/0.1.0".to_string(),
|
||||||
|
capabilities: vec![Capability::new("eth".into(), EthVersion::Eth67 as usize)],
|
||||||
|
port: 30303,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut hello_encoded = Vec::new();
|
||||||
|
hello.encode(&mut hello_encoded);
|
||||||
|
|
||||||
|
assert_eq!(hello_encoded.len(), hello.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hello_message_id_prefix() {
|
||||||
|
// ensure that the hello message id is prefixed
|
||||||
|
let secret_key = SecretKey::new(&mut rand::thread_rng());
|
||||||
|
let id = pk2id(&secret_key.public_key(SECP256K1));
|
||||||
|
let hello = P2PMessage::Hello(HelloMessage {
|
||||||
|
protocol_version: ProtocolVersion::V5,
|
||||||
|
client_version: "reth/0.1.0".to_string(),
|
||||||
|
capabilities: vec![Capability::new("eth".into(), EthVersion::Eth67 as usize)],
|
||||||
|
port: 30303,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut hello_encoded = Vec::new();
|
||||||
|
hello.encode(&mut hello_encoded);
|
||||||
|
|
||||||
|
// zero is encoded as 0x80, the empty string code in RLP
|
||||||
|
assert_eq!(hello_encoded[0], EMPTY_STRING_CODE);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
#![warn(missing_docs, unreachable_pub, unused_crate_dependencies)]
|
#![warn(missing_docs, unreachable_pub)]
|
||||||
#![deny(unused_must_use, rust_2018_idioms)]
|
#![deny(unused_must_use, rust_2018_idioms)]
|
||||||
#![doc(test(
|
#![doc(test(
|
||||||
no_crate_inject,
|
no_crate_inject,
|
||||||
@ -11,6 +11,7 @@ pub mod capability;
|
|||||||
mod disconnect;
|
mod disconnect;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod ethstream;
|
mod ethstream;
|
||||||
|
mod hello;
|
||||||
mod p2pstream;
|
mod p2pstream;
|
||||||
mod pinger;
|
mod pinger;
|
||||||
pub use builder::*;
|
pub use builder::*;
|
||||||
@ -25,5 +26,6 @@ pub use tokio_util::codec::{
|
|||||||
pub use crate::{
|
pub use crate::{
|
||||||
disconnect::DisconnectReason,
|
disconnect::DisconnectReason,
|
||||||
ethstream::{EthStream, UnauthedEthStream, MAX_MESSAGE_SIZE},
|
ethstream::{EthStream, UnauthedEthStream, MAX_MESSAGE_SIZE},
|
||||||
p2pstream::{HelloMessage, P2PStream, ProtocolVersion, UnauthedP2PStream},
|
hello::HelloMessage,
|
||||||
|
p2pstream::{P2PMessage, P2PMessageID, P2PStream, ProtocolVersion, UnauthedP2PStream},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,13 +3,12 @@ use crate::{
|
|||||||
capability::{Capability, SharedCapability},
|
capability::{Capability, SharedCapability},
|
||||||
error::{P2PHandshakeError, P2PStreamError},
|
error::{P2PHandshakeError, P2PStreamError},
|
||||||
pinger::{Pinger, PingerEvent},
|
pinger::{Pinger, PingerEvent},
|
||||||
DisconnectReason,
|
DisconnectReason, HelloMessage,
|
||||||
};
|
};
|
||||||
use bytes::{Buf, Bytes, BytesMut};
|
use bytes::{Buf, Bytes, BytesMut};
|
||||||
use futures::{Sink, SinkExt, StreamExt};
|
use futures::{Sink, SinkExt, StreamExt};
|
||||||
use pin_project::pin_project;
|
use pin_project::pin_project;
|
||||||
use reth_primitives::H512 as PeerId;
|
use reth_rlp::{Decodable, DecodeError, Encodable, EMPTY_STRING_CODE};
|
||||||
use reth_rlp::{Decodable, DecodeError, Encodable, RlpDecodable, RlpEncodable, EMPTY_STRING_CODE};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeSet, HashMap, VecDeque},
|
collections::{BTreeSet, HashMap, VecDeque},
|
||||||
@ -524,7 +523,7 @@ pub fn set_capability_offsets(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This represents only the reserved `p2p` subprotocol messages.
|
/// This represents only the reserved `p2p` subprotocol messages.
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum P2PMessage {
|
pub enum P2PMessage {
|
||||||
/// The first packet sent over the connection, and sent once by both sides.
|
/// The first packet sent over the connection, and sent once by both sides.
|
||||||
Hello(HelloMessage),
|
Hello(HelloMessage),
|
||||||
@ -653,24 +652,6 @@ impl TryFrom<u8> for P2PMessageID {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: determine if we should allow for the extra fields at the end like EIP-706 suggests
|
|
||||||
/// Message used in the `p2p` handshake, containing information about the supported RLPx protocol
|
|
||||||
/// version and capabilities.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize)]
|
|
||||||
pub struct HelloMessage {
|
|
||||||
/// The version of the `p2p` protocol.
|
|
||||||
pub protocol_version: ProtocolVersion,
|
|
||||||
/// Specifies the client software identity, as a human-readable string (e.g.
|
|
||||||
/// "Ethereum(++)/1.0.0").
|
|
||||||
pub client_version: String,
|
|
||||||
/// The list of supported capabilities and their versions.
|
|
||||||
pub capabilities: Vec<Capability>,
|
|
||||||
/// The port that the client is listening on, zero indicates the client is not listening.
|
|
||||||
pub port: u16,
|
|
||||||
/// The secp256k1 public key corresponding to the node's private key.
|
|
||||||
pub id: PeerId,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// RLPx `p2p` protocol version
|
/// RLPx `p2p` protocol version
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum ProtocolVersion {
|
pub enum ProtocolVersion {
|
||||||
@ -701,6 +682,12 @@ impl Decodable for ProtocolVersion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for ProtocolVersion {
|
||||||
|
fn default() -> Self {
|
||||||
|
ProtocolVersion::V5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -842,98 +829,4 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(decompressed, ping_raw);
|
assert_eq!(decompressed, ping_raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pong_snappy_encoding_parity() {
|
|
||||||
// encode pong using our `Encodable` implementation
|
|
||||||
let pong = P2PMessage::Pong;
|
|
||||||
let mut pong_encoded = Vec::new();
|
|
||||||
pong.encode(&mut pong_encoded);
|
|
||||||
|
|
||||||
// the definition of pong is 0x80 (an empty rlp string)
|
|
||||||
let pong_raw = vec![EMPTY_STRING_CODE];
|
|
||||||
let mut snappy_encoder = snap::raw::Encoder::new();
|
|
||||||
let pong_compressed = snappy_encoder.compress_vec(&pong_raw).unwrap();
|
|
||||||
let mut pong_expected = vec![P2PMessageID::Pong as u8];
|
|
||||||
pong_expected.extend(&pong_compressed);
|
|
||||||
|
|
||||||
// ensure that the two encodings are equal
|
|
||||||
assert_eq!(
|
|
||||||
pong_expected, pong_encoded,
|
|
||||||
"left: {pong_expected:#x?}, right: {pong_encoded:#x?}"
|
|
||||||
);
|
|
||||||
|
|
||||||
// also ensure that the length is correct
|
|
||||||
assert_eq!(pong_expected.len(), P2PMessage::Pong.length());
|
|
||||||
|
|
||||||
// try to decode using Decodable
|
|
||||||
let p2p_message = P2PMessage::decode(&mut &pong_expected[..]).unwrap();
|
|
||||||
assert_eq!(p2p_message, P2PMessage::Pong);
|
|
||||||
|
|
||||||
// finally decode the encoded message with snappy
|
|
||||||
let mut snappy_decoder = snap::raw::Decoder::new();
|
|
||||||
|
|
||||||
// the message id is not compressed, only compress the latest bits
|
|
||||||
let decompressed = snappy_decoder.decompress_vec(&pong_encoded[1..]).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(decompressed, pong_raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_hello_encoding_round_trip() {
|
|
||||||
let secret_key = SecretKey::new(&mut rand::thread_rng());
|
|
||||||
let id = pk2id(&secret_key.public_key(SECP256K1));
|
|
||||||
let hello = P2PMessage::Hello(HelloMessage {
|
|
||||||
protocol_version: ProtocolVersion::V5,
|
|
||||||
client_version: "reth/0.1.0".to_string(),
|
|
||||||
capabilities: vec![Capability::new("eth".into(), EthVersion::Eth67 as usize)],
|
|
||||||
port: 30303,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut hello_encoded = Vec::new();
|
|
||||||
hello.encode(&mut hello_encoded);
|
|
||||||
|
|
||||||
let hello_decoded = P2PMessage::decode(&mut &hello_encoded[..]).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(hello, hello_decoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hello_encoding_length() {
|
|
||||||
let secret_key = SecretKey::new(&mut rand::thread_rng());
|
|
||||||
let id = pk2id(&secret_key.public_key(SECP256K1));
|
|
||||||
let hello = P2PMessage::Hello(HelloMessage {
|
|
||||||
protocol_version: ProtocolVersion::V5,
|
|
||||||
client_version: "reth/0.1.0".to_string(),
|
|
||||||
capabilities: vec![Capability::new("eth".into(), EthVersion::Eth67 as usize)],
|
|
||||||
port: 30303,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut hello_encoded = Vec::new();
|
|
||||||
hello.encode(&mut hello_encoded);
|
|
||||||
|
|
||||||
assert_eq!(hello_encoded.len(), hello.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hello_message_id_prefix() {
|
|
||||||
// ensure that the hello message id is prefixed
|
|
||||||
let secret_key = SecretKey::new(&mut rand::thread_rng());
|
|
||||||
let id = pk2id(&secret_key.public_key(SECP256K1));
|
|
||||||
let hello = P2PMessage::Hello(HelloMessage {
|
|
||||||
protocol_version: ProtocolVersion::V5,
|
|
||||||
client_version: "reth/0.1.0".to_string(),
|
|
||||||
capabilities: vec![Capability::new("eth".into(), EthVersion::Eth67 as usize)],
|
|
||||||
port: 30303,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut hello_encoded = Vec::new();
|
|
||||||
hello.encode(&mut hello_encoded);
|
|
||||||
|
|
||||||
// zero is encoded as 0x80, the empty string code in RLP
|
|
||||||
assert_eq!(hello_encoded[0], EMPTY_STRING_CODE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,15 @@ pub struct GetBlockHeaders {
|
|||||||
|
|
||||||
/// The response to [`GetBlockHeaders`], containing headers if any headers were found.
|
/// The response to [`GetBlockHeaders`], containing headers if any headers were found.
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Serialize, Deserialize,
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
RlpEncodableWrapper,
|
||||||
|
RlpDecodableWrapper,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Default,
|
||||||
)]
|
)]
|
||||||
pub struct BlockHeaders(
|
pub struct BlockHeaders(
|
||||||
/// The requested headers.
|
/// The requested headers.
|
||||||
@ -51,7 +59,15 @@ impl From<Vec<Header>> for BlockHeaders {
|
|||||||
|
|
||||||
/// A request for a peer to return block bodies for the given block hashes.
|
/// A request for a peer to return block bodies for the given block hashes.
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Serialize, Deserialize,
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
RlpEncodableWrapper,
|
||||||
|
RlpDecodableWrapper,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Default,
|
||||||
)]
|
)]
|
||||||
pub struct GetBlockBodies(
|
pub struct GetBlockBodies(
|
||||||
/// The block hashes to request bodies for.
|
/// The block hashes to request bodies for.
|
||||||
@ -66,7 +82,9 @@ impl From<Vec<H256>> for GetBlockBodies {
|
|||||||
|
|
||||||
// TODO(onbjerg): We should have this type in primitives
|
// TODO(onbjerg): We should have this type in primitives
|
||||||
/// A response to [`GetBlockBodies`], containing bodies if any bodies were found.
|
/// A response to [`GetBlockBodies`], containing bodies if any bodies were found.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize)]
|
#[derive(
|
||||||
|
Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize, Default,
|
||||||
|
)]
|
||||||
pub struct BlockBody {
|
pub struct BlockBody {
|
||||||
/// Transactions in the block
|
/// Transactions in the block
|
||||||
pub transactions: Vec<TransactionSigned>,
|
pub transactions: Vec<TransactionSigned>,
|
||||||
@ -88,7 +106,15 @@ impl BlockBody {
|
|||||||
/// The response to [`GetBlockBodies`], containing the block bodies that the peer knows about if
|
/// The response to [`GetBlockBodies`], containing the block bodies that the peer knows about if
|
||||||
/// any were found.
|
/// any were found.
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Serialize, Deserialize,
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
RlpEncodableWrapper,
|
||||||
|
RlpDecodableWrapper,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Default,
|
||||||
)]
|
)]
|
||||||
pub struct BlockBodies(
|
pub struct BlockBodies(
|
||||||
/// The requested block bodies, each of which should correspond to a hash in the request.
|
/// The requested block bodies, each of which should correspond to a hash in the request.
|
||||||
|
|||||||
@ -6,7 +6,15 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
/// This informs peers of new blocks that have appeared on the network.
|
/// This informs peers of new blocks that have appeared on the network.
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Serialize, Deserialize,
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
RlpEncodableWrapper,
|
||||||
|
RlpDecodableWrapper,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Default,
|
||||||
)]
|
)]
|
||||||
pub struct NewBlockHashes(
|
pub struct NewBlockHashes(
|
||||||
/// New block hashes and the block number for each blockhash.
|
/// New block hashes and the block number for each blockhash.
|
||||||
@ -29,7 +37,9 @@ impl NewBlockHashes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A block hash _and_ a block number.
|
/// A block hash _and_ a block number.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize)]
|
#[derive(
|
||||||
|
Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize, Default,
|
||||||
|
)]
|
||||||
pub struct BlockHashNumber {
|
pub struct BlockHashNumber {
|
||||||
/// The block hash
|
/// The block hash
|
||||||
pub hash: H256,
|
pub hash: H256,
|
||||||
@ -64,7 +74,9 @@ pub struct RawBlockBody {
|
|||||||
|
|
||||||
/// A new block with the current total difficulty, which includes the difficulty of the returned
|
/// A new block with the current total difficulty, which includes the difficulty of the returned
|
||||||
/// block.
|
/// block.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize)]
|
#[derive(
|
||||||
|
Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize, Default,
|
||||||
|
)]
|
||||||
pub struct NewBlock {
|
pub struct NewBlock {
|
||||||
/// A new block.
|
/// A new block.
|
||||||
pub block: RawBlockBody,
|
pub block: RawBlockBody,
|
||||||
@ -75,7 +87,15 @@ pub struct NewBlock {
|
|||||||
/// This informs peers of transactions that have appeared on the network and are not yet included
|
/// This informs peers of transactions that have appeared on the network and are not yet included
|
||||||
/// in a block.
|
/// in a block.
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Serialize, Deserialize,
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
RlpEncodableWrapper,
|
||||||
|
RlpDecodableWrapper,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Default,
|
||||||
)]
|
)]
|
||||||
pub struct Transactions(
|
pub struct Transactions(
|
||||||
/// New transactions for the peer to include in its mempool.
|
/// New transactions for the peer to include in its mempool.
|
||||||
@ -107,7 +127,15 @@ pub struct SharedTransactions(
|
|||||||
/// This informs peers of transaction hashes for transactions that have appeared on the network,
|
/// This informs peers of transaction hashes for transactions that have appeared on the network,
|
||||||
/// but have not been included in a block.
|
/// but have not been included in a block.
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Serialize, Deserialize,
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
RlpEncodableWrapper,
|
||||||
|
RlpDecodableWrapper,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Default,
|
||||||
)]
|
)]
|
||||||
pub struct NewPooledTransactionHashes(
|
pub struct NewPooledTransactionHashes(
|
||||||
/// Transaction hashes for new transactions that have appeared on the network.
|
/// Transaction hashes for new transactions that have appeared on the network.
|
||||||
|
|||||||
@ -5,7 +5,15 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
/// A request for transaction receipts from the given block hashes.
|
/// A request for transaction receipts from the given block hashes.
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Serialize, Deserialize,
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
RlpEncodableWrapper,
|
||||||
|
RlpDecodableWrapper,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Default,
|
||||||
)]
|
)]
|
||||||
pub struct GetReceipts(
|
pub struct GetReceipts(
|
||||||
/// The block hashes to request receipts for.
|
/// The block hashes to request receipts for.
|
||||||
@ -15,7 +23,15 @@ pub struct GetReceipts(
|
|||||||
/// The response to [`GetReceipts`], containing receipt lists that correspond to each block
|
/// The response to [`GetReceipts`], containing receipt lists that correspond to each block
|
||||||
/// requested.
|
/// requested.
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Serialize, Deserialize,
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
RlpEncodableWrapper,
|
||||||
|
RlpDecodableWrapper,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Default,
|
||||||
)]
|
)]
|
||||||
pub struct Receipts(
|
pub struct Receipts(
|
||||||
/// Each receipt hash should correspond to a block hash in the request.
|
/// Each receipt hash should correspond to a block hash in the request.
|
||||||
|
|||||||
@ -7,7 +7,15 @@ use serde::{Deserialize, Serialize};
|
|||||||
/// This message was removed in `eth/67`, only clients running `eth/66` or earlier will respond to
|
/// This message was removed in `eth/67`, only clients running `eth/66` or earlier will respond to
|
||||||
/// this message.
|
/// this message.
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Serialize, Deserialize,
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
RlpEncodableWrapper,
|
||||||
|
RlpDecodableWrapper,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Default,
|
||||||
)]
|
)]
|
||||||
pub struct GetNodeData(pub Vec<H256>);
|
pub struct GetNodeData(pub Vec<H256>);
|
||||||
|
|
||||||
@ -17,7 +25,15 @@ pub struct GetNodeData(pub Vec<H256>);
|
|||||||
/// Not all nodes are guaranteed to be returned by the peer.
|
/// Not all nodes are guaranteed to be returned by the peer.
|
||||||
/// This message was removed in `eth/67`.
|
/// This message was removed in `eth/67`.
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Serialize, Deserialize,
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
RlpEncodableWrapper,
|
||||||
|
RlpDecodableWrapper,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Default,
|
||||||
)]
|
)]
|
||||||
pub struct NodeData(pub Vec<bytes::Bytes>);
|
pub struct NodeData(pub Vec<bytes::Bytes>);
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,15 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
/// A list of transaction hashes that the peer would like transaction bodies for.
|
/// A list of transaction hashes that the peer would like transaction bodies for.
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Serialize, Deserialize,
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
RlpEncodableWrapper,
|
||||||
|
RlpDecodableWrapper,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Default,
|
||||||
)]
|
)]
|
||||||
pub struct GetPooledTransactions(
|
pub struct GetPooledTransactions(
|
||||||
/// The transaction hashes to request transaction bodies for.
|
/// The transaction hashes to request transaction bodies for.
|
||||||
@ -29,7 +37,15 @@ where
|
|||||||
/// corresponds to a requested hash. Hashes may need to be re-requested if the bodies are not
|
/// corresponds to a requested hash. Hashes may need to be re-requested if the bodies are not
|
||||||
/// included in the response.
|
/// included in the response.
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Serialize, Deserialize,
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
RlpEncodableWrapper,
|
||||||
|
RlpDecodableWrapper,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Default,
|
||||||
)]
|
)]
|
||||||
pub struct PooledTransactions(
|
pub struct PooledTransactions(
|
||||||
/// The transaction bodies, each of which should correspond to a requested hash.
|
/// The transaction bodies, each of which should correspond to a requested hash.
|
||||||
|
|||||||
116
crates/net/eth-wire/tests/fuzz_roundtrip.rs
Normal file
116
crates/net/eth-wire/tests/fuzz_roundtrip.rs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
//! Round-trip encoding fuzzing for the `eth-wire` crate.
|
||||||
|
use reth_rlp::{Decodable, Encodable};
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
/// Creates a fuzz test for a type that should be [`Encodable`](reth_rlp::Encodable) and
|
||||||
|
/// [`Decodable`](reth_rlp::Decodable).
|
||||||
|
///
|
||||||
|
/// The test will create a random instance of the type, encode it, and then decode it.
|
||||||
|
fn roundtrip_encoding<T>(thing: T)
|
||||||
|
where
|
||||||
|
T: Encodable + Decodable + Clone + Serialize + Debug + PartialEq + Eq,
|
||||||
|
{
|
||||||
|
let mut encoded = Vec::new();
|
||||||
|
thing.encode(&mut encoded);
|
||||||
|
let decoded = T::decode(&mut &encoded[..]).unwrap();
|
||||||
|
assert_eq!(thing, decoded, "expected: {thing:?}, got: {decoded:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a fuzz test for a rlp encodable and decodable type.
|
||||||
|
macro_rules! fuzz_type_and_name {
|
||||||
|
( $x:ty, $fuzzname:ident ) => {
|
||||||
|
/// Fuzzes the round-trip encoding of the type.
|
||||||
|
#[test_fuzz]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn $fuzzname(thing: $x) {
|
||||||
|
roundtrip_encoding::<$x>(thing)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[cfg(any(test, feature = "bench"))]
|
||||||
|
pub mod fuzz_rlp {
|
||||||
|
use reth_eth_wire::{
|
||||||
|
BlockBodies, BlockHeaders, DisconnectReason, GetBlockBodies, GetBlockHeaders, GetNodeData,
|
||||||
|
GetPooledTransactions, GetReceipts, HelloMessage, NewBlock, NewBlockHashes,
|
||||||
|
NewPooledTransactionHashes, NodeData, P2PMessage, PooledTransactions, Receipts, Status,
|
||||||
|
Transactions,
|
||||||
|
};
|
||||||
|
use reth_primitives::BlockHashOrNumber;
|
||||||
|
use reth_rlp::{RlpDecodableWrapper, RlpEncodableWrapper};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use test_fuzz::test_fuzz;
|
||||||
|
|
||||||
|
use crate::roundtrip_encoding;
|
||||||
|
|
||||||
|
// p2p subprotocol messages
|
||||||
|
fuzz_type_and_name!(HelloMessage, fuzz_HelloMessage);
|
||||||
|
fuzz_type_and_name!(DisconnectReason, fuzz_DisconnectReason);
|
||||||
|
|
||||||
|
// eth subprotocol messages
|
||||||
|
fuzz_type_and_name!(Status, fuzz_Status);
|
||||||
|
fuzz_type_and_name!(NewBlockHashes, fuzz_NewBlockHashes);
|
||||||
|
fuzz_type_and_name!(Transactions, fuzz_Transactions);
|
||||||
|
|
||||||
|
// GetBlockHeaders implements all the traits required for roundtrip_encoding, so why is this
|
||||||
|
// wrapper type needed?
|
||||||
|
//
|
||||||
|
// While GetBlockHeaders implements all traits needed to work for test-fuzz, it does not have
|
||||||
|
// an obvious Default implementation since BlockHashOrNumber can be either a hash or number,
|
||||||
|
// and the default value of BlockHashOrNumber is not obvious.
|
||||||
|
//
|
||||||
|
// We just provide a default value here so test-fuzz can auto-generate a corpus file for the
|
||||||
|
// type.
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
RlpEncodableWrapper,
|
||||||
|
RlpDecodableWrapper,
|
||||||
|
)]
|
||||||
|
struct GetBlockHeadersWrapper(pub GetBlockHeaders);
|
||||||
|
|
||||||
|
impl Default for GetBlockHeadersWrapper {
|
||||||
|
fn default() -> Self {
|
||||||
|
GetBlockHeadersWrapper(GetBlockHeaders {
|
||||||
|
start_block: BlockHashOrNumber::Number(0),
|
||||||
|
limit: Default::default(),
|
||||||
|
skip: Default::default(),
|
||||||
|
direction: Default::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fuzz_type_and_name!(GetBlockHeadersWrapper, fuzz_GetBlockHeaders);
|
||||||
|
|
||||||
|
fuzz_type_and_name!(BlockHeaders, fuzz_BlockHeaders);
|
||||||
|
fuzz_type_and_name!(GetBlockBodies, fuzz_GetBlockBodies);
|
||||||
|
fuzz_type_and_name!(BlockBodies, fuzz_BlockBodies);
|
||||||
|
fuzz_type_and_name!(NewBlock, fuzz_NewBlock);
|
||||||
|
fuzz_type_and_name!(NewPooledTransactionHashes, fuzz_NewPooledTransactionHashes);
|
||||||
|
fuzz_type_and_name!(GetPooledTransactions, fuzz_GetPooledTransactions);
|
||||||
|
fuzz_type_and_name!(PooledTransactions, fuzz_PooledTransactions);
|
||||||
|
fuzz_type_and_name!(GetNodeData, fuzz_GetNodeData);
|
||||||
|
fuzz_type_and_name!(NodeData, fuzz_NodeData);
|
||||||
|
fuzz_type_and_name!(GetReceipts, fuzz_GetReceipts);
|
||||||
|
fuzz_type_and_name!(Receipts, fuzz_Receipts);
|
||||||
|
|
||||||
|
// manually test Ping and Pong which are not covered by the above
|
||||||
|
|
||||||
|
/// Tests the round-trip encoding of Ping
|
||||||
|
#[test]
|
||||||
|
fn roundtrip_ping() {
|
||||||
|
roundtrip_encoding::<P2PMessage>(P2PMessage::Ping)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests the round-trip encoding of Pong
|
||||||
|
#[test]
|
||||||
|
fn roundtrip_pong() {
|
||||||
|
roundtrip_encoding::<P2PMessage>(P2PMessage::Pong)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -37,12 +37,16 @@ eyre = "0.6.8"
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.3.0"
|
tempfile = "3.3.0"
|
||||||
test-fuzz = "3.0.4"
|
test-fuzz = "3.0.4"
|
||||||
|
|
||||||
criterion = "0.4.0"
|
criterion = "0.4.0"
|
||||||
iai = "0.1.1"
|
iai = "0.1.1"
|
||||||
tokio = { version = "1.21.2", features = ["full"] }
|
tokio = { version = "1.21.2", features = ["full"] }
|
||||||
arbitrary = { version = "1.1.7", features = ["derive"]}
|
arbitrary = { version = "1.1.7", features = ["derive"]}
|
||||||
reth-db = { path = ".", features = ["test-utils","bench"]}
|
reth-db = { path = ".", features = ["test-utils","bench"]}
|
||||||
|
|
||||||
|
# needed for test-fuzz to work properly, see https://github.com/paradigmxyz/reth/pull/177#discussion_r1021172198
|
||||||
|
secp256k1 = "0.24.0"
|
||||||
|
|
||||||
reth-interfaces = { path = "../../interfaces",features=["bench"] }
|
reth-interfaces = { path = "../../interfaces",features=["bench"] }
|
||||||
async-trait = "0.1.58"
|
async-trait = "0.1.58"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user