-
Notifications
You must be signed in to change notification settings - Fork 78
P2P Flashblocks Propagation #373
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
base: main
Are you sure you want to change the base?
Changes from all commits
147c01f
14c4297
54af8b1
9966ecb
cddf97f
89f5ee6
c3ad860
36201ba
0dc7064
e94925e
dc15ab1
677574a
9c3667a
9944798
1d0e8ad
782b951
f225b5e
1fa5b68
e6b0e9d
4ac4633
0cbf403
dcf2a43
485adf0
6d252fe
a476ee9
6c60ddd
463c2f7
8298f29
fd198aa
fd06a6c
495ab62
65125aa
7f82992
ba36548
a7b4973
7e36324
ad658fe
69f50d3
b40a313
2f3a210
05b6eba
bf980dd
0abf5e8
d1f3ed1
36559b8
dd3db0f
ffb966e
6f4ab14
80f24e1
70d37d7
72b2bc6
d6d129e
9a8b031
aa2f722
83cb7ef
d24dcb5
6b45336
7680cb7
bd451a1
fd99315
4a862cc
3aa5090
822f0bc
481893e
e0a40c6
888bd1b
8dc7540
360158f
bb57a44
d8249da
528cac3
93456df
7ab74a1
36926ad
c0cc1e2
f634e4b
12c65df
d84a2da
378fd99
0a0079d
171cfbc
bf86104
e46bf80
c7130aa
101ffc0
fe634cb
ba6b3e4
54d8745
58feb62
bb873b2
39d6a27
dc3f48c
075b9c6
40774a2
a236f55
4ea4ba1
513d85f
5c9dbbc
e690dec
6355b88
6be9b9d
46fd26d
afeed2d
53838ee
26a342e
f929970
39b4d24
40cf2e1
02ac0d5
b565106
2eb0895
55060fc
0117c0d
79bcd17
ba36d45
fc0f983
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,7 +15,7 @@ RUN cargo install sccache --version ^0.9 | |
| RUN cargo install cargo-chef --version ^0.1 | ||
|
|
||
| RUN apt-get update \ | ||
| && apt-get install -y clang libclang-dev | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this necessary
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have this now: Should be fine.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I'm asking is this a necessary addition. It was working previously, so what addition made this a necessary change
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TBH it was a while ago and I'm not sure :p |
||
| && apt-get install -y clang libclang-dev gcc | ||
|
|
||
| ENV CARGO_HOME=/usr/local/cargo | ||
| ENV RUSTC_WRAPPER=sccache | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,222 @@ | ||
| use alloy_primitives::{B64, Bytes}; | ||
| use alloy_rlp::{Decodable, Encodable, Header}; | ||
| use alloy_rpc_types_engine::PayloadId; | ||
| use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; | ||
| use serde::{Deserialize, Serialize}; | ||
| use thiserror::Error; | ||
|
|
||
| /// An authorization token that grants a builder permission to publish flashblocks for a specific payload. | ||
| /// | ||
| /// The `authorizer_sig` is made over the `payload_id`, `timestamp`, and `builder_vk`. This is | ||
| /// useful because it allows the authorizer to control which builders can publish flashblocks in | ||
| /// real time, without relying on consumers to verify the builder's public key against a | ||
| /// pre-defined list. | ||
| #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | ||
| pub struct Authorization { | ||
| /// The unique identifier of the payload this authorization applies to | ||
| pub payload_id: PayloadId, | ||
| /// Unix timestamp when this authorization was created | ||
| pub timestamp: u64, | ||
| /// The public key of the builder who is authorized to sign messages | ||
| pub builder_vk: VerifyingKey, | ||
| /// The authorizer's signature over the payload_id, timestamp, and builder_vk | ||
| pub authorizer_sig: Signature, | ||
| } | ||
|
|
||
| #[derive(Debug, Error, PartialEq)] | ||
| pub enum AuthorizationError { | ||
| #[error("invalid authorizer signature")] | ||
| InvalidAuthorizerSig, | ||
| } | ||
|
|
||
| impl Authorization { | ||
| /// Creates a new authorization token for a builder to publish messages for a specific payload. | ||
| /// | ||
| /// This function creates a cryptographic authorization by signing a message containing the | ||
| /// payload ID, timestamp, and builder's public key using the authorizer's signing key. | ||
| /// | ||
| /// # Arguments | ||
| /// | ||
| /// * `payload_id` - The unique identifier of the payload this authorization applies to | ||
| /// * `timestamp` - Unix timestamp associated with this `payload_id` | ||
| /// * `authorizer_sk` - The authorizer's signing key used to create the signature | ||
| /// * `actor_vk` - The verifying key of the actor being authorized | ||
| /// | ||
| /// # Returns | ||
| /// | ||
| /// A new `Authorization` instance with the generated signature | ||
| pub fn new( | ||
| payload_id: PayloadId, | ||
| timestamp: u64, | ||
| authorizer_sk: &SigningKey, | ||
| actor_vk: VerifyingKey, | ||
| ) -> Self { | ||
| let mut msg = payload_id.0.to_vec(); | ||
| msg.extend_from_slice(×tamp.to_le_bytes()); | ||
| msg.extend_from_slice(actor_vk.as_bytes()); | ||
| let hash = blake3::hash(&msg); | ||
| let sig = authorizer_sk.sign(hash.as_bytes()); | ||
|
|
||
| Self { | ||
| payload_id, | ||
| timestamp, | ||
| builder_vk: actor_vk, | ||
| authorizer_sig: sig, | ||
| } | ||
| } | ||
|
|
||
| /// Verifies the authorization signature against the provided authorizer's verifying key. | ||
| /// | ||
| /// This function reconstructs the signed message from the authorization data and verifies | ||
| /// that the signature was created by the holder of the authorizer's private key. | ||
| /// | ||
| /// # Arguments | ||
| /// | ||
| /// * `authorizer_sk` - The verifying key of the authorizer to verify against | ||
| /// | ||
| /// # Returns | ||
| /// | ||
| /// * `Ok(())` if the signature is valid | ||
| /// * `Err(FlashblocksP2PError::InvalidAuthorizerSig)` if the signature is invalid | ||
| pub fn verify(&self, authorizer_sk: VerifyingKey) -> Result<(), AuthorizationError> { | ||
| let mut msg = self.payload_id.0.to_vec(); | ||
| msg.extend_from_slice(&self.timestamp.to_le_bytes()); | ||
| msg.extend_from_slice(self.builder_vk.as_bytes()); | ||
| let hash = blake3::hash(&msg); | ||
| authorizer_sk | ||
| .verify(hash.as_bytes(), &self.authorizer_sig) | ||
| .map_err(|_| AuthorizationError::InvalidAuthorizerSig) | ||
| } | ||
| } | ||
|
|
||
| impl Encodable for Authorization { | ||
| fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { | ||
| // pre-serialize the key & sig once so we can reuse the bytes & lengths | ||
| let pub_bytes = Bytes::copy_from_slice(self.builder_vk.as_bytes()); // 33 bytes | ||
| let sig_bytes = Bytes::copy_from_slice(&self.authorizer_sig.to_bytes()); // 64 bytes | ||
|
|
||
| let payload_len = self.payload_id.0.length() | ||
| + self.timestamp.length() | ||
| + pub_bytes.length() | ||
| + sig_bytes.length(); | ||
|
|
||
| Header { | ||
| list: true, | ||
| payload_length: payload_len, | ||
| } | ||
| .encode(out); | ||
|
|
||
| // 1. payload_id (inner B64 already Encodable) | ||
| self.payload_id.0.encode(out); | ||
| // 2. timestamp | ||
| self.timestamp.encode(out); | ||
| // 3. builder_pub | ||
| pub_bytes.encode(out); | ||
| // 4. authorizer_sig | ||
| sig_bytes.encode(out); | ||
| } | ||
|
|
||
| fn length(&self) -> usize { | ||
| let pub_bytes = Bytes::copy_from_slice(self.builder_vk.as_bytes()); | ||
| let sig_bytes = Bytes::copy_from_slice(&self.authorizer_sig.to_bytes()); | ||
|
|
||
| let payload_len = self.payload_id.0.length() | ||
| + self.timestamp.length() | ||
| + pub_bytes.length() | ||
| + sig_bytes.length(); | ||
|
|
||
| Header { | ||
| list: true, | ||
| payload_length: payload_len, | ||
| } | ||
| .length() | ||
| + payload_len | ||
| } | ||
| } | ||
|
|
||
| impl Decodable for Authorization { | ||
| fn decode(buf: &mut &[u8]) -> Result<Self, alloy_rlp::Error> { | ||
| let header = Header::decode(buf)?; | ||
| if !header.list { | ||
| return Err(alloy_rlp::Error::UnexpectedString); | ||
| } | ||
| let mut body = &buf[..header.payload_length]; | ||
|
|
||
| // 1. payload_id | ||
| let payload_id = alloy_rpc_types_engine::PayloadId(B64::decode(&mut body)?); | ||
|
|
||
| // 2. timestamp | ||
| let timestamp = u64::decode(&mut body)?; | ||
|
|
||
| // 3. builder_pub | ||
| let pub_bytes = Bytes::decode(&mut body)?; | ||
| let builder_pub = VerifyingKey::try_from(pub_bytes.as_ref()) | ||
| .map_err(|_| alloy_rlp::Error::Custom("bad builder_pub"))?; | ||
|
|
||
| // 4. authorizer_sig | ||
| let sig_bytes = Bytes::decode(&mut body)?; | ||
| let authorizer_sig = Signature::try_from(sig_bytes.as_ref()) | ||
| .map_err(|_| alloy_rlp::Error::Custom("bad signature"))?; | ||
|
|
||
| // advance caller’s slice cursor | ||
| *buf = &buf[header.payload_length..]; | ||
|
|
||
| Ok(Self { | ||
| payload_id, | ||
| timestamp, | ||
| builder_vk: builder_pub, | ||
| authorizer_sig, | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
| use alloy_rlp::{Decodable, Encodable, encode}; | ||
|
|
||
| fn key_pair(seed: u8) -> (SigningKey, VerifyingKey) { | ||
| let bytes = [seed; 32]; | ||
| let sk = SigningKey::from_bytes(&bytes); | ||
| let vk = sk.verifying_key(); | ||
| (sk, vk) | ||
| } | ||
|
|
||
| #[test] | ||
| fn authorization_rlp_roundtrip_and_verify() { | ||
| let (authorizer_sk, authorizer_vk) = key_pair(1); | ||
| let (_, builder_vk) = key_pair(2); | ||
|
|
||
| let auth = Authorization::new( | ||
| PayloadId::default(), | ||
| 1_700_000_123, | ||
| &authorizer_sk, | ||
| builder_vk, | ||
| ); | ||
|
|
||
| let encoded = encode(auth); | ||
| assert_eq!(encoded.len(), auth.length(), "length impl correct"); | ||
|
|
||
| let mut slice = encoded.as_ref(); | ||
| let decoded = Authorization::decode(&mut slice).expect("decoding succeeds"); | ||
| assert!(slice.is_empty(), "decoder consumed all bytes"); | ||
| assert_eq!(decoded, auth, "round-trip preserves value"); | ||
|
|
||
| // Signature is valid | ||
| decoded.verify(authorizer_vk).expect("signature verifies"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn authorization_signature_tamper_is_detected() { | ||
| let (authorizer_sk, authorizer_vk) = key_pair(1); | ||
| let (_, builder_vk) = key_pair(2); | ||
|
|
||
| let mut auth = Authorization::new(PayloadId::default(), 42, &authorizer_sk, builder_vk); | ||
|
|
||
| let mut sig_bytes = auth.authorizer_sig.to_bytes(); | ||
| sig_bytes[0] ^= 1; | ||
| auth.authorizer_sig = Signature::try_from(sig_bytes.as_ref()).unwrap(); | ||
|
|
||
| assert!(auth.verify(authorizer_vk).is_err()); | ||
| } | ||
| } |
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.
These dependencies should be pinned to
v1.9.3There 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.
1.9.3 doesn't have the latest op-alloy which is causing problems
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.
Why can't we just match the alloy versions from reth?
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'd have to downgrade
op_alloy_rpc_types_enginesince we're already depending on the newer version here.