mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-07-15 10:20:01 +00:00
fix(sequencer): validate adminConfigureChannel requests, per Copilot review
This commit is contained in:
parent
bbb1f999df
commit
7c3043119b
@ -30,8 +30,8 @@ impl FromStr for ChannelId {
|
||||
/// 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.
|
||||
/// - `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>,
|
||||
@ -40,3 +40,81 @@ pub struct ConfigureChannelRequest {
|
||||
pub configuration_threshold: u16,
|
||||
pub withdraw_threshold: u16,
|
||||
}
|
||||
|
||||
impl ConfigureChannelRequest {
|
||||
/// Structural sanity checks for the request.
|
||||
///
|
||||
/// The L1 validates this too, but its async so we don't immediately
|
||||
/// know about them when we submit. Checking this here instead gives
|
||||
/// immediate feedback to the caller.
|
||||
///
|
||||
/// We don't need a particular error type here, it's going to be logged only.
|
||||
pub fn validate(&self) -> Result<(), String> {
|
||||
let key_count = self.keys.len();
|
||||
if key_count == 0 {
|
||||
return Err("Channel key list must not be empty".to_owned());
|
||||
}
|
||||
for (name, threshold) in [
|
||||
("configuration_threshold", self.configuration_threshold),
|
||||
("withdraw_threshold", self.withdraw_threshold),
|
||||
] {
|
||||
if threshold == 0 || usize::from(threshold) > key_count {
|
||||
return Err(format!(
|
||||
"{name} must be between 1 and the key count ({key_count}), got {threshold}"
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn configure_channel_request_validate_rejects_static_garbage() {
|
||||
// `validate` is structural: key contents are not parsed here.
|
||||
let base = ConfigureChannelRequest {
|
||||
keys: vec!["unparsed".to_owned(), "unparsed".to_owned()],
|
||||
posting_timeframe: 20,
|
||||
posting_timeout: 30,
|
||||
configuration_threshold: 1,
|
||||
withdraw_threshold: 2,
|
||||
};
|
||||
|
||||
assert!(base.validate().is_ok());
|
||||
assert!(
|
||||
ConfigureChannelRequest {
|
||||
keys: vec![],
|
||||
..base
|
||||
}
|
||||
.validate()
|
||||
.is_err()
|
||||
);
|
||||
assert!(
|
||||
ConfigureChannelRequest {
|
||||
configuration_threshold: 0,
|
||||
..base.clone()
|
||||
}
|
||||
.validate()
|
||||
.is_err()
|
||||
);
|
||||
assert!(
|
||||
ConfigureChannelRequest {
|
||||
configuration_threshold: 3,
|
||||
..base.clone()
|
||||
}
|
||||
.validate()
|
||||
.is_err()
|
||||
);
|
||||
assert!(
|
||||
ConfigureChannelRequest {
|
||||
withdraw_threshold: 3,
|
||||
..base
|
||||
}
|
||||
.validate()
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,6 +204,7 @@ impl<BC: BlockPublisherTrait + Send + Sync + 'static> sequencer_service_rpc::Rpc
|
||||
&self,
|
||||
request: ConfigureChannelRequest,
|
||||
) -> Result<(), ErrorObjectOwned> {
|
||||
request.validate().map_err(invalid_params)?;
|
||||
let keys = request
|
||||
.keys
|
||||
.iter()
|
||||
@ -234,16 +235,17 @@ fn internal_error(err: &DbError) -> ErrorObjectOwned {
|
||||
ErrorObjectOwned::owned(ErrorCode::InternalError.code(), err.to_string(), None::<()>)
|
||||
}
|
||||
|
||||
fn invalid_params(detail: String) -> ErrorObjectOwned {
|
||||
ErrorObjectOwned::owned(ErrorCode::InvalidParams.code(), detail, 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}")))?;
|
||||
.map_err(|err| invalid_params(format!("Invalid hex-encoded key: {err}")))?;
|
||||
Ed25519PublicKey::from_bytes(&bytes)
|
||||
.map_err(|err| invalid(format!("Invalid Ed25519 public key: {err}")))
|
||||
.map_err(|err| invalid_params(format!("Invalid Ed25519 public key: {err}")))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user