Subscribe to topic and decode SignedSSVMessage#80
Conversation
4773cb0 to
2172c8d
Compare
9c09396 to
cf4576e
Compare
69c40c7 to
44d28bf
Compare
dknopik
left a comment
There was a problem hiding this comment.
Left some remarks. Looking good!
| impl Encode for MessageID { | ||
| fn is_ssz_fixed_len() -> bool { | ||
| true | ||
| } | ||
|
|
||
| fn ssz_append(&self, buf: &mut Vec<u8>) { | ||
| buf.extend_from_slice(&self.0); | ||
| } | ||
|
|
||
| fn ssz_fixed_len() -> usize { | ||
| MESSAGE_ID_LEN | ||
| } | ||
|
|
||
| fn ssz_bytes_len(&self) -> usize { | ||
| MESSAGE_ID_LEN | ||
| } | ||
| } | ||
|
|
||
| impl Decode for MessageID { | ||
| fn is_ssz_fixed_len() -> bool { | ||
| true | ||
| } | ||
|
|
||
| fn ssz_fixed_len() -> usize { | ||
| MESSAGE_ID_LEN | ||
| } | ||
|
|
||
| fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> { | ||
| if bytes.len() != MESSAGE_ID_LEN { | ||
| return Err(DecodeError::InvalidByteLength { | ||
| len: bytes.len(), | ||
| expected: MESSAGE_ID_LEN, | ||
| }); | ||
| } | ||
| let mut id = [0u8; MESSAGE_ID_LEN]; | ||
| id.copy_from_slice(bytes); | ||
| Ok(MessageID(id)) | ||
| } | ||
| } |
There was a problem hiding this comment.
impls look good, but it's unfortunate that this is necessary, using the derive macro would be nicer. I'll see if we can improve the situation upstream 👍
There was a problem hiding this comment.
Yeah, it fails with an error. Should we add a comment explaining why this is necessary?
| impl Encode for MsgType { | ||
| fn is_ssz_fixed_len() -> bool { | ||
| true | ||
| } | ||
|
|
||
| fn ssz_append(&self, buf: &mut Vec<u8>) { | ||
| let value: u64 = match self { | ||
| MsgType::SSVConsensusMsgType => 0, | ||
| MsgType::SSVPartialSignatureMsgType => 1, | ||
| }; | ||
| buf.extend_from_slice(&value.to_le_bytes()); | ||
| } | ||
|
|
||
| fn ssz_fixed_len() -> usize { | ||
| U64_SIZE | ||
| } | ||
|
|
||
| fn ssz_bytes_len(&self) -> usize { | ||
| U64_SIZE | ||
| } | ||
| } | ||
|
|
||
| impl Decode for MsgType { | ||
| fn is_ssz_fixed_len() -> bool { | ||
| true | ||
| } | ||
|
|
||
| fn ssz_fixed_len() -> usize { | ||
| U64_SIZE | ||
| } | ||
|
|
||
| fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> { | ||
| if bytes.len() != U64_SIZE { | ||
| return Err(DecodeError::InvalidByteLength { | ||
| len: bytes.len(), | ||
| expected: U64_SIZE, | ||
| }); | ||
| } | ||
| let value = u64::from_le_bytes(bytes.try_into().unwrap()); | ||
| MsgType::try_from_u64(value) | ||
| } | ||
| } |
There was a problem hiding this comment.
Same here, would be nice for the macro to support generating this if there is #[repr(u*)] on an enum, will check upstream. Otherwise, lgtm.
| pub struct SSVMessage { | ||
| msg_type: MsgType, | ||
| msg_id: MessageID, // Fixed-size [u8; 56] | ||
| data: Vec<u8>, // Variable-length byte array |
There was a problem hiding this comment.
I do not like the Vec<u8> here - but unfortunately, it's probably the best option.
The other option would be an enum with ssz_derive's #[ssz(enum_behaviour = "transparent")], but this is inefficient (as it blindly tries to decode each variant until one variant does not fail to decode).
Longer term, IMO the SSV spec should switch to a SSZ union - or separate topics for separate message types.
There was a problem hiding this comment.
I agree now. My argument of decoupling this type from the application doesn't make much as the msg_type: MsgType already has application knowledge, doesn't it? Also, not sure that decoding this at the application level is the right place for that. Would this inefficiency severely affect performance? No way around that? But another possible issue is the EthSpec everywhere in the code.
There was a problem hiding this comment.
Not decoding it at the networking layer would make more sense if the msg_type was encoded inside the data. In this way, we would know nothing about the data and just deliver it to the application. If messages are created/deleted/modified nothing would change at this layer.
There was a problem hiding this comment.
I think we are pretty flexible in being able to push msg_type down into data. As long as qbft can work with serialized data without having to introduce EthSpec I dont think we have a ton of hard constraints. Some internal abstraction layer will be needed regardless. Something like
Network -> decode -> Validator store -> Qbft manager/Signature Collector -> Validator Store -> encoder -> Network
There was a problem hiding this comment.
It's even simpler. We would just not decode msg_type and pass it as a number. It'd make even more sense with the current code, I guess.
| signatures: Vec<Vec<u8>>, // Vec of Vec<u8>, max 13 elements, each up to 256 bytes | ||
| operator_ids: Vec<OperatorID>, // Vec of OperatorID (u64), max 13 elements | ||
| ssv_message: SSVMessage, // SSVMessage: Required field | ||
| full_data: Vec<u8>, // Variable-length byte array, max 4,194,532 bytes |
There was a problem hiding this comment.
I think it's preferable to use ssz_types::VariableList (basically a Vec wrapper) here to enforce the maximum length.
There was a problem hiding this comment.
The docs seem to say the list is truncated if it's bigger than the max.
There was a problem hiding this comment.
There's a validation in the current code checking the length.
There was a problem hiding this comment.
The docs seem to say the list is truncated if it's bigger than the max.
Yeah, the From implementation unfortunately truncates for some reason. However, the Decode impl does check: https://docs.rs/ssz_types/0.10.0/src/ssz_types/variable_list.rs.html#274-313
Issue Addressed
Initial implementation for #74. The aim of this PR is to able to subscribe to a topic, receive and decode SignedSSVMessage's.
Proposed Changes
ssv.v2.9in therunmethod.Additional Info
As in the previous subnet search PR, subnet 9 is an arbitrary choice for test purposes and has no specific significance.