mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-07-15 10:20:01 +00:00
feat(sequencer): adminConfigureChannel RPC for channel roster changes
This commit is contained in:
parent
0143a7edf1
commit
726312025d
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -9085,6 +9085,7 @@ dependencies = [
|
||||
"common",
|
||||
"env_logger",
|
||||
"futures",
|
||||
"hex",
|
||||
"jsonrpsee",
|
||||
"lee",
|
||||
"log",
|
||||
@ -9105,6 +9106,7 @@ dependencies = [
|
||||
"hex",
|
||||
"lee",
|
||||
"lee_core",
|
||||
"serde",
|
||||
"serde_with",
|
||||
]
|
||||
|
||||
|
||||
@ -108,14 +108,17 @@ pub trait BlockPublisherTrait: Clone {
|
||||
/// `ChannelConfig` op. The sequencer's bedrock key must be the channel
|
||||
/// admin (`keys[0]`); the L1 rejects non-admin signers, so this is not
|
||||
/// re-validated here.
|
||||
async fn configure_channel(
|
||||
///
|
||||
/// Desugared (not `async fn`) so the returned future is provably `Send` —
|
||||
/// generic callers awaiting it inside jsonrpsee handlers require that.
|
||||
fn configure_channel(
|
||||
&self,
|
||||
keys: Vec<Ed25519PublicKey>,
|
||||
posting_timeframe: u32,
|
||||
posting_timeout: u32,
|
||||
configuration_threshold: u16,
|
||||
withdraw_threshold: u16,
|
||||
) -> Result<()>;
|
||||
) -> impl Future<Output = Result<()>> + Send;
|
||||
|
||||
fn channel_id(&self) -> ChannelId;
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ programs.workspace = true
|
||||
clap = { workspace = true, features = ["derive", "env"] }
|
||||
anyhow.workspace = true
|
||||
env_logger.workspace = true
|
||||
hex.workspace = true
|
||||
log.workspace = true
|
||||
tokio.workspace = true
|
||||
tokio-util.workspace = true
|
||||
|
||||
@ -13,4 +13,5 @@ lee.workspace = true
|
||||
lee_core.workspace = true
|
||||
|
||||
hex.workspace = true
|
||||
serde.workspace = true
|
||||
serde_with.workspace = true
|
||||
|
||||
@ -26,3 +26,17 @@ impl FromStr for ChannelId {
|
||||
Ok(Self(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
/// Request for `adminConfigureChannel`: replaces the channel's accredited key
|
||||
/// set and rotation parameters.
|
||||
///
|
||||
/// `keys` are hex-encoded 32-byte Ed25519 public keys; `keys[0]` must be this
|
||||
/// sequencer's (admin) key.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ConfigureChannelRequest {
|
||||
pub keys: Vec<String>,
|
||||
pub posting_timeframe: u32,
|
||||
pub posting_timeout: u32,
|
||||
pub configuration_threshold: u16,
|
||||
pub withdraw_threshold: u16,
|
||||
}
|
||||
|
||||
@ -6,8 +6,8 @@ use jsonrpsee::types::ErrorObjectOwned;
|
||||
#[cfg(feature = "client")]
|
||||
pub use jsonrpsee::{core::ClientError, http_client::HttpClientBuilder as SequencerClientBuilder};
|
||||
use sequencer_service_protocol::{
|
||||
Account, AccountId, Block, BlockId, ChannelId, Commitment, HashType, LeeTransaction,
|
||||
MembershipProof, Nonce, ProgramId,
|
||||
Account, AccountId, Block, BlockId, ChannelId, Commitment, ConfigureChannelRequest, HashType,
|
||||
LeeTransaction, MembershipProof, Nonce, ProgramId,
|
||||
};
|
||||
|
||||
#[cfg(all(not(feature = "server"), not(feature = "client")))]
|
||||
@ -92,4 +92,12 @@ pub trait Rpc {
|
||||
async fn get_channel_id(&self) -> Result<ChannelId, ErrorObjectOwned>;
|
||||
|
||||
// =============================================================================================
|
||||
|
||||
/// Admin-only in effect: the L1 rejects the config op unless this
|
||||
/// sequencer's key is the channel admin (`keys[0]` of the current roster).
|
||||
#[method(name = "adminConfigureChannel")]
|
||||
async fn admin_configure_channel(
|
||||
&self,
|
||||
request: ConfigureChannelRequest,
|
||||
) -> Result<(), ErrorObjectOwned>;
|
||||
}
|
||||
|
||||
@ -9,11 +9,12 @@ use lee;
|
||||
use log::warn;
|
||||
use mempool::MemPoolHandle;
|
||||
use sequencer_core::{
|
||||
DbError, SequencerCore, TransactionOrigin, block_publisher::BlockPublisherTrait,
|
||||
DbError, SequencerCore, TransactionOrigin,
|
||||
block_publisher::{BlockPublisherTrait, Ed25519PublicKey},
|
||||
};
|
||||
use sequencer_service_protocol::{
|
||||
Account, AccountId, Block, BlockId, ChannelId, Commitment, HashType, MembershipProof, Nonce,
|
||||
ProgramId,
|
||||
Account, AccountId, Block, BlockId, ChannelId, Commitment, ConfigureChannelRequest, HashType,
|
||||
MembershipProof, Nonce, ProgramId,
|
||||
};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
@ -40,7 +41,7 @@ impl<BC: BlockPublisherTrait> SequencerService<BC> {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<BC: BlockPublisherTrait + Send + 'static> sequencer_service_rpc::RpcServer
|
||||
impl<BC: BlockPublisherTrait + Send + Sync + 'static> sequencer_service_rpc::RpcServer
|
||||
for SequencerService<BC>
|
||||
{
|
||||
async fn send_transaction(&self, tx: LeeTransaction) -> Result<HashType, ErrorObjectOwned> {
|
||||
@ -198,8 +199,66 @@ impl<BC: BlockPublisherTrait + Send + 'static> sequencer_service_rpc::RpcServer
|
||||
let channel_id = self.sequencer.lock().await.block_publisher().channel_id();
|
||||
Ok(ChannelId(*channel_id.as_ref()))
|
||||
}
|
||||
|
||||
async fn admin_configure_channel(
|
||||
&self,
|
||||
request: ConfigureChannelRequest,
|
||||
) -> Result<(), ErrorObjectOwned> {
|
||||
let keys = request
|
||||
.keys
|
||||
.iter()
|
||||
.map(|hex_key| parse_channel_key(hex_key))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let sequencer = self.sequencer.lock().await;
|
||||
sequencer
|
||||
.configure_channel(
|
||||
keys,
|
||||
request.posting_timeframe,
|
||||
request.posting_timeout,
|
||||
request.configuration_threshold,
|
||||
request.withdraw_threshold,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
ErrorObjectOwned::owned(
|
||||
ErrorCode::InternalError.code(),
|
||||
format!("{err:#}"),
|
||||
None::<()>,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn internal_error(err: &DbError) -> ErrorObjectOwned {
|
||||
ErrorObjectOwned::owned(ErrorCode::InternalError.code(), err.to_string(), None::<()>)
|
||||
}
|
||||
|
||||
/// Parses one hex-encoded 32-byte Ed25519 public key from an RPC request.
|
||||
fn parse_channel_key(hex_key: &str) -> Result<Ed25519PublicKey, ErrorObjectOwned> {
|
||||
let invalid = |detail: String| {
|
||||
ErrorObjectOwned::owned(ErrorCode::InvalidParams.code(), detail, None::<()>)
|
||||
};
|
||||
let mut bytes = [0_u8; 32];
|
||||
hex::decode_to_slice(hex_key, &mut bytes)
|
||||
.map_err(|err| invalid(format!("Invalid hex-encoded key: {err}")))?;
|
||||
Ed25519PublicKey::from_bytes(&bytes)
|
||||
.map_err(|err| invalid(format!("Invalid Ed25519 public key: {err}")))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use sequencer_core::block_publisher::Ed25519Key;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_channel_key_roundtrips_and_rejects_garbage() {
|
||||
let key = Ed25519Key::from_bytes(&[7; 32]).public_key();
|
||||
let parsed = parse_channel_key(&hex::encode(key.to_bytes())).unwrap();
|
||||
assert_eq!(parsed.to_bytes(), key.to_bytes());
|
||||
|
||||
assert!(parse_channel_key("not-hex").is_err());
|
||||
assert!(parse_channel_key("abcd").is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user