-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Subscribe to topic and decode SignedSSVMessage #80
base: unstable
Are you sure you want to change the base?
Conversation
4773cb0
to
2172c8d
Compare
9c09396
to
cf4576e
Compare
69c40c7
to
44d28bf
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
impl
s 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docs seem to say the list is truncated if it's bigger than the max.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a validation in the current code checking the length.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.9
in therun
method.Additional Info
As in the previous subnet search PR, subnet 9 is an arbitrary choice for test purposes and has no specific significance.