Skip to content

Commit 445c16e

Browse files
committed
justification parsing
1 parent 72a182f commit 445c16e

File tree

8 files changed

+159
-90
lines changed

8 files changed

+159
-90
lines changed

Diff for: Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: anchor/common/qbft/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,7 @@ where
885885
round_change_justification,
886886
prepare_justification,
887887
};
888+
println!("{:?}", qbft_message);
888889

889890
let ssv_message = SSVMessage::new(
890891
MsgType::SSVConsensusMsgType,

Diff for: anchor/common/ssv_types/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ indexmap = { workspace = true }
1414
openssl = { workspace = true }
1515
rusqlite = { workspace = true }
1616
serde = { workspace = true }
17+
serde_json = { workspace = true }
1718
sha2 = { workspace = true }
1819
thiserror = { workspace = true }
1920
tree_hash = { workspace = true }

Diff for: anchor/common/ssv_types/src/message.rs

+75-17
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ use std::{
33
fmt::{Debug, Formatter},
44
};
55

6+
use base64::prelude::*;
7+
use serde::{de::Error, Deserialize, Deserializer};
8+
use serde_json::Value;
69
use ssz::{Decode, DecodeError, Encode};
710
use ssz_derive::{Decode, Encode};
811
use thiserror::Error;
@@ -76,6 +79,23 @@ pub enum MsgType {
7679
SSVPartialSignatureMsgType = 1,
7780
}
7881

82+
impl<'de> Deserialize<'de> for MsgType {
83+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
84+
where
85+
D: Deserializer<'de>,
86+
{
87+
let value = u64::deserialize(deserializer)?;
88+
match value {
89+
0 => Ok(MsgType::SSVConsensusMsgType),
90+
1 => Ok(MsgType::SSVPartialSignatureMsgType),
91+
_ => Err(serde::de::Error::custom(format!(
92+
"Invalid MsgType value: {}",
93+
value
94+
))),
95+
}
96+
}
97+
}
98+
7999
impl TreeHash for MsgType {
80100
fn tree_hash_type() -> TreeHashType {
81101
TreeHashType::Basic
@@ -173,11 +193,17 @@ pub enum SSVMessageError {
173193
}
174194

175195
/// Represents a bare SSVMessage with a type, ID, and data.
176-
#[derive(Encode, Decode, Clone, PartialEq, Eq)]
196+
#[derive(Encode, Decode, Clone, PartialEq, Eq, Deserialize)]
177197
pub struct SSVMessage {
198+
#[serde(rename = "MsgType")]
178199
msg_type: MsgType,
179-
msg_id: MessageId, // Fixed-size [u8; 56]
180-
data: Vec<u8>, // Variable-length byte array
200+
201+
#[serde(rename = "MsgID")]
202+
msg_id: MessageId,
203+
204+
#[serde(rename = "Data")]
205+
#[serde(deserialize_with = "deserialize_base64_or_empty")]
206+
data: Vec<u8>,
181207
}
182208

183209
impl TreeHash for SSVMessage {
@@ -202,23 +228,16 @@ impl TreeHash for SSVMessage {
202228
hasher
203229
.write(self.msg_id.tree_hash_root().as_slice())
204230
.unwrap();
205-
// Field 2: Data - variable-length byte array
206-
// First get the data index
207-
// Calculate chunks needed ((max_size + 31) / 32)
231+
208232
let chunks_needed = (722412 + 31) / 32;
209233

210-
// Merkleize the data with the calculated chunk count
211234
let data_root = merkle_root(&self.data, chunks_needed);
212235

213-
// Mix in the length - this is equivalent to MerkleizeWithMixin in Go
214236
let data_with_length = mix_in_length(&data_root, self.data.len());
215237

216238
// Add hashed data to the main tree
217-
hasher
218-
.write(data_with_length.as_slice())
219-
.expect("Failed to write data");
239+
hasher.write(data_with_length.as_slice()).unwrap();
220240

221-
// Finalize and return the root
222241
hasher.finish().expect("Failed to finish hashing")
223242
}
224243
}
@@ -347,12 +366,51 @@ pub enum SignedSSVMessageError {
347366

348367
/// Represents a signed SSV Message with signatures, operator IDs, the message itself, and full
349368
/// data.
350-
#[derive(Encode, Decode, Clone, PartialEq, Eq)]
369+
#[derive(Encode, Decode, Clone, PartialEq, Eq, Deserialize)]
351370
pub struct SignedSSVMessage {
352-
signatures: Vec<Vec<u8>>, // Vec of Vec<u8>, max 13 elements, each with 256 bytes
353-
operator_ids: Vec<OperatorId>, // Vec of OperatorID (u64), max 13 elements
354-
ssv_message: SSVMessage, // SSVMessage: Required field
355-
full_data: Vec<u8>, // Variable-length byte array, max 4,194,532 bytes
371+
#[serde(rename = "Signatures")]
372+
#[serde(deserialize_with = "deserialize_base64_vec")]
373+
signatures: Vec<Vec<u8>>,
374+
375+
#[serde(rename = "OperatorIDs")]
376+
operator_ids: Vec<OperatorId>,
377+
378+
#[serde(rename = "SSVMessage")]
379+
ssv_message: SSVMessage,
380+
381+
#[serde(rename = "FullData")]
382+
#[serde(deserialize_with = "deserialize_base64_or_empty")]
383+
full_data: Vec<u8>,
384+
}
385+
386+
fn deserialize_base64_or_empty<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
387+
where
388+
D: serde::Deserializer<'de>,
389+
{
390+
let value = Value::deserialize(deserializer)?;
391+
392+
match value {
393+
Value::Null => Ok(Vec::new()), // Return empty Vec for null values
394+
Value::String(s) => BASE64_STANDARD
395+
.decode(s.as_bytes())
396+
.map_err(D::Error::custom),
397+
_ => Err(D::Error::custom("Expected null or a base64 string")),
398+
}
399+
}
400+
401+
fn deserialize_base64_vec<'de, D>(deserializer: D) -> Result<Vec<Vec<u8>>, D::Error>
402+
where
403+
D: serde::Deserializer<'de>,
404+
{
405+
let string_vec: Vec<String> = serde::Deserialize::deserialize(deserializer)?;
406+
string_vec
407+
.into_iter()
408+
.map(|s| {
409+
BASE64_STANDARD
410+
.decode(s.as_bytes())
411+
.map_err(serde::de::Error::custom)
412+
})
413+
.collect()
356414
}
357415

358416
impl TreeHash for SignedSSVMessage {

Diff for: anchor/common/ssv_types/src/msgid.rs

+28
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::fmt::{Debug, Formatter};
22

33
use derive_more::{Display, From, Into};
4+
use serde::{Deserialize, Deserializer};
45
use ssz::{Decode, DecodeError, Encode};
56
use tree_hash::{PackedEncoding, TreeHash, TreeHashType};
67
use types::{typenum::U56, PublicKeyBytes, VariableList};
@@ -85,6 +86,23 @@ impl TreeHash for MessageId {
8586
}
8687
}
8788

89+
impl<'de> Deserialize<'de> for MessageId {
90+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
91+
where
92+
D: Deserializer<'de>,
93+
{
94+
// First deserialize as a Vec<u8>
95+
let vec = Vec::<u8>::deserialize(deserializer)?;
96+
97+
// Then try to convert to [u8; 56]
98+
vec.try_into()
99+
.map(MessageId)
100+
.map_err(|_| serde::de::Error::custom("Expected array of 56 bytes".to_string()))
101+
}
102+
}
103+
104+
// Implement custom deserialization for MessageId
105+
88106
impl Debug for MessageId {
89107
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
90108
write!(f, "{}", hex::encode(self.0))
@@ -108,6 +126,16 @@ impl MessageId {
108126
MessageId(id)
109127
}
110128

129+
// todo!() remove or gate this
130+
pub fn for_spectest() -> Self {
131+
let mut id = [0; 56];
132+
id[0] = 1;
133+
id[1] = 2;
134+
id[2] = 3;
135+
id[6] = 4;
136+
MessageId(id)
137+
}
138+
111139
pub fn domain(&self) -> DomainType {
112140
DomainType(
113141
self.0[0..4]

Diff for: anchor/common/ssv_types/src/operator.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,28 @@ use std::{cmp::Eq, fmt::Debug, hash::Hash};
22

33
use derive_more::{Deref, From};
44
use openssl::{pkey::Public, rsa::Rsa};
5+
use serde::Deserialize;
56
use ssz_derive::{Decode, Encode};
67
use types::Address;
78

89
use crate::util::parse_rsa;
910

1011
/// Unique identifier for an Operator.
1112
#[derive(
12-
Clone, Copy, Debug, Default, Eq, PartialEq, Hash, From, Deref, Encode, Decode, Ord, PartialOrd,
13+
Clone,
14+
Copy,
15+
Debug,
16+
Default,
17+
Eq,
18+
PartialEq,
19+
Hash,
20+
From,
21+
Deref,
22+
Encode,
23+
Decode,
24+
Ord,
25+
PartialOrd,
26+
Deserialize,
1327
)]
1428
#[ssz(struct_behaviour = "transparent")]
1529
pub struct OperatorId(pub u64);

Diff for: anchor/spec_tests/src/qbft/create_message.rs

+28-28
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use openssl::pkey::{PKey, Private};
22
use serde::Deserialize;
3-
use ssv_types::{consensus::QbftMessageType, IndexSet, OperatorId, Round};
3+
use ssv_types::{consensus::QbftMessageType, msgid::MessageId, IndexSet, OperatorId, Round};
44
use types::Hash256;
55

66
use super::{qbft_deserializers::*, SpecQbft};
7-
use crate::{utils::TestKeySet, QbftSpecTestType, SpecTest, SpecTestType};
7+
use crate::{qbft::SignedSSVMessage, utils::TestKeySet, QbftSpecTestType, SpecTest, SpecTestType};
88

99
impl SpecTest for CreateMessageTest {
1010
fn name(&self) -> &str {
@@ -15,14 +15,33 @@ impl SpecTest for CreateMessageTest {
1515
fn run(&self) -> bool {
1616
let spec_qbft = self.spec_qbft.as_ref().expect("Setup has been called");
1717
let key = self.signing_key.as_ref().expect("Setup has been called");
18+
let prepare_justifications = if let Some(prepare) = &self.prepare_justifications {
19+
prepare.clone()
20+
} else {
21+
Vec::new()
22+
};
23+
24+
let round_change_justifications =
25+
if let Some(round_change) = &self.round_change_justifications {
26+
round_change.clone()
27+
} else {
28+
Vec::new()
29+
};
1830

1931
// Create a new unsigned message. Have to create a new unsigned message to be received on
2032
// the queue and then perform signing
21-
let unsigned_message = spec_qbft.create_message(self.create_type, self.root);
33+
let unsigned_message = spec_qbft.create_message(
34+
self.create_type,
35+
self.root,
36+
round_change_justifications,
37+
prepare_justifications,
38+
);
2239
let signed_message = spec_qbft.sign(unsigned_message, key);
2340

2441
// Compute the merkle root of the message and compare it to the expected_root
2542
spec_qbft.verify_root(signed_message, self.expected_root)
43+
44+
// If there are justifications, verify those.. todo!()
2645
}
2746

2847
// Setup the qbft instance for constructing a new message
@@ -31,6 +50,9 @@ impl SpecTest for CreateMessageTest {
3150
let committee: IndexSet<OperatorId> =
3251
four_share_set.operator_keys.keys().cloned().collect();
3352

53+
// All test identifiers are [1,2,3,4]
54+
let identifier = MessageId::for_spectest();
55+
3456
// All message creation testing code uses operator one as the message signer
3557
let operator_one_private = four_share_set
3658
.operator_keys
@@ -39,7 +61,7 @@ impl SpecTest for CreateMessageTest {
3961
let operator_one_private =
4062
PKey::from_rsa(operator_one_private.to_owned()).expect("Valid key");
4163

42-
let qbft = SpecQbft::new(committee);
64+
let qbft = SpecQbft::new(committee, identifier);
4365

4466
// Complete the setup
4567
self.spec_qbft = Some(qbft);
@@ -71,11 +93,11 @@ pub struct CreateMessageTest {
7193

7294
// Any round change justifications for the message
7395
#[serde(rename = "RoundChangeJustifications")]
74-
pub round_change_justifications: Option<Vec<Justification>>,
96+
pub round_change_justifications: Option<Vec<SignedSSVMessage>>,
7597

7698
// Any prepare justifications for the message
7799
#[serde(rename = "PrepareJustifications")]
78-
pub prepare_justifications: Option<Vec<Justification>>,
100+
pub prepare_justifications: Option<Vec<SignedSSVMessage>>,
79101

80102
// The type of the QBFT Message to create
81103
#[serde(
@@ -100,25 +122,3 @@ pub struct CreateMessageTest {
100122
#[serde(skip)]
101123
pub signing_key: Option<PKey<Private>>,
102124
}
103-
104-
#[derive(Debug, Deserialize)]
105-
pub struct Justification {
106-
#[serde(rename = "Signatures")]
107-
pub signatures: Vec<String>,
108-
#[serde(rename = "OperatorIDs")]
109-
pub operator_ids: Vec<u64>,
110-
#[serde(rename = "SSVMessage")]
111-
pub ssv_message: SsvMessage,
112-
#[serde(rename = "FullData")]
113-
pub full_data: Option<String>,
114-
}
115-
116-
#[derive(Debug, Deserialize)]
117-
pub struct SsvMessage {
118-
#[serde(rename = "MsgType")]
119-
pub msg_type: u8,
120-
#[serde(rename = "MsgID")]
121-
pub msg_id: Vec<u8>,
122-
#[serde(rename = "Data")]
123-
pub data: String,
124-
}

0 commit comments

Comments
 (0)