Skip to content

Commit 9a808ef

Browse files
committed
feat: add sudo rename-challenge command
- Add RenameChallenge variant to SudoAction enum - Add rename_challenge() method to both ChainState implementations - Add 'rename' command to platform-sudo CLI - Handle rename action in RPC sudo handler and P2P message processing - Storage unaffected: uses UUID namespace, not name
1 parent 0929c4f commit 9a808ef

File tree

6 files changed

+169
-4
lines changed

6 files changed

+169
-4
lines changed

bins/platform-sudo/src/main.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ enum Commands {
5353
#[arg(short, long)]
5454
id: String,
5555
},
56+
/// Rename a challenge (storage unaffected - uses UUID)
57+
Rename {
58+
/// Challenge ID (UUID)
59+
#[arg(short, long)]
60+
id: String,
61+
/// New name for the challenge
62+
#[arg(short, long)]
63+
name: String,
64+
},
5665
/// List all challenges
5766
List,
5867
/// Show validator status
@@ -242,6 +251,47 @@ impl SudoCli {
242251
Ok(())
243252
}
244253

254+
async fn rename_challenge(&self, challenge_id: &str, new_name: &str) -> Result<()> {
255+
let timestamp = chrono::Utc::now().timestamp_millis();
256+
let challenge_id = ChallengeId::from_string(challenge_id);
257+
258+
// Sign the message
259+
let msg_to_sign = format!("sudo:rename:{}:{}:{}", challenge_id, new_name, timestamp);
260+
let signature = self.sign(msg_to_sign.as_bytes())?;
261+
262+
let request = SudoRequest {
263+
action: "rename".to_string(),
264+
challenge_id: challenge_id.to_string(),
265+
data: None,
266+
name: Some(new_name.to_string()),
267+
signature: hex::encode(&signature),
268+
timestamp,
269+
};
270+
271+
let response = self
272+
.client
273+
.post(format!("{}/sudo/challenge", self.rpc_url))
274+
.json(&request)
275+
.send()
276+
.await
277+
.context("Failed to send request")?;
278+
279+
if response.status().is_success() {
280+
let result: SudoResponse = response.json().await?;
281+
if result.success {
282+
info!(challenge_id = %challenge_id, new_name = new_name, "Challenge renamed");
283+
} else {
284+
warn!(message = %result.message, "Rename failed");
285+
}
286+
} else {
287+
let status = response.status();
288+
let text = response.text().await.unwrap_or_default();
289+
error!(status = %status, body = %text, "RPC request failed");
290+
}
291+
292+
Ok(())
293+
}
294+
245295
async fn list_challenges(&self) -> Result<()> {
246296
let response = self
247297
.client
@@ -390,6 +440,9 @@ async fn main() -> Result<()> {
390440
Some(Commands::Deactivate { id }) => {
391441
sudo_cli.set_challenge_status(&id, false).await?;
392442
}
443+
Some(Commands::Rename { id, name }) => {
444+
sudo_cli.rename_challenge(&id, &name).await?;
445+
}
393446
Some(Commands::List) => {
394447
sudo_cli.list_challenges().await?;
395448
}

bins/validator-node/src/main.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,24 @@ async fn main() -> Result<()> {
931931
}
932932
}
933933
} // end if is_valid
934+
} else if update_type == "rename" && !data.is_empty() {
935+
// Handle rename action
936+
if let Ok(new_name) = String::from_utf8(data) {
937+
let mut cs = chain_state.write();
938+
if cs.rename_challenge(&challenge_id, new_name.clone()) {
939+
info!(
940+
challenge_id = %challenge_id,
941+
new_name = %new_name,
942+
"Challenge renamed locally"
943+
);
944+
} else {
945+
warn!(
946+
challenge_id = %challenge_id,
947+
new_name = %new_name,
948+
"Failed to rename challenge - not found or name conflict"
949+
);
950+
}
951+
}
934952
}
935953
}
936954
}
@@ -1628,6 +1646,24 @@ async fn handle_network_event(
16281646
});
16291647
info!(challenge_id = %update.challenge_id, "Challenge deactivated");
16301648
}
1649+
"rename" => {
1650+
if let Ok(new_name) = String::from_utf8(update.data.clone()) {
1651+
let mut cs = chain_state.write();
1652+
if cs.rename_challenge(&update.challenge_id, new_name.clone()) {
1653+
info!(
1654+
challenge_id = %update.challenge_id,
1655+
new_name = %new_name,
1656+
"Challenge renamed via P2P"
1657+
);
1658+
} else {
1659+
warn!(
1660+
challenge_id = %update.challenge_id,
1661+
new_name = %new_name,
1662+
"Failed to rename challenge - not found or name conflict"
1663+
);
1664+
}
1665+
}
1666+
}
16311667
other => {
16321668
warn!(
16331669
challenge_id = %update.challenge_id,

crates/core/src/message.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,12 @@ pub enum SudoAction {
329329

330330
/// Force state update (for recovery)
331331
ForceStateUpdate { state: ChainState },
332+
333+
/// Rename a challenge (storage unaffected - uses UUID)
334+
RenameChallenge {
335+
challenge_id: ChallengeId,
336+
new_name: String,
337+
},
332338
}
333339

334340
/// Configuration for how weights are distributed on a mechanism

crates/core/src/state.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,36 @@ impl ChainState {
313313
self.challenge_routes.get(challenge_id)
314314
}
315315

316+
/// Rename a challenge (storage unaffected - uses UUID namespace)
317+
pub fn rename_challenge(&mut self, id: &ChallengeId, new_name: String) -> bool {
318+
// Check name uniqueness in wasm_challenge_configs
319+
let name_exists = self
320+
.wasm_challenge_configs
321+
.values()
322+
.any(|c| c.name == new_name && c.challenge_id != *id);
323+
if name_exists {
324+
return false;
325+
}
326+
327+
// Update in wasm_challenge_configs
328+
if let Some(config) = self.wasm_challenge_configs.get_mut(id) {
329+
tracing::info!(challenge_id = %id, old_name = %config.name, new_name = %new_name, "Renaming WASM challenge");
330+
config.name = new_name.clone();
331+
self.update_hash();
332+
return true;
333+
}
334+
335+
// Also check legacy challenges
336+
if let Some(config) = self.challenges.get_mut(id) {
337+
tracing::info!(challenge_id = %id, old_name = %config.name, new_name = %new_name, "Renaming legacy challenge");
338+
config.name = new_name;
339+
self.update_hash();
340+
return true;
341+
}
342+
343+
false
344+
}
345+
316346
/// Create a snapshot of the state
317347
pub fn snapshot(&self) -> StateSnapshot {
318348
StateSnapshot {

crates/p2p-consensus/src/state.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,28 @@ impl ChainState {
438438
}
439439
}
440440

441+
/// Rename a challenge (storage unaffected - uses UUID namespace)
442+
pub fn rename_challenge(&mut self, id: &ChallengeId, new_name: String) -> bool {
443+
// Check name uniqueness
444+
let name_exists = self
445+
.challenges
446+
.values()
447+
.any(|c| c.name == new_name && c.id != *id);
448+
if name_exists {
449+
return false;
450+
}
451+
452+
// Update challenge name
453+
if let Some(config) = self.challenges.get_mut(id) {
454+
info!(challenge_id = %id, old_name = %config.name, new_name = %new_name, "Renaming challenge");
455+
config.name = new_name;
456+
self.increment_sequence();
457+
return true;
458+
}
459+
460+
false
461+
}
462+
441463
/// Add a storage proposal
442464
pub fn add_storage_proposal(&mut self, proposal: StorageProposal) {
443465
info!(

crates/rpc-server/src/server.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -992,10 +992,21 @@ async fn sudo_challenge_handler(
992992
}
993993

994994
// Verify signature is from sudo key
995-
let message = format!(
996-
"sudo:{}:{}:{}",
997-
request.action, request.challenge_id, request.timestamp
998-
);
995+
// For rename action, include the new name in the message
996+
let message = if request.action == "rename" {
997+
format!(
998+
"sudo:{}:{}:{}:{}",
999+
request.action,
1000+
request.challenge_id,
1001+
request.name.as_deref().unwrap_or(""),
1002+
request.timestamp
1003+
)
1004+
} else {
1005+
format!(
1006+
"sudo:{}:{}:{}",
1007+
request.action, request.challenge_id, request.timestamp
1008+
)
1009+
};
9991010

10001011
let signature = match sp_core::sr25519::Signature::try_from(signature_bytes.as_slice()) {
10011012
Ok(sig) => sig,
@@ -1057,6 +1068,13 @@ async fn sudo_challenge_handler(
10571068
None => Vec::new(),
10581069
};
10591070

1071+
// For rename action, use the new name as data
1072+
let data = if request.action == "rename" {
1073+
request.name.clone().unwrap_or_default().into_bytes()
1074+
} else {
1075+
data
1076+
};
1077+
10601078
// Send broadcast command
10611079
let cmd = RpcP2PCommand::BroadcastChallengeUpdate {
10621080
challenge_id,

0 commit comments

Comments
 (0)