mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
chore(net): Add proptest roundtrip to rlp types (#829)
This commit is contained in:
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -3812,6 +3812,7 @@ dependencies = [
|
||||
name = "reth-eth-wire"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"bytes",
|
||||
"ethers-core",
|
||||
"futures",
|
||||
@ -3819,7 +3820,10 @@ dependencies = [
|
||||
"hex-literal",
|
||||
"metrics",
|
||||
"pin-project",
|
||||
"proptest",
|
||||
"proptest-derive",
|
||||
"rand 0.8.5",
|
||||
"reth-codecs",
|
||||
"reth-ecies",
|
||||
"reth-primitives",
|
||||
"reth-rlp",
|
||||
@ -4049,6 +4053,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"strum",
|
||||
"sucds",
|
||||
"test-fuzz",
|
||||
"thiserror",
|
||||
|
||||
@ -252,7 +252,7 @@ decode_integer!(ethnum::U256);
|
||||
mod ethereum_types_support {
|
||||
use super::*;
|
||||
use ethereum_types::*;
|
||||
use revm_interpreter::{B160, B256, U256 as RU256};
|
||||
use revm_interpreter::{ruint::aliases::U128 as RU128, B160, B256, U256 as RU256};
|
||||
|
||||
macro_rules! fixed_hash_impl {
|
||||
($t:ty) => {
|
||||
@ -337,6 +337,7 @@ mod ethereum_types_support {
|
||||
}
|
||||
|
||||
fixed_revm_uint_impl!(RU256, 32);
|
||||
fixed_revm_uint_impl!(RU128, 16);
|
||||
|
||||
fixed_uint_impl!(U64, 8);
|
||||
fixed_uint_impl!(U128, 16);
|
||||
|
||||
@ -240,7 +240,7 @@ mod ethereum_types_support {
|
||||
use super::*;
|
||||
use ethereum_types::*;
|
||||
|
||||
use revm_interpreter::{B160, B256, U256 as RU256};
|
||||
use revm_interpreter::{ruint::aliases::U128 as RU128, B160, B256, U256 as RU256};
|
||||
|
||||
macro_rules! fixed_hash_impl {
|
||||
($t:ty) => {
|
||||
@ -311,6 +311,7 @@ mod ethereum_types_support {
|
||||
};
|
||||
}
|
||||
|
||||
fixed_revm_uint_impl!(RU128, 16);
|
||||
fixed_revm_uint_impl!(RU256, 32);
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ thiserror = "1"
|
||||
serde = "1"
|
||||
|
||||
# reth
|
||||
reth-codecs = { path = "../../storage/codecs" }
|
||||
reth-primitives = { path = "../../primitives" }
|
||||
reth-rlp = { path = "../../common/rlp", features = ["alloc", "derive", "std", "ethereum-types", "smol_str"] }
|
||||
|
||||
@ -29,7 +30,13 @@ snap = "1.0.5"
|
||||
smol_str = { version = "0.1", features = ["serde"] }
|
||||
metrics = "0.20.1"
|
||||
|
||||
# arbitrary utils
|
||||
arbitrary = { version = "1.1.7", features = ["derive"], optional = true }
|
||||
proptest = { version = "1.0", optional = true }
|
||||
proptest-derive = { version = "0.3", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
reth-primitives = { path = "../../primitives", features = ["arbitrary"] }
|
||||
reth-ecies = { path = "../ecies" }
|
||||
reth-tracing = { path = "../../tracing" }
|
||||
ethers-core = { git = "https://github.com/gakonst/ethers-rs", default-features = false }
|
||||
@ -39,3 +46,16 @@ tokio-util = { version = "0.7.4", features = ["io", "codec"] }
|
||||
hex-literal = "0.3"
|
||||
rand = "0.8"
|
||||
secp256k1 = { version = "0.24.2", features = ["global-context", "rand-std", "recovery"] }
|
||||
|
||||
arbitrary = { version = "1.1.7", features = ["derive"] }
|
||||
proptest = { version = "1.0" }
|
||||
proptest-derive = "0.3"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
arbitrary = ["reth-primitives/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive"]
|
||||
|
||||
[[test]]
|
||||
name = "fuzz_roundtrip"
|
||||
path = "tests/fuzz_roundtrip.rs"
|
||||
required-features = ["arbitrary"]
|
||||
@ -2,10 +2,17 @@
|
||||
|
||||
use crate::{version::ParseVersionError, EthMessage, EthVersion};
|
||||
use bytes::{BufMut, Bytes};
|
||||
use reth_codecs::add_arbitrary_tests;
|
||||
use reth_rlp::{Decodable, DecodeError, Encodable, RlpDecodable, RlpEncodable};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smol_str::SmolStr;
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
use proptest::{
|
||||
arbitrary::{any_with, ParamsFor},
|
||||
strategy::{BoxedStrategy, Strategy},
|
||||
};
|
||||
|
||||
/// A Capability message consisting of the message-id and the payload
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct RawCapabilityMessage {
|
||||
@ -26,6 +33,7 @@ pub enum CapabilityMessage {
|
||||
}
|
||||
|
||||
/// A message indicating a supported capability and capability version.
|
||||
#[add_arbitrary_tests(rlp)]
|
||||
#[derive(
|
||||
Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize, Default, Hash,
|
||||
)]
|
||||
@ -55,6 +63,30 @@ impl Capability {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
impl<'a> arbitrary::Arbitrary<'a> for Capability {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let version = u.int_in_range(0..=32)?; // TODO: What's the max?
|
||||
let name: SmolStr = String::arbitrary(u)?.into(); // TODO: what possible values?
|
||||
Ok(Self { name, version })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
impl proptest::arbitrary::Arbitrary for Capability {
|
||||
type Parameters = ParamsFor<String>;
|
||||
type Strategy = BoxedStrategy<Capability>;
|
||||
|
||||
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
|
||||
any_with::<String>(args) // TODO: what possible values?
|
||||
.prop_flat_map(move |name| {
|
||||
any_with::<usize>(()) // TODO: What's the max?
|
||||
.prop_map(move |version| Capability { name: name.clone().into(), version })
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents all capabilities of a node.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Capabilities {
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
//! Disconnect
|
||||
|
||||
use bytes::Buf;
|
||||
use reth_codecs::derive_arbitrary;
|
||||
use reth_rlp::{Decodable, DecodeError, Encodable, Header};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
use thiserror::Error;
|
||||
|
||||
/// RLPx disconnect reason.
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DisconnectReason {
|
||||
/// Disconnect requested by the local node or remote peer.
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::{capability::Capability, EthVersion, ProtocolVersion};
|
||||
use reth_codecs::derive_arbitrary;
|
||||
use reth_primitives::PeerId;
|
||||
use reth_rlp::{RlpDecodable, RlpEncodable};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -9,6 +10,7 @@ pub(crate) const DEFAULT_CLIENT_VERSION: &str = concat!("reth/v", env!("CARGO_PK
|
||||
// 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_arbitrary(rlp)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize)]
|
||||
pub struct HelloMessage {
|
||||
/// The version of the `p2p` protocol.
|
||||
|
||||
@ -9,6 +9,7 @@ use bytes::{Buf, Bytes, BytesMut};
|
||||
use futures::{Sink, SinkExt, StreamExt};
|
||||
use metrics::counter;
|
||||
use pin_project::pin_project;
|
||||
use reth_codecs::derive_arbitrary;
|
||||
use reth_rlp::{Decodable, DecodeError, Encodable, EMPTY_LIST_CODE};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
@ -584,6 +585,7 @@ pub fn set_capability_offsets(
|
||||
}
|
||||
|
||||
/// This represents only the reserved `p2p` subprotocol messages.
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum P2PMessage {
|
||||
/// The first packet sent over the connection, and sent once by both sides.
|
||||
@ -715,6 +717,7 @@ impl TryFrom<u8> for P2PMessageID {
|
||||
}
|
||||
|
||||
/// RLPx `p2p` protocol version
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum ProtocolVersion {
|
||||
/// `p2p` version 4
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
//! Implements the `GetBlockHeaders`, `GetBlockBodies`, `BlockHeaders`, and `BlockBodies` message
|
||||
//! types.
|
||||
use super::RawBlockBody;
|
||||
use reth_codecs::derive_arbitrary;
|
||||
use reth_primitives::{BlockHashOrNumber, Header, HeadersDirection, TransactionSigned, H256};
|
||||
use reth_rlp::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -14,6 +15,7 @@ use serde::{Deserialize, Serialize};
|
||||
///
|
||||
/// If the [`skip`](#structfield.skip) field is non-zero, the peer must skip that amount of headers
|
||||
/// in the direction specified by [`reverse`](#structfield.reverse).
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, PartialEq, Eq, Hash, RlpEncodable, RlpDecodable, Serialize, Deserialize,
|
||||
)]
|
||||
@ -35,6 +37,7 @@ pub struct GetBlockHeaders {
|
||||
}
|
||||
|
||||
/// The response to [`GetBlockHeaders`], containing headers if any headers were found.
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
@ -58,6 +61,7 @@ impl From<Vec<Header>> for BlockHeaders {
|
||||
}
|
||||
|
||||
/// A request for a peer to return block bodies for the given block hashes.
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
@ -82,6 +86,7 @@ impl From<Vec<H256>> for GetBlockBodies {
|
||||
|
||||
// TODO(onbjerg): We should have this type in primitives
|
||||
/// A response to [`GetBlockBodies`], containing bodies if any bodies were found.
|
||||
#[derive_arbitrary(rlp, 10)]
|
||||
#[derive(
|
||||
Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize, Default,
|
||||
)]
|
||||
@ -105,6 +110,7 @@ impl BlockBody {
|
||||
|
||||
/// The response to [`GetBlockBodies`], containing the block bodies that the peer knows about if
|
||||
/// any were found.
|
||||
#[derive_arbitrary(rlp, 1)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
//! Types for broadcasting new data.
|
||||
use reth_codecs::derive_arbitrary;
|
||||
use reth_primitives::{Header, TransactionSigned, H256, U128};
|
||||
use reth_rlp::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// This informs peers of new blocks that have appeared on the network.
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
@ -37,6 +39,7 @@ impl NewBlockHashes {
|
||||
}
|
||||
|
||||
/// A block hash _and_ a block number.
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(
|
||||
Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize, Default,
|
||||
)]
|
||||
@ -60,6 +63,7 @@ impl From<NewBlockHashes> for Vec<BlockHashNumber> {
|
||||
}
|
||||
|
||||
/// A block body, including transactions and uncle headers.
|
||||
#[derive_arbitrary(rlp, 25)]
|
||||
#[derive(
|
||||
Debug, Clone, PartialEq, Eq, Default, RlpEncodable, RlpDecodable, Serialize, Deserialize,
|
||||
)]
|
||||
@ -74,6 +78,7 @@ pub struct RawBlockBody {
|
||||
|
||||
/// A new block with the current total difficulty, which includes the difficulty of the returned
|
||||
/// block.
|
||||
#[derive_arbitrary(rlp, 25)]
|
||||
#[derive(
|
||||
Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize, Default,
|
||||
)]
|
||||
@ -86,6 +91,7 @@ pub struct NewBlock {
|
||||
|
||||
/// This informs peers of transactions that have appeared on the network and are not yet included
|
||||
/// in a block.
|
||||
#[derive_arbitrary(rlp, 10)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
@ -118,6 +124,7 @@ impl From<Transactions> for Vec<TransactionSigned> {
|
||||
///
|
||||
/// The list of transactions is constructed on per-peers basis, but the underlying transaction
|
||||
/// objects are shared.
|
||||
#[derive_arbitrary(rlp, 20)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper)]
|
||||
pub struct SharedTransactions(
|
||||
/// New transactions for the peer to include in its mempool.
|
||||
@ -126,6 +133,7 @@ pub struct SharedTransactions(
|
||||
|
||||
/// This informs peers of transaction hashes for transactions that have appeared on the network,
|
||||
/// but have not been included in a block.
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
//! Implements the `GetReceipts` and `Receipts` message types.
|
||||
use reth_codecs::derive_arbitrary;
|
||||
use reth_primitives::{Receipt, H256};
|
||||
use reth_rlp::{RlpDecodableWrapper, RlpEncodableWrapper};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A request for transaction receipts from the given block hashes.
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
@ -22,6 +24,7 @@ pub struct GetReceipts(
|
||||
|
||||
/// The response to [`GetReceipts`], containing receipt lists that correspond to each block
|
||||
/// requested.
|
||||
#[derive_arbitrary(rlp, 1)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
//! Implements the `GetNodeData` and `NodeData` message types.
|
||||
use reth_primitives::H256;
|
||||
use reth_codecs::derive_arbitrary;
|
||||
use reth_primitives::{Bytes, H256};
|
||||
use reth_rlp::{RlpDecodableWrapper, RlpEncodableWrapper};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A request for state tree nodes corresponding to the given hashes.
|
||||
/// This message was removed in `eth/67`, only clients running `eth/66` or earlier will respond to
|
||||
/// this message.
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
@ -24,6 +26,7 @@ pub struct GetNodeData(pub Vec<H256>);
|
||||
///
|
||||
/// Not all nodes are guaranteed to be returned by the peer.
|
||||
/// This message was removed in `eth/67`.
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
@ -35,7 +38,7 @@ pub struct GetNodeData(pub Vec<H256>);
|
||||
Deserialize,
|
||||
Default,
|
||||
)]
|
||||
pub struct NodeData(pub Vec<bytes::Bytes>);
|
||||
pub struct NodeData(pub Vec<Bytes>);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use crate::{EthVersion, StatusBuilder};
|
||||
|
||||
use reth_codecs::derive_arbitrary;
|
||||
use reth_primitives::{Chain, ForkId, Hardfork, H256, MAINNET_GENESIS, U256};
|
||||
use reth_rlp::{RlpDecodable, RlpEncodable};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -10,6 +11,7 @@ use std::fmt::{Debug, Display};
|
||||
///
|
||||
/// When performing a handshake, the total difficulty is not guaranteed to correspond to the block
|
||||
/// hash. This information should be treated as untrusted.
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable, Serialize, Deserialize)]
|
||||
pub struct Status {
|
||||
/// The current protocol version. For example, peers running `eth/66` would have a version of
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
//! Implements the `GetPooledTransactions` and `PooledTransactions` message types.
|
||||
use reth_codecs::derive_arbitrary;
|
||||
use reth_primitives::{TransactionSigned, H256};
|
||||
use reth_rlp::{RlpDecodableWrapper, RlpEncodableWrapper};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A list of transaction hashes that the peer would like transaction bodies for.
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
@ -36,6 +38,7 @@ where
|
||||
/// as the request's hashes. Hashes may be skipped, and the client should ensure that each body
|
||||
/// corresponds to a requested hash. Hashes may need to be re-requested if the bodies are not
|
||||
/// included in the response.
|
||||
#[derive_arbitrary(rlp, 10)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
|
||||
@ -47,13 +47,14 @@ macro_rules! fuzz_type_and_name {
|
||||
#[allow(non_snake_case)]
|
||||
#[cfg(any(test, feature = "bench"))]
|
||||
pub mod fuzz_rlp {
|
||||
use reth_codecs::derive_arbitrary;
|
||||
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_primitives::{BlockHashOrNumber, TransactionSigned};
|
||||
use reth_rlp::{RlpDecodableWrapper, RlpEncodableWrapper};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use test_fuzz::test_fuzz;
|
||||
@ -64,6 +65,7 @@ pub mod fuzz_rlp {
|
||||
|
||||
// see message below for why wrapper types are necessary for fuzzing types that do not have a
|
||||
// Default impl
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
@ -105,6 +107,7 @@ pub mod fuzz_rlp {
|
||||
//
|
||||
// We just provide a default value here so test-fuzz can auto-generate a corpus file for the
|
||||
// type.
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
@ -141,6 +144,7 @@ pub mod fuzz_rlp {
|
||||
fuzz_type_and_name!(NodeData, fuzz_NodeData);
|
||||
fuzz_type_and_name!(GetReceipts, fuzz_GetReceipts);
|
||||
fuzz_type_and_name!(Receipts, fuzz_Receipts);
|
||||
fuzz_type_and_name!(TransactionSigned, fuzz_TransactionSigned);
|
||||
|
||||
// manually test Ping and Pong which are not covered by the above
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ use reth_eth_wire::{
|
||||
SharedTransactions, Transactions,
|
||||
};
|
||||
use reth_interfaces::p2p::error::{RequestError, RequestResult};
|
||||
use reth_primitives::{Header, PeerId, Receipt, TransactionSigned, H256};
|
||||
use reth_primitives::{Bytes, Header, PeerId, Receipt, TransactionSigned, H256};
|
||||
use std::{
|
||||
fmt,
|
||||
sync::Arc,
|
||||
@ -198,7 +198,7 @@ pub enum PeerResponseResult {
|
||||
BlockHeaders(RequestResult<Vec<Header>>),
|
||||
BlockBodies(RequestResult<Vec<BlockBody>>),
|
||||
PooledTransactions(RequestResult<Vec<TransactionSigned>>),
|
||||
NodeData(RequestResult<Vec<bytes::Bytes>>),
|
||||
NodeData(RequestResult<Vec<Bytes>>),
|
||||
Receipts(RequestResult<Vec<Vec<Receipt>>>),
|
||||
}
|
||||
|
||||
|
||||
@ -62,6 +62,7 @@ hash-db = "0.15"
|
||||
arbitrary = { version = "1.1.7", features = ["derive"], optional = true }
|
||||
proptest = { version = "1.0", optional = true }
|
||||
proptest-derive = { version = "0.3", optional = true }
|
||||
strum = { version = "0.24", features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1.0"
|
||||
|
||||
74
crates/primitives/src/bits.rs
Normal file
74
crates/primitives/src/bits.rs
Normal file
@ -0,0 +1,74 @@
|
||||
//! Fixed hash types
|
||||
use bytes::Buf;
|
||||
use derive_more::{AsRef, Deref};
|
||||
use fixed_hash::construct_fixed_hash;
|
||||
use impl_serde::impl_fixed_hash_serde;
|
||||
use reth_codecs::{impl_hash_compact, Compact};
|
||||
use reth_rlp::{RlpDecodableWrapper, RlpEncodableWrapper, RlpMaxEncodedLen};
|
||||
|
||||
/// Implements a fixed hash type (eg. H512) with `serde`, `Arbitrary`, `proptest::Arbitrary` and
|
||||
/// `Compact` support.
|
||||
#[macro_export]
|
||||
macro_rules! impl_fixed_hash_type {
|
||||
($(($name:tt, $size:expr)),+) => {
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
use proptest::{
|
||||
arbitrary::{any_with, ParamsFor},
|
||||
strategy::{BoxedStrategy, Strategy},
|
||||
};
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
use arbitrary::Arbitrary;
|
||||
|
||||
$(
|
||||
construct_fixed_hash! {
|
||||
#[cfg_attr(any(test, feature = "arbitrary"), derive(Arbitrary))]
|
||||
#[derive(AsRef, Deref, RlpEncodableWrapper, RlpDecodableWrapper, RlpMaxEncodedLen)]
|
||||
#[doc = concat!(stringify!($name), " fixed hash type.")]
|
||||
pub struct $name($size);
|
||||
}
|
||||
|
||||
impl_hash_compact!($name);
|
||||
|
||||
impl_fixed_hash_serde!($name, $size);
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
impl proptest::arbitrary::Arbitrary for $name {
|
||||
type Parameters = ParamsFor<u8>;
|
||||
type Strategy = BoxedStrategy<$name>;
|
||||
|
||||
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
|
||||
proptest::collection::vec(any_with::<u8>(args), $size)
|
||||
.prop_map(move |vec| $name::from_slice(&vec))
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
)+
|
||||
|
||||
#[cfg(test)]
|
||||
mod hash_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn arbitrary() {
|
||||
$(
|
||||
proptest::proptest!(|(field: $name)| {
|
||||
let mut buf = vec![];
|
||||
field.to_compact(&mut buf);
|
||||
|
||||
// Add noise. We want to make sure that only $size bytes get consumed.
|
||||
buf.push(1);
|
||||
|
||||
let (decoded, remaining_buf) = $name::from_compact(&buf, buf.len());
|
||||
|
||||
assert!(field == decoded);
|
||||
assert!(remaining_buf.len() == 1);
|
||||
});
|
||||
)+
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_fixed_hash_type!((H64, 8), (H512, 64));
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::{Header, SealedHeader, TransactionSigned, H256};
|
||||
use reth_codecs::derive_arbitrary;
|
||||
use reth_rlp::{Decodable, DecodeError, Encodable, RlpDecodable, RlpEncodable};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ops::Deref;
|
||||
@ -61,6 +62,7 @@ impl Deref for SealedBlock {
|
||||
}
|
||||
|
||||
/// Either a block hash _or_ a block number
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum BlockHashOrNumber {
|
||||
/// A block hash
|
||||
|
||||
@ -1,45 +1,16 @@
|
||||
//! Bloom related utilities.
|
||||
use crate::{keccak256, Log};
|
||||
//! Bloom type.
|
||||
use crate::{impl_fixed_hash_type, keccak256, Log};
|
||||
use bytes::Buf;
|
||||
use derive_more::{AsRef, Deref};
|
||||
use fixed_hash::construct_fixed_hash;
|
||||
use impl_serde::impl_fixed_hash_serde;
|
||||
use reth_codecs::{impl_hash_compact, Compact};
|
||||
use reth_rlp::{RlpDecodableWrapper, RlpEncodableWrapper};
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
use proptest::{
|
||||
arbitrary::{any_with, Arbitrary as PropTestArbitrary, ParamsFor},
|
||||
strategy::{BoxedStrategy, Strategy},
|
||||
};
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
use arbitrary::Arbitrary;
|
||||
use reth_rlp::{RlpDecodableWrapper, RlpEncodableWrapper, RlpMaxEncodedLen};
|
||||
|
||||
/// Length of bloom filter used for Ethereum.
|
||||
pub const BLOOM_BYTE_LENGTH: usize = 256;
|
||||
|
||||
construct_fixed_hash! {
|
||||
/// 2048 bits type.
|
||||
#[cfg_attr(any(test, feature = "arbitrary"), derive(Arbitrary))]
|
||||
#[derive(AsRef, Deref, RlpEncodableWrapper, RlpDecodableWrapper)]
|
||||
pub struct Bloom(BLOOM_BYTE_LENGTH);
|
||||
}
|
||||
|
||||
impl_hash_compact!(Bloom);
|
||||
impl_fixed_hash_serde!(Bloom, BLOOM_BYTE_LENGTH);
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
impl PropTestArbitrary for Bloom {
|
||||
type Parameters = ParamsFor<u8>;
|
||||
type Strategy = BoxedStrategy<Bloom>;
|
||||
|
||||
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
|
||||
proptest::collection::vec(any_with::<u8>(args), BLOOM_BYTE_LENGTH)
|
||||
.prop_map(move |vec| Bloom::from_slice(&vec))
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
impl_fixed_hash_type!((Bloom, BLOOM_BYTE_LENGTH));
|
||||
|
||||
// See Section 4.3.1 "Transaction Receipt" of the Yellow Paper
|
||||
fn m3_2048(bloom: &mut Bloom, x: &[u8]) {
|
||||
@ -105,19 +76,4 @@ mod tests {
|
||||
))
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn arbitrary() {
|
||||
proptest::proptest!(|(bloom: Bloom)| {
|
||||
let mut buf = vec![];
|
||||
bloom.to_compact(&mut buf);
|
||||
|
||||
// Add noise
|
||||
buf.push(1);
|
||||
|
||||
let (decoded, remaining_buf) = Bloom::from_compact(&buf, buf.len());
|
||||
|
||||
assert!(bloom == decoded);
|
||||
assert!(remaining_buf.len() == 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
use crate::U256;
|
||||
use ethers_core::types::U64;
|
||||
use reth_codecs::add_arbitrary_tests;
|
||||
use reth_rlp::{Decodable, Encodable};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fmt, str::FromStr};
|
||||
|
||||
/// Either a named or chain id or the actual id value
|
||||
#[add_arbitrary_tests(rlp)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Chain {
|
||||
/// Contains a known chain
|
||||
@ -168,6 +170,43 @@ impl Default for Chain {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
impl<'a> arbitrary::Arbitrary<'a> for Chain {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
if u.ratio(1, 2)? {
|
||||
let chain = u.int_in_range(0..=(ethers_core::types::Chain::COUNT - 1))?;
|
||||
|
||||
return Ok(Chain::Named(ethers_core::types::Chain::iter().nth(chain).expect("in range")))
|
||||
}
|
||||
|
||||
Ok(Self::Id(u64::arbitrary(u)?))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
use strum::{EnumCount, IntoEnumIterator};
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
use proptest::{
|
||||
arbitrary::ParamsFor,
|
||||
prelude::{any, Strategy},
|
||||
sample::Selector,
|
||||
strategy::BoxedStrategy,
|
||||
};
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
impl proptest::arbitrary::Arbitrary for Chain {
|
||||
type Parameters = ParamsFor<u32>;
|
||||
type Strategy = BoxedStrategy<Chain>;
|
||||
|
||||
fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
|
||||
let named = any::<Selector>()
|
||||
.prop_map(move |sel| Chain::Named(sel.select(ethers_core::types::Chain::iter())));
|
||||
let id = any::<u64>().prop_map(Chain::from);
|
||||
proptest::strategy::Union::new_weighted(vec![(50, named.boxed()), (50, id.boxed())]).boxed()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
use crate::{BlockNumber, H256};
|
||||
use crc::crc32;
|
||||
use reth_codecs::derive_arbitrary;
|
||||
use reth_rlp::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
@ -15,6 +16,7 @@ use std::{
|
||||
use thiserror::Error;
|
||||
|
||||
/// `CRC32` hash of all previous forks starting from genesis block.
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
@ -58,6 +60,7 @@ impl Add<BlockNumber> for ForkHash {
|
||||
|
||||
/// A fork identifier as defined by EIP-2124.
|
||||
/// Serves as the chain compatibility identifier.
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
|
||||
@ -5,7 +5,7 @@ use crate::{
|
||||
};
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use ethers_core::types::H64;
|
||||
use reth_codecs::{main_codec, Compact};
|
||||
use reth_codecs::{derive_arbitrary, main_codec, Compact};
|
||||
use reth_rlp::{length_of_length, Decodable, Encodable};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ops::Deref;
|
||||
@ -290,6 +290,7 @@ impl SealedHeader {
|
||||
/// [`HeadersDirection::Falling`] block numbers for `reverse == 1 == true`
|
||||
///
|
||||
/// See also <https://github.com/ethereum/devp2p/blob/master/caps/eth.md#getblockheaders-0x03>
|
||||
#[derive_arbitrary(rlp)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]
|
||||
pub enum HeadersDirection {
|
||||
/// Falling block number.
|
||||
|
||||
@ -220,7 +220,7 @@ impl proptest::prelude::Arbitrary for Bytes {
|
||||
type Strategy = proptest::prelude::BoxedStrategy<Bytes>;
|
||||
|
||||
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
|
||||
proptest::collection::vec(proptest::arbitrary::any_with::<u8>(args), 0..1000)
|
||||
proptest::collection::vec(proptest::arbitrary::any_with::<u8>(args), 0..80)
|
||||
.prop_map(move |vec| bytes::Bytes::from(vec).into())
|
||||
.boxed()
|
||||
}
|
||||
@ -229,7 +229,7 @@ impl proptest::prelude::Arbitrary for Bytes {
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
impl<'a> arbitrary::Arbitrary<'a> for Bytes {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let size = u.int_in_range(0..=1000)?;
|
||||
let size = u.int_in_range(0..=80)?;
|
||||
Ok(Self(bytes::Bytes::copy_from_slice(u.bytes(size)?)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
//! This crate contains Ethereum primitive types and helper functions.
|
||||
|
||||
mod account;
|
||||
mod bits;
|
||||
mod block;
|
||||
pub mod bloom;
|
||||
mod chain;
|
||||
@ -32,6 +33,7 @@ mod transaction;
|
||||
pub mod proofs;
|
||||
|
||||
pub use account::Account;
|
||||
pub use bits::H512;
|
||||
pub use block::{Block, BlockHashOrNumber, SealedBlock};
|
||||
pub use bloom::Bloom;
|
||||
pub use chain::Chain;
|
||||
@ -77,9 +79,9 @@ pub type TransitionId = u64;
|
||||
|
||||
pub use ethers_core::{
|
||||
types as rpc,
|
||||
types::{BigEndianHash, H128, H512, H64, U128, U64},
|
||||
types::{BigEndianHash, H128, H64, U64},
|
||||
};
|
||||
pub use revm_interpreter::{B160 as H160, B256 as H256, U256};
|
||||
pub use revm_interpreter::{ruint::aliases::U128, B160 as H160, B256 as H256, U256};
|
||||
|
||||
#[doc(hidden)]
|
||||
mod __reexport {
|
||||
|
||||
@ -3,7 +3,7 @@ use reth_codecs::{main_codec, Compact};
|
||||
use reth_rlp::{RlpDecodable, RlpEncodable};
|
||||
|
||||
/// Ethereum Log
|
||||
#[main_codec]
|
||||
#[main_codec(rlp)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, RlpDecodable, RlpEncodable, Default)]
|
||||
pub struct Log {
|
||||
/// Contract that emitted this log.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use ethers_core::types::H512;
|
||||
use crate::H512;
|
||||
|
||||
// TODO: should we use `PublicKey` for this? Even when dealing with public keys we should try to
|
||||
// prevent misuse
|
||||
|
||||
@ -112,7 +112,7 @@ impl Encodable for Receipt {
|
||||
if matches!(self.tx_type, TxType::EIP1559 | TxType::EIP2930) {
|
||||
payload_len += 1;
|
||||
// we include a string header for typed receipts, so include the length here
|
||||
payload_len = length_of_length(payload_len);
|
||||
payload_len += length_of_length(payload_len);
|
||||
}
|
||||
|
||||
payload_len
|
||||
|
||||
@ -5,7 +5,7 @@ use reth_rlp::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrap
|
||||
|
||||
/// A list of addresses and storage keys that the transaction plans to access.
|
||||
/// Accesses outside the list are possible, but become more expensive.
|
||||
#[main_codec]
|
||||
#[main_codec(rlp)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, RlpDecodable, RlpEncodable)]
|
||||
pub struct AccessListItem {
|
||||
/// Account addresses that would be loaded at the start of execution
|
||||
@ -15,6 +15,6 @@ pub struct AccessListItem {
|
||||
}
|
||||
|
||||
/// AccessList as defined in EIP-2930
|
||||
#[main_codec]
|
||||
#[main_codec(rlp)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, RlpDecodableWrapper, RlpEncodableWrapper)]
|
||||
pub struct AccessList(pub Vec<AccessListItem>);
|
||||
|
||||
@ -2,7 +2,7 @@ use crate::{keccak256, Address, Bytes, ChainId, TxHash, H256};
|
||||
pub use access_list::{AccessList, AccessListItem};
|
||||
use bytes::{Buf, BytesMut};
|
||||
use derive_more::{AsRef, Deref};
|
||||
use reth_codecs::{main_codec, Compact};
|
||||
use reth_codecs::{add_arbitrary_tests, main_codec, Compact};
|
||||
use reth_rlp::{length_of_length, Decodable, DecodeError, Encodable, Header, EMPTY_STRING_CODE};
|
||||
pub use signature::Signature;
|
||||
pub use tx_type::TxType;
|
||||
@ -187,6 +187,15 @@ impl Transaction {
|
||||
keccak256(&buf)
|
||||
}
|
||||
|
||||
/// Get chain_id.
|
||||
pub fn chain_id(&self) -> Option<&u64> {
|
||||
match self {
|
||||
Transaction::Legacy(TxLegacy { chain_id, .. }) => chain_id.as_ref(),
|
||||
Transaction::Eip2930(TxEip2930 { chain_id, .. }) => Some(chain_id),
|
||||
Transaction::Eip1559(TxEip1559 { chain_id, .. }) => Some(chain_id),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the transaction's chain id to the provided value.
|
||||
pub fn set_chain_id(&mut self, chain_id: u64) {
|
||||
match self {
|
||||
@ -525,7 +534,8 @@ impl Decodable for TransactionKind {
|
||||
}
|
||||
|
||||
/// Signed transaction.
|
||||
#[main_codec]
|
||||
#[main_codec(no_arbitrary)]
|
||||
#[add_arbitrary_tests(rlp, compact)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, AsRef, Deref, Default)]
|
||||
pub struct TransactionSigned {
|
||||
/// Transaction hash
|
||||
@ -538,6 +548,53 @@ pub struct TransactionSigned {
|
||||
pub transaction: Transaction,
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
use proptest::{
|
||||
prelude::{any, Strategy},
|
||||
strategy::BoxedStrategy,
|
||||
};
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
impl proptest::arbitrary::Arbitrary for TransactionSigned {
|
||||
type Parameters = ();
|
||||
type Strategy = BoxedStrategy<TransactionSigned>;
|
||||
|
||||
fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
|
||||
any::<(Transaction, Signature)>()
|
||||
.prop_map(move |(mut transaction, sig)| {
|
||||
if let Some(chain_id) = transaction.chain_id().cloned() {
|
||||
// Otherwise we might overflow when calculating `v` on `recalculate_hash`
|
||||
transaction.set_chain_id(chain_id % (u64::MAX / 2 - 36));
|
||||
}
|
||||
let mut tx =
|
||||
TransactionSigned { hash: Default::default(), signature: sig, transaction };
|
||||
tx.hash = tx.recalculate_hash();
|
||||
tx
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
impl<'a> arbitrary::Arbitrary<'a> for TransactionSigned {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let mut transaction = Transaction::arbitrary(u)?;
|
||||
if let Some(chain_id) = transaction.chain_id().cloned() {
|
||||
// Otherwise we might overflow when calculating `v` on `recalculate_hash`
|
||||
transaction.set_chain_id(chain_id % (u64::MAX / 2 - 36));
|
||||
}
|
||||
|
||||
let mut tx = TransactionSigned {
|
||||
hash: Default::default(),
|
||||
signature: Signature::arbitrary(u)?,
|
||||
transaction,
|
||||
};
|
||||
tx.hash = tx.recalculate_hash();
|
||||
|
||||
Ok(tx)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TransactionSignedEcRecovered> for TransactionSigned {
|
||||
fn from(recovered: TransactionSignedEcRecovered) -> Self {
|
||||
recovered.signed_transaction
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use reth_codecs::{derive_compact_arbitrary, Compact};
|
||||
use reth_codecs::{derive_arbitrary, Compact};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Transaction Type
|
||||
#[derive_compact_arbitrary]
|
||||
#[derive_arbitrary(compact)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize)]
|
||||
pub enum TxType {
|
||||
/// Legacy transaction pre EIP-2929
|
||||
|
||||
73
crates/storage/codecs/derive/src/arbitrary.rs
Normal file
73
crates/storage/codecs/derive/src/arbitrary.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use proc_macro::{self, TokenStream};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::DeriveInput;
|
||||
|
||||
/// If `compact` or `rlp` is passed to `derive_arbitrary`, this function will generate the
|
||||
/// corresponding proptest roundtrip tests.
|
||||
///
|
||||
/// It accepts an optional integer number for the number of proptest cases. Otherwise, it will set
|
||||
/// it at 1000.
|
||||
pub fn maybe_generate_tests(args: TokenStream, ast: &DeriveInput) -> TokenStream2 {
|
||||
let type_ident = ast.ident.clone();
|
||||
|
||||
// Same as proptest
|
||||
let mut default_cases = 256;
|
||||
|
||||
let mut traits = vec![];
|
||||
let mut roundtrips = vec![];
|
||||
|
||||
for arg in args {
|
||||
if arg.to_string() == "compact" {
|
||||
traits.push(quote! { use super::Compact; });
|
||||
roundtrips.push(quote! {
|
||||
{
|
||||
let mut buf = vec![];
|
||||
|
||||
let len = field.clone().to_compact(&mut buf);
|
||||
let (decoded, _) = super::#type_ident::from_compact(&buf, len);
|
||||
|
||||
assert!(field == decoded);
|
||||
}
|
||||
});
|
||||
} else if arg.to_string() == "rlp" {
|
||||
traits.push(quote! { use reth_rlp::{Encodable, Decodable}; });
|
||||
roundtrips.push(quote! {
|
||||
{
|
||||
let mut buf = vec![];
|
||||
|
||||
let len = field.clone().encode(&mut buf);
|
||||
let decoded = super::#type_ident::decode(&mut buf.as_slice()).unwrap();
|
||||
|
||||
assert!(field == decoded);
|
||||
}
|
||||
});
|
||||
} else if let Ok(num) = arg.to_string().parse() {
|
||||
default_cases = num;
|
||||
}
|
||||
}
|
||||
|
||||
let mut tests = TokenStream2::default();
|
||||
if !roundtrips.is_empty() {
|
||||
let mod_tests = format_ident!("{}Tests", ast.ident);
|
||||
|
||||
tests = quote! {
|
||||
#[allow(non_snake_case)]
|
||||
#[cfg(test)]
|
||||
mod #mod_tests {
|
||||
#(#traits)*
|
||||
|
||||
#[test]
|
||||
fn proptest() {
|
||||
let mut config = proptest::prelude::ProptestConfig::with_cases(#default_cases as u32);
|
||||
|
||||
proptest::proptest!(config, |(field: super::#type_ident)| {
|
||||
#(#roundtrips)*
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tests
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
use proc_macro::{self, TokenStream};
|
||||
use proc_macro::{self, TokenStream, TokenTree};
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
|
||||
mod arbitrary;
|
||||
mod compact;
|
||||
|
||||
#[proc_macro_derive(Compact, attributes(maybe_zero))]
|
||||
@ -9,6 +10,11 @@ pub fn derive(input: TokenStream) -> TokenStream {
|
||||
compact::derive(input)
|
||||
}
|
||||
|
||||
/// Implements the main codec. If the codec supports it, it will call `derive_arbitrary(..)`.
|
||||
///
|
||||
/// Example usage:
|
||||
/// * `#[main_codec(rlp)]`: will implement `derive_arbitrary(rlp)` or `derive_arbitrary(compact, rlp)`, if `compact` is the `main_codec`.
|
||||
/// * `#[main_codec(no_arbitrary)]`: will skip `derive_arbitrary`
|
||||
#[proc_macro_attribute]
|
||||
#[rustfmt::skip]
|
||||
#[allow(unreachable_code)]
|
||||
@ -70,28 +76,48 @@ pub fn use_postcard(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn use_compact(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
pub fn use_compact(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
derive_compact_arbitrary(
|
||||
_args,
|
||||
quote! {
|
||||
let compact = quote! {
|
||||
#[derive(Compact, serde::Serialize, serde::Deserialize)]
|
||||
#ast
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
.into();
|
||||
|
||||
if let Some(first_arg) = args.clone().into_iter().next() {
|
||||
if first_arg.to_string() == "no_arbitrary" {
|
||||
return compact
|
||||
}
|
||||
}
|
||||
|
||||
let mut args = args.into_iter().collect::<Vec<_>>();
|
||||
args.push(TokenTree::Ident(proc_macro::Ident::new("compact", proc_macro::Span::call_site())));
|
||||
|
||||
derive_arbitrary(TokenStream::from_iter(args.into_iter()), compact)
|
||||
}
|
||||
|
||||
/// Adds `Arbitrary` and `proptest::Arbitrary` imports into scope and derives the struct/enum.
|
||||
///
|
||||
/// If `compact` or `rlp` is passed to `derive_arbitrary`, there will be proptest roundtrip tests
|
||||
/// generated. An integer value passed will limit the number of proptest cases generated (default:
|
||||
/// 256).
|
||||
///
|
||||
/// Examples:
|
||||
/// * `#[derive_arbitrary]`: will derive arbitrary with no tests.
|
||||
/// * `#[derive_arbitrary(rlp)]`: will derive arbitrary and generate rlp roundtrip proptests.
|
||||
/// * `#[derive_arbitrary(rlp, 10)]`: will derive arbitrary and generate rlp roundtrip proptests.
|
||||
/// Limited to 10 cases.
|
||||
/// * `#[derive_arbitrary(compact, rlp)]`. will derive arbitrary and generate rlp and compact
|
||||
/// roundtrip proptests.
|
||||
#[proc_macro_attribute]
|
||||
pub fn derive_compact_arbitrary(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
pub fn derive_arbitrary(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let tests = arbitrary::maybe_generate_tests(args, &ast);
|
||||
|
||||
// Avoid duplicate names
|
||||
let prop_import = format_ident!("{}PropTestArbitratry", ast.ident);
|
||||
let arb_import = format_ident!("{}Arbitratry", ast.ident);
|
||||
let mod_tests = format_ident!("{}Tests", ast.ident);
|
||||
let type_ident = ast.ident.clone();
|
||||
|
||||
quote! {
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
@ -103,23 +129,19 @@ pub fn derive_compact_arbitrary(_args: TokenStream, input: TokenStream) -> Token
|
||||
#[cfg_attr(any(test, feature = "arbitrary"), derive(#prop_import, #arb_import))]
|
||||
#ast
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[cfg(test)]
|
||||
mod #mod_tests {
|
||||
use super::Compact;
|
||||
|
||||
#[test]
|
||||
fn proptest() {
|
||||
proptest::proptest!(|(field: super::#type_ident)| {
|
||||
let mut buf = vec![];
|
||||
|
||||
let len = field.clone().to_compact(&mut buf);
|
||||
let (decoded, _) = super::#type_ident::from_compact(&buf, len);
|
||||
|
||||
assert!(field == decoded);
|
||||
});
|
||||
}
|
||||
#tests
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// To be used for types that implement `Arbitrary` manually. See [`derive_arbitrary`] for more.
|
||||
#[proc_macro_attribute]
|
||||
pub fn add_arbitrary_tests(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let tests = arbitrary::maybe_generate_tests(args, &ast);
|
||||
quote! {
|
||||
#ast
|
||||
#tests
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ proptest-derive = { version = "0.3", optional = true }
|
||||
[dev-dependencies]
|
||||
# reth libs with arbitrary
|
||||
reth-primitives = { path = "../../primitives", features = ["arbitrary"]}
|
||||
reth-codecs = { path = "../codecs",features = ["arbitrary"] }
|
||||
reth-codecs = { path = "../codecs", features = ["arbitrary"] }
|
||||
reth-interfaces = { path = "../../interfaces", features = ["bench"] }
|
||||
|
||||
tempfile = "3.3.0"
|
||||
|
||||
Reference in New Issue
Block a user