Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ members = [
"anchor/common/version",
"anchor/database",
"anchor/eth",
"anchor/fuzz",
"anchor/http_api",
"anchor/http_metrics",
"anchor/keygen",
"anchor/keysplit",
"anchor/logging",
"anchor/message_receiver",
"anchor/message_sender",
"anchor/message_validator",
Expand All @@ -26,6 +26,7 @@ members = [
"anchor/subnet_tracker",
"anchor/validator_store",
]

resolver = "2"

[workspace.package]
Expand Down Expand Up @@ -86,6 +87,7 @@ alloy = { version = "0.12.0", features = [
"rpc-types",
"rlp",
] }
arbitrary = "1.4.1"
async-channel = "1.9"
axum = "0.8.1"
base64 = "0.22.1"
Expand Down
4 changes: 4 additions & 0 deletions anchor/common/qbft/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ version = "0.1.0"
authors = ["Sigma Prime <contact@sigmaprime.io"]
edition = { workspace = true }

[features]
arbitrary-fuzz = ["arbitrary"]

[dependencies]
arbitrary = { workspace = true, optional = true }
derive_more = { workspace = true }
ethereum_ssz = { workspace = true }
ethereum_ssz_derive = { workspace = true }
Expand Down
4 changes: 4 additions & 0 deletions anchor/common/ssv_types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ version = "0.1.0"
edition = { workspace = true }
authors = ["Sigma Prime <contact@sigmaprime.io>"]

[features]
arbitrary-fuzz = ["arbitrary"]

[dependencies]
arbitrary = { workspace = true, optional = true }
base64 = { workspace = true }
derive_more = { workspace = true }
ethereum_ssz = { workspace = true }
Expand Down
3 changes: 3 additions & 0 deletions anchor/common/ssv_types/src/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub struct UnsignedSSVMessage {

/// A QBFT specific message
#[derive(Clone, Encode, Decode)]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
pub struct QbftMessage {
pub qbft_message_type: QbftMessageType,
pub height: u64,
Expand Down Expand Up @@ -82,6 +83,7 @@ impl Debug for QbftMessage {

/// Different states the QBFT Message may represent
#[derive(Clone, Debug, PartialEq, PartialOrd, Copy)]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
pub enum QbftMessageType {
Proposal = 0,
Prepare,
Expand Down Expand Up @@ -304,6 +306,7 @@ pub struct Contribution<E: EthSpec> {
}

#[derive(Clone, Debug, TreeHash, PartialEq, Eq, Encode, Decode)]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
pub struct BeaconVote {
pub block_root: Hash256,
pub source: Checkpoint,
Expand Down
38 changes: 38 additions & 0 deletions anchor/common/ssv_types/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const MAX_ENCODED_PARTIAL_SIGNATURE_SIZE: usize = MAX_PARTIAL_SIGNATURE_MSGS_SIZ

/// Defines the types of messages with explicit discriminant values.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[repr(u64)]
pub enum MsgType {
SSVConsensusMsgType = 0,
Expand Down Expand Up @@ -152,6 +153,7 @@ pub enum SSVMessageError {

/// Represents a bare SSVMessage with a type, ID, and data.
#[derive(Encode, Decode, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
pub struct SSVMessage {
msg_type: MsgType,
msg_id: MessageId, // Fixed-size [u8; 56]
Expand Down Expand Up @@ -292,6 +294,42 @@ pub struct SignedSSVMessage {
full_data: Vec<u8>, // Variable-length byte array, max 4,194,532 bytes
}

#[cfg(feature = "arbitrary-fuzz")]
use arbitrary::{Arbitrary, Result, Unstructured};

#[cfg(feature = "arbitrary-fuzz")]
use crate::consensus::{BeaconVote, QbftMessage};

#[cfg(feature = "arbitrary-fuzz")]
impl<'a> Arbitrary<'a> for SignedSSVMessage {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
// Generate arbitrary BeaconVote
let beacon_vote = BeaconVote::arbitrary(u)?;

// Generate arbitrary QbftMessage
let qbft_message = QbftMessage::arbitrary(u)?;

// Create arbitrary basic fields
let signatures = Vec::<Vec<u8>>::arbitrary(u)?;
let operator_ids = Vec::<OperatorId>::arbitrary(u)?;

// Create SSV message with serialized QbftMessage
let ssv_message = SSVMessage {
msg_type: MsgType::arbitrary(u)?,
msg_id: MessageId::arbitrary(u)?,
data: qbft_message.as_ssz_bytes(), // Serialize QbftMessage to bytes
};

// Create the SignedSSVMessage with serialized BeaconVote
Ok(SignedSSVMessage {
signatures,
operator_ids,
ssv_message,
full_data: beacon_vote.as_ssz_bytes(), // Serialize BeaconVote to bytes
})
}
}

impl Debug for SignedSSVMessage {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let signatures = self.signatures.iter().map(hex::encode).collect::<Vec<_>>();
Expand Down
1 change: 1 addition & 0 deletions anchor/common/ssv_types/src/msgid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ pub enum DutyExecutor {
}

#[derive(Clone, Hash, Eq, PartialEq, From, Into)]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
pub struct MessageId([u8; 56]);

impl Debug for MessageId {
Expand Down
1 change: 1 addition & 0 deletions anchor/common/ssv_types/src/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::util::parse_rsa;
Clone, Copy, Debug, Default, Eq, PartialEq, Hash, From, Deref, Encode, Decode, Ord, PartialOrd,
)]
#[ssz(struct_behaviour = "transparent")]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
pub struct OperatorId(pub u64);

/// Client responsible for maintaining the overall health of the network.
Expand Down
1 change: 1 addition & 0 deletions anchor/common/ssv_types/src/partial_sig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use types::{Hash256, Signature, Slot};
use crate::{OperatorId, ValidatorIndex};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
pub enum PartialSignatureKind {
// PostConsensusPartialSig is a partial signature over a decided duty (attestation data,
// block, etc)
Expand Down
5 changes: 5 additions & 0 deletions anchor/fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
target
corpus
artifacts
coverage
keysplit*
72 changes: 72 additions & 0 deletions anchor/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
[package]
name = "anchor-fuzz"
version = "0.0.0"
publish = false
edition = "2021"

[package.metadata]
cargo-fuzz = true

[features]
arbitrary-fuzz = ["ssv_types/arbitrary-fuzz"]

[dependencies]
arbitrary = { workspace = true }
async-channel = { workspace = true }
bls = { workspace = true }
dashmap = { workspace = true }
database = { workspace = true }
ethereum_ssz = { workspace = true }
futures = { workspace = true }
gossipsub = { workspace = true }
hex = { workspace = true }
indexmap = { workspace = true }
libfuzzer-sys = "0.4"
libp2p = { workspace = true }
message_receiver = { workspace = true }
message_sender = { workspace = true }
message_validator = { workspace = true }
openssl = { workspace = true }
parking_lot = { workspace = true }
processor = { workspace = true }
qbft = { workspace = true, features = ["arbitrary-fuzz"] }
qbft_manager = { workspace = true }
sha2 = { workspace = true }
signature_collector = { workspace = true }
slot_clock = { workspace = true }
ssv_types = { workspace = true, features = ["arbitrary-fuzz"] }
subnet_tracker = { workspace = true }
task_executor = { workspace = true }
tempfile = "3.14.0"
thiserror = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = true }
types = { workspace = true }

[[bin]]
name = "validate_target"
path = "fuzz_targets/validate_target.rs"
test = false
doc = false
bench = false

[[bin]]
name = "receive_target"
path = "fuzz_targets/receive_target.rs"
test = false
doc = false
bench = false

[[bin]]
name = "qbft_target"
path = "fuzz_targets/qbft_target.rs"
test = false
doc = false
bench = false

[[bin]]
name = "custom_ssz"
path = "fuzz_targets/custom_ssz.rs"
test = false
doc = false
bench = false
50 changes: 50 additions & 0 deletions anchor/fuzz/fuzz_targets/custom_ssz.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#![no_main]

use arbitrary::Arbitrary;
use libfuzzer_sys::fuzz_target;
use ssv_types::{
consensus::QbftMessageType, message::MsgType, msgid::MessageId,
partial_sig::PartialSignatureKind,
};
use ssz::{Decode, Encode};

#[derive(Arbitrary, Debug)]
enum SszTarget {
MessageId(MessageId),
MsgType(MsgType),
QbftMessageType(QbftMessageType),
PartialSignatureKind(PartialSignatureKind),
}

fuzz_target!(|target: SszTarget| {
match target {
SszTarget::MessageId(message_id) => {
let encoded = message_id.as_ssz_bytes();
match MessageId::from_ssz_bytes(&encoded) {
Ok(decoded) => assert_eq!(message_id, decoded),
Err(e) => panic!("Failed to decode MessageId: {:?}", e),
}
}
SszTarget::MsgType(msg_type) => {
let encoded = msg_type.as_ssz_bytes();
match MsgType::from_ssz_bytes(&encoded) {
Ok(decoded) => assert_eq!(msg_type, decoded),
Err(e) => panic!("Failed to decode MsgType: {:?}", e),
}
}
SszTarget::QbftMessageType(qbft_msg_type) => {
let encoded = qbft_msg_type.as_ssz_bytes();
match QbftMessageType::from_ssz_bytes(&encoded) {
Ok(decoded) => assert_eq!(qbft_msg_type, decoded),
Err(e) => panic!("Failed to decode QbftMessageType: {:?}", e),
}
}
SszTarget::PartialSignatureKind(partial_sig_kind) => {
let encoded = partial_sig_kind.as_ssz_bytes();
match PartialSignatureKind::from_ssz_bytes(&encoded) {
Ok(decoded) => assert_eq!(partial_sig_kind, decoded),
Err(e) => panic!("Failed to decode PartialSignatureKind: {:?}", e),
}
}
}
});
Loading
Loading