Skip to content

Commit

Permalink
Derive Arbitrary impls for a bunch of chain and network types (#2179)
Browse files Browse the repository at this point in the history
Enable proptests for internal and external network protocol messages,
using times with the correct protocol-specific ranges. (4 or 8 bytes.)
  • Loading branch information
teor2345 authored May 24, 2021
1 parent 6797f41 commit f0549b2
Show file tree
Hide file tree
Showing 10 changed files with 73 additions and 7 deletions.
9 changes: 4 additions & 5 deletions zebra-chain/src/block/arbitrary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ use proptest::{
prelude::*,
};

use chrono::{TimeZone, Utc};
use std::sync::Arc;

use crate::{
parameters::{Network, NetworkUpgrade},
serialization,
work::{difficulty::CompactDifficulty, equihash},
};

Expand Down Expand Up @@ -186,8 +186,7 @@ impl Arbitrary for Header {
any::<Hash>(),
any::<merkle::Root>(),
any::<[u8; 32]>(),
// time is interpreted as u32 in the spec, but rust timestamps are i64
(0i64..(u32::MAX as i64)),
serialization::arbitrary::datetime_u32(),
any::<CompactDifficulty>(),
any::<[u8; 32]>(),
any::<equihash::Solution>(),
Expand All @@ -198,7 +197,7 @@ impl Arbitrary for Header {
previous_block_hash,
merkle_root,
commitment_bytes,
timestamp,
time,
difficulty_threshold,
nonce,
solution,
Expand All @@ -207,7 +206,7 @@ impl Arbitrary for Header {
previous_block_hash,
merkle_root,
commitment_bytes,
time: Utc.timestamp(timestamp, 0),
time,
difficulty_threshold,
nonce,
solution,
Expand Down
4 changes: 4 additions & 0 deletions zebra-chain/src/block/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ use crate::{

use super::{merkle, Hash, Height};

#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;

/// A block header, containing metadata about a block.
///
/// How are blocks chained together? They are chained together via the
Expand Down Expand Up @@ -121,6 +124,7 @@ impl Header {
///
/// This structure is used in the Bitcoin network protocol.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub struct CountedHeader {
pub header: Header,
pub transaction_count: usize,
Expand Down
3 changes: 3 additions & 0 deletions zebra-chain/src/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ pub(crate) mod serde_helpers;

pub mod sha256d;

#[cfg(any(test, feature = "proptest-impl"))]
pub mod arbitrary;

pub use constraint::AtLeastOne;
pub use error::SerializationError;
pub use read_zcash::ReadZcashExt;
Expand Down
33 changes: 33 additions & 0 deletions zebra-chain/src/serialization/arbitrary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use chrono::{TimeZone, Utc, MAX_DATETIME, MIN_DATETIME};
use proptest::{arbitrary::any, prelude::*};

/// Returns a strategy that produces an arbitrary [`chrono::DateTime<Utc>`],
/// based on the full valid range of the type.
///
/// Both the seconds and nanoseconds values are randomised. But leap nanoseconds
/// are never present.
/// https://docs.rs/chrono/0.4.19/chrono/struct.DateTime.html#method.timestamp_subsec_nanos
///
/// Zebra uses these times internally, via [`Utc::now`].
///
/// Some parts of the Zcash network protocol ([`Version`] messages) also use times
/// with an 8-byte seconds value. Unlike this function, they have zero
/// nanoseconds values.
pub fn datetime_full() -> impl Strategy<Value = chrono::DateTime<Utc>> {
(
MIN_DATETIME.timestamp()..=MAX_DATETIME.timestamp(),
0..1_000_000_000_u32,
)
.prop_map(|(secs, nsecs)| Utc.timestamp(secs, nsecs))
}

/// Returns a strategy that produces an arbitrary time from a [`u32`] number
/// of seconds past the epoch.
///
/// The nanoseconds value is always zero.
///
/// The Zcash protocol typically uses 4-byte seconds values, except for the
/// [`Version`] message.
pub fn datetime_u32() -> impl Strategy<Value = chrono::DateTime<Utc>> {
any::<u32>().prop_map(|secs| Utc.timestamp(secs.into(), 0))
}
2 changes: 1 addition & 1 deletion zebra-chain/src/sprout/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ impl From<Root> for [u8; 32] {

/// Sprout Note Commitment Tree
#[derive(Clone, Debug, Default, Eq, PartialEq)]
#[cfg_attr(test, derive(Arbitrary))]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
struct NoteCommitmentTree {
/// The root node of the tree (often used as an anchor).
root: Root,
Expand Down
1 change: 1 addition & 0 deletions zebra-network/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ proptest = "0.10"
proptest-derive = "0.3"
toml = "0.5"

zebra-chain = { path = "../zebra-chain", features = ["proptest-impl"] }
zebra-test = { path = "../zebra-test/" }
13 changes: 13 additions & 0 deletions zebra-network/src/protocol/external/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ use super::inv::InventoryHash;
use super::types::*;
use crate::meta_addr::MetaAddr;

#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
#[cfg(any(test, feature = "proptest-impl"))]
use zebra_chain::serialization::arbitrary::datetime_full;

/// A Bitcoin-like network message for the Zcash protocol.
///
/// The Zcash network protocol is mostly inherited from Bitcoin, and a list of
Expand All @@ -31,6 +36,7 @@ use crate::meta_addr::MetaAddr;
///
/// [btc_wiki_protocol]: https://en.bitcoin.it/wiki/Protocol_documentation
#[derive(Clone, Eq, PartialEq, Debug)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub enum Message {
/// A `version` message.
///
Expand All @@ -47,6 +53,12 @@ pub enum Message {
services: PeerServices,

/// The time when the version message was sent.
///
/// This is a 64-bit field. Zebra rejects out-of-range times as invalid.
#[cfg_attr(
any(test, feature = "proptest-impl"),
proptest(strategy = "datetime_full()")
)]
timestamp: DateTime<Utc>,

/// The network address of the node receiving this message, and its
Expand Down Expand Up @@ -307,6 +319,7 @@ where
///
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#reject)
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
#[repr(u8)]
#[allow(missing_docs)]
pub enum RejectReason {
Expand Down
6 changes: 5 additions & 1 deletion zebra-network/src/protocol/external/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use proptest_derive::Arbitrary;

/// A magic number identifying the network.
#[derive(Copy, Clone, Eq, PartialEq)]
#[cfg_attr(test, derive(Arbitrary))]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub struct Magic(pub [u8; 4]);

impl fmt::Debug for Magic {
Expand All @@ -38,6 +38,7 @@ impl From<Network> for Magic {

/// A protocol version number.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub struct Version(pub u32);

impl Version {
Expand Down Expand Up @@ -89,6 +90,7 @@ bitflags! {

/// A nonce used in the networking layer to identify messages.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub struct Nonce(pub u64);

impl Default for Nonce {
Expand All @@ -100,6 +102,7 @@ impl Default for Nonce {

/// A random value to add to the seed value in a hash function.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub struct Tweak(pub u32);

impl Default for Tweak {
Expand All @@ -112,6 +115,7 @@ impl Default for Tweak {
/// A Bloom filter consisting of a bit field of arbitrary byte-aligned
/// size, maximum size is 36,000 bytes.
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub struct Filter(pub Vec<u8>);

#[cfg(test)]
Expand Down
4 changes: 4 additions & 0 deletions zebra-network/src/protocol/internal/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ use zebra_chain::{

use super::super::types::Nonce;

#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;

/// A network request, represented in internal format.
///
/// The network layer aims to abstract away the details of the Bitcoin wire
Expand All @@ -27,6 +30,7 @@ use super::super::types::Nonce;
/// a best-effort attempt to ignore any messages responsive to the cancelled
/// request, subject to limitations in the underlying Zcash protocol.
#[derive(Clone, Debug)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub enum Request {
/// Requests additional peers from the server.
///
Expand Down
5 changes: 5 additions & 0 deletions zebra-network/src/protocol/internal/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ use zebra_chain::{
};

use crate::meta_addr::MetaAddr;

use std::sync::Arc;

#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;

/// A response to a network request, represented in internal format.
#[derive(Clone, Debug)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub enum Response {
/// Do not send any response to this request.
///
Expand Down

0 comments on commit f0549b2

Please sign in to comment.