Skip to content

Commit bee11e7

Browse files
gd-0louisponet
andauthored
impl fabric gateway commitments api (#185)
Co-authored-by: Louis Ponet <[email protected]>
1 parent f7755cc commit bee11e7

File tree

18 files changed

+476
-68
lines changed

18 files changed

+476
-68
lines changed

based/Cargo.lock

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

based/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,4 @@ tree_hash_derive = "0.10"
131131
uuid = { version = "1.12.1", features = ["serde", "v4"] }
132132

133133
backtrace = "0.3.73"
134-
tabwriter = "1.4.1"
134+
tabwriter = "1.4.1"

based/bin/gateway/src/main.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ fn run(args: GatewayArgs) -> eyre::Result<()> {
6363
};
6464
let sequencer_config: SequencerConfig = (&args).into();
6565
let evm_config = sequencer_config.evm_config.clone();
66+
let (tx, _) = tokio::sync::broadcast::channel(10_000);
6667

6768
std::thread::scope(|s| {
6869
let rt: Arc<Runtime> = tokio::runtime::Builder::new_current_thread()
@@ -74,7 +75,7 @@ fn run(args: GatewayArgs) -> eyre::Result<()> {
7475

7576
s.spawn({
7677
let rt = rt.clone();
77-
start_rpc(&args, &spine, &rt);
78+
start_rpc(&args, &spine, &rt, tx.clone());
7879
move || rt.block_on(wait_for_signal())
7980
});
8081

@@ -103,7 +104,7 @@ fn run(args: GatewayArgs) -> eyre::Result<()> {
103104
let root_peer_url = args.gossip_root_peer_url.clone();
104105
let gossip_signer_private_key = args.gossip_signer_private_key.map(|key| ECDSASigner::new(key).unwrap());
105106
s.spawn(|| {
106-
Gossiper::new(root_peer_url, gossip_signer_private_key).run(
107+
Gossiper::new(root_peer_url, gossip_signer_private_key, tx).run(
107108
spine.to_connections("Gossiper"),
108109
ActorConfig::default().with_min_loop_duration(Duration::from_millis(10)),
109110
);

based/bin/portal/src/server.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ use std::{fmt, net::SocketAddr, sync::Arc, time::Duration};
33
use alloy_eips::eip7685::RequestsOrHash;
44
use alloy_primitives::{Address, B256, Bytes, U256};
55
use alloy_rpc_types::{
6-
BlockId, BlockNumberOrTag,
6+
BlockId, BlockNumberOrTag,
77
engine::{ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus},
88
};
99
use bop_common::{
1010
api::{
11-
EngineApiClient, EngineApiServer, EthApiClient, EthApiServer, OpGethAdminApiClient,
12-
OpNodeApiClient, OpNodeP2PApiClient, OpRpcBlock, PORTAL_CAPABILITIES, PortalApiServer, RegistryApiClient,
11+
EngineApiClient, EngineApiServer, EthApiClient, EthApiServer, OpGethAdminApiClient, OpNodeApiClient,
12+
OpNodeP2PApiClient, OpRpcBlock, PORTAL_CAPABILITIES, PortalApiServer, RegistryApiClient,
1313
},
1414
communication::messages::{RpcError, RpcResult},
1515
utils::{uuid, wait_for_signal},

based/crates/common/src/api.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use alloy_rpc_types::{
99
use jsonrpsee::proc_macros::rpc;
1010
use op_alloy_consensus::OpTxEnvelope;
1111
use op_alloy_rpc_types::OpTransactionReceipt;
12-
use op_alloy_rpc_types_engine::{OpExecutionPayloadEnvelopeV3, OpExecutionPayloadEnvelopeV4, OpExecutionPayloadV4, OpPayloadAttributes};
12+
use op_alloy_rpc_types_engine::{
13+
OpExecutionPayloadEnvelopeV3, OpExecutionPayloadEnvelopeV4, OpExecutionPayloadV4, OpPayloadAttributes,
14+
};
1315
use reqwest::Url;
1416
use serde::{Deserialize, Serialize};
1517
use serde_json::Value;

based/crates/common/src/communication/messages.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ use std::{
33
sync::Arc,
44
};
55

6-
use alloy_consensus::{constants::EMPTY_WITHDRAWALS, BlockHeader, Transaction as TransactionTrait};
6+
use alloy_consensus::{BlockHeader, Transaction as TransactionTrait, constants::EMPTY_WITHDRAWALS};
77
use alloy_eips::eip2718::Encodable2718;
88
use alloy_rpc_types::engine::{
99
ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3, ForkchoiceState, PayloadAttributes, PayloadError,
1010
PayloadId,
1111
};
1212
use jsonrpsee::types::{ErrorCode, ErrorObject as RpcErrorObject};
1313
use op_alloy_rpc_types_engine::{OpExecutionPayloadEnvelopeV4, OpExecutionPayloadV4, OpPayloadAttributes};
14-
use reth_evm::{execute::BlockExecutionError, NextBlockEnvAttributes};
14+
use reth_evm::{NextBlockEnvAttributes, execute::BlockExecutionError};
1515
use reth_primitives_traits::transaction::signed::RecoveryError;
1616
use revm_primitives::{Address, U256};
1717
use serde::{Deserialize, Serialize};
@@ -258,6 +258,9 @@ pub enum RpcError {
258258
#[error("response channel closed {0}")]
259259
ChannelClosed(#[from] oneshot::error::RecvError),
260260

261+
#[error("broadcast channel closed")]
262+
BroadcastChannelClosed,
263+
261264
#[error("invalid transaction bytes")]
262265
InvalidTransaction(#[from] alloy_rlp::Error),
263266

@@ -275,6 +278,12 @@ pub enum RpcError {
275278

276279
#[error("no return")]
277280
NoReturn,
281+
282+
#[error("no commitment for request: {0:?}")]
283+
NoCommitmentForRequest(B256),
284+
285+
#[error("serialization error: {0}")]
286+
Serialization(#[from] serde_json::Error),
278287
}
279288

280289
impl From<RpcError> for RpcErrorObject<'static> {
@@ -283,16 +292,23 @@ impl From<RpcError> for RpcErrorObject<'static> {
283292
RpcError::Internal |
284293
RpcError::Timeout(_) |
285294
RpcError::ChannelClosed(_) |
295+
RpcError::BroadcastChannelClosed |
286296
RpcError::Jsonrpsee(_) |
287297
RpcError::TokioJoin(_) |
288298
RpcError::Db(_) |
289299
RpcError::Io(_) |
300+
RpcError::Serialization(_) |
290301
RpcError::NoReturn => internal_error(),
291302
RpcError::InvalidTransaction(error) => RpcErrorObject::owned(
292303
ErrorCode::InvalidParams.code(),
293304
ErrorCode::InvalidParams.message(),
294305
Some(error.to_string()),
295306
),
307+
RpcError::NoCommitmentForRequest(slot) => RpcErrorObject::owned(
308+
ErrorCode::InvalidParams.code(),
309+
ErrorCode::InvalidParams.message(),
310+
Some(format!("no commitment for request: {slot}")),
311+
),
296312
}
297313
}
298314
}

based/crates/common/src/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ pub struct GatewayArgs {
3333
/// The port to run the engine_ and eth_ RPC
3434
#[arg(long = "rpc.port", default_value_t = 9997)]
3535
pub rpc_port: u16,
36+
#[arg(long = "rpc.port_no_auth", default_value_t = 9998)]
37+
pub rpc_port_no_auth: u16,
3638
#[arg(long = "rpc.jwt")]
3739
pub rpc_jwt: String,
3840
/// Url to an L2 eth api rpc

based/crates/common/src/fabric.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use alloy_primitives::{Address, B256, Bytes};
2+
use jsonrpsee::proc_macros::rpc;
3+
use serde::{Deserialize, Serialize};
4+
use ssz_types::{VariableList, typenum};
5+
use tree_hash_derive::TreeHash;
6+
7+
use crate::communication::messages::RpcResult;
8+
9+
pub type MaxCommitmentRequestPayloadSize = typenum::U1048576; // 1MB.
10+
pub type CommitmentRequestPayload = VariableList<u8, MaxCommitmentRequestPayloadSize>;
11+
12+
pub const FRAG_COMMITMENT_TYPE: u64 = 7;
13+
14+
/// A CommitmentRequest message created by a user
15+
#[derive(Clone, Serialize, Deserialize, Debug, TreeHash)]
16+
#[serde(rename_all = "camelCase")]
17+
pub struct CommitmentRequest {
18+
pub commitment_type: u64,
19+
pub payload: CommitmentRequestPayload,
20+
pub slasher: Address,
21+
}
22+
23+
/// A Commitment message responding to a CommitmentRequest
24+
#[derive(Clone, Serialize, Deserialize, Debug)]
25+
#[serde(rename_all = "camelCase")]
26+
pub struct Commitment {
27+
pub commitment_type: u64,
28+
pub payload: Bytes,
29+
pub request_hash: B256,
30+
pub slasher: Address,
31+
}
32+
33+
/// A signed Commitment binding to a CommitmentRequest
34+
#[derive(Clone, Serialize, Deserialize, Debug)]
35+
#[serde(rename_all = "camelCase")]
36+
pub struct SignedCommitment {
37+
pub commitment: Commitment,
38+
pub signature: Bytes,
39+
}
40+
41+
/// Specifies which commitments can be made for a specific chain
42+
#[derive(Clone, Serialize, Deserialize, Debug)]
43+
#[serde(rename_all = "camelCase")]
44+
pub struct Offering {
45+
pub chain_id: u64,
46+
pub commitment_types: Vec<u64>,
47+
}
48+
49+
/// Information about a Gateway's offerings at a specific slot
50+
#[derive(Clone, Serialize, Deserialize, Debug)]
51+
#[serde(rename_all = "camelCase")]
52+
pub struct SlotInfo {
53+
pub slot: u64,
54+
pub offerings: Vec<Offering>,
55+
}
56+
57+
/// Response containing multiple SlotInfo
58+
#[derive(Clone, Serialize, Deserialize, Debug, Default)]
59+
#[serde(rename_all = "camelCase")]
60+
pub struct SlotInfoResponse {
61+
pub slots: Vec<SlotInfo>,
62+
}
63+
64+
/// Fee information for a specific commitment request
65+
#[derive(Clone, Serialize, Deserialize, Debug)]
66+
#[serde(rename_all = "camelCase")]
67+
pub struct FeeInfo {
68+
pub payload: Bytes,
69+
pub commitment_type: u64,
70+
}
71+
72+
impl Default for FeeInfo {
73+
fn default() -> Self {
74+
Self { payload: Bytes::new(), commitment_type: FRAG_COMMITMENT_TYPE }
75+
}
76+
}
77+
78+
#[rpc(client, server, namespace = "gateway")]
79+
pub trait FabricGatewayApi {
80+
/// Request a new SignedCommitment
81+
#[method(name = "commitment")]
82+
async fn post_commitment(&self, commitment_request: CommitmentRequest) -> RpcResult<SignedCommitment>;
83+
84+
/// Request an existing SignedCommitment by request hash
85+
#[method(name = "getCommitment")]
86+
async fn get_commitment(&self, request_hash: B256) -> RpcResult<SignedCommitment>;
87+
88+
/// Get Gateway information for upcoming slots
89+
#[method(name = "slots")]
90+
async fn get_slots(&self) -> RpcResult<SlotInfoResponse>;
91+
92+
/// Get commitment fee information
93+
#[method(name = "fee")]
94+
async fn get_fee_info(&self, commitment_request: CommitmentRequest) -> RpcResult<FeeInfo>;
95+
}

based/crates/common/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub mod communication;
44
pub mod config;
55
pub mod db;
66
pub mod eth;
7+
pub mod fabric;
78
pub mod p2p;
89
pub mod shared;
910
pub mod signing;

based/crates/common/src/p2p.rs

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,6 @@ use tree_hash_derive::TreeHash;
88

99
use crate::{signing::ECDSASigner, transaction::Transaction as BuilderTransaction};
1010

11-
#[derive(Debug, Clone, PartialEq, Eq, TreeHash, Serialize, Deserialize, AsRefStr)]
12-
#[tree_hash(enum_behaviour = "union")]
13-
#[serde(untagged)]
14-
#[non_exhaustive]
15-
pub enum VersionedMessage {
16-
FragV0(FragV0),
17-
SealV0(SealV0),
18-
EnvV0(EnvV0),
19-
}
20-
21-
impl From<FragV0> for VersionedMessage {
22-
fn from(value: FragV0) -> Self {
23-
Self::FragV0(value)
24-
}
25-
}
26-
27-
impl From<SealV0> for VersionedMessage {
28-
fn from(value: SealV0) -> Self {
29-
Self::SealV0(value)
30-
}
31-
}
32-
33-
impl From<EnvV0> for VersionedMessage {
34-
fn from(value: EnvV0) -> Self {
35-
Self::EnvV0(value)
36-
}
37-
}
38-
3911
pub type MaxExtraDataSize = typenum::U256;
4012
pub type ExtraData = VariableList<u8, MaxExtraDataSize>;
4113

@@ -91,7 +63,7 @@ pub struct FragV0 {
9163
pub is_last: bool,
9264
/// Ordered list of EIP-2718 encoded transactions
9365
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
94-
txs: Transactions,
66+
pub txs: Transactions,
9567
}
9668

9769
impl FragV0 {
@@ -124,13 +96,52 @@ pub struct SealV0 {
12496
pub block_hash: B256,
12597
}
12698

99+
#[derive(Debug, Clone, PartialEq, Eq, TreeHash, Serialize, Deserialize, AsRefStr)]
100+
#[tree_hash(enum_behaviour = "union")]
101+
#[serde(untagged)]
102+
#[non_exhaustive]
103+
pub enum VersionedMessage {
104+
FragV0(FragV0),
105+
SealV0(SealV0),
106+
EnvV0(EnvV0),
107+
}
108+
127109
impl VersionedMessage {
128-
pub fn to_json(&self, signer: &ECDSASigner) -> serde_json::Value {
110+
pub fn to_signed(self, signer: &ECDSASigner) -> SignedVersionedMessage {
129111
let hash = self.tree_hash_root();
130112
let signature = signer.sign_message(hash).expect("couldn't sign message");
131113
let signature = Bytes::from(signature.as_bytes());
114+
SignedVersionedMessage { message: self, signature }
115+
}
116+
}
117+
118+
impl From<FragV0> for VersionedMessage {
119+
fn from(value: FragV0) -> Self {
120+
Self::FragV0(value)
121+
}
122+
}
123+
124+
impl From<SealV0> for VersionedMessage {
125+
fn from(value: SealV0) -> Self {
126+
Self::SealV0(value)
127+
}
128+
}
129+
130+
impl From<EnvV0> for VersionedMessage {
131+
fn from(value: EnvV0) -> Self {
132+
Self::EnvV0(value)
133+
}
134+
}
135+
136+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
137+
pub struct SignedVersionedMessage {
138+
pub message: VersionedMessage,
139+
pub signature: Bytes,
140+
}
132141

133-
let method = match &self {
142+
impl SignedVersionedMessage {
143+
pub fn to_json(&self) -> serde_json::Value {
144+
let method = match &self.message {
134145
VersionedMessage::FragV0(_) => "based_newFrag",
135146
VersionedMessage::SealV0(_) => "based_sealFrag",
136147
VersionedMessage::EnvV0(_) => "based_env",
@@ -139,7 +150,7 @@ impl VersionedMessage {
139150
serde_json::json!({
140151
"jsonrpc": "2.0",
141152
"method": method,
142-
"params": [{"signature": signature, "message": self}],
153+
"params": [{"signature": self.signature, "message": self.message}],
143154
"id": 1
144155
})
145156
}

0 commit comments

Comments
 (0)