feat(server): ProposalKind::UpdateShamirParameters
This commit is contained in:
@@ -16,6 +16,7 @@ message CreateProposalRequest {
|
||||
GrantWalletAccessPayload grant_wallet_access = 3;
|
||||
ApproveServerUpdatePayload approve_server_update = 4;
|
||||
ReplaceOperatorPayload replace_operator = 5;
|
||||
UpdateShamirParametersPayload update_shamir_parameters = 6;
|
||||
}
|
||||
optional uint32 ttl_secs = 2;
|
||||
}
|
||||
@@ -24,6 +25,10 @@ message ReplaceOperatorPayload {
|
||||
bytes new_pubkey = 1;
|
||||
}
|
||||
|
||||
message UpdateShamirParametersPayload {
|
||||
uint32 new_n = 1;
|
||||
}
|
||||
|
||||
message ApproveServerUpdatePayload {}
|
||||
|
||||
message ApproveSdkClientPayload {
|
||||
|
||||
@@ -20,6 +20,7 @@ pub enum ProposalKind {
|
||||
GrantWalletAccess { wallet_id: i32, client_id: i32 },
|
||||
ApproveServerUpdate,
|
||||
ReplaceOperator { new_pubkey: Vec<u8> },
|
||||
UpdateShamirParameters { new_n: u8 },
|
||||
}
|
||||
|
||||
impl ProposalKind {
|
||||
@@ -29,6 +30,7 @@ impl ProposalKind {
|
||||
Self::GrantWalletAccess { .. } => "grant_wallet_access",
|
||||
Self::ApproveServerUpdate => "approve_server_update",
|
||||
Self::ReplaceOperator { .. } => "replace_operator",
|
||||
Self::UpdateShamirParameters { .. } => "update_shamir_parameters",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +52,7 @@ impl ProposalKind {
|
||||
buf.extend_from_slice(new_pubkey);
|
||||
buf
|
||||
}
|
||||
Self::UpdateShamirParameters { new_n } => vec![*new_n],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +87,12 @@ impl ProposalKind {
|
||||
new_pubkey: rest[..len].to_vec(),
|
||||
})
|
||||
}
|
||||
"update_shamir_parameters" => {
|
||||
let &[new_n] = payload else {
|
||||
return Err("invalid payload for update_shamir_parameters".to_owned());
|
||||
};
|
||||
Ok(Self::UpdateShamirParameters { new_n })
|
||||
}
|
||||
other => Err(format!("unknown proposal kind: {other}")),
|
||||
}
|
||||
}
|
||||
@@ -415,6 +424,9 @@ impl ProposalManager {
|
||||
ProposalKind::ReplaceOperator { new_pubkey } => {
|
||||
self.execute_replace_operator(new_pubkey).await
|
||||
}
|
||||
ProposalKind::UpdateShamirParameters { new_n } => {
|
||||
self.execute_update_shamir_parameters(new_n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,6 +457,16 @@ impl ProposalManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::unused_self,
|
||||
clippy::unnecessary_wraps,
|
||||
reason = "signature must match other execute_* methods"
|
||||
)]
|
||||
fn execute_update_shamir_parameters(&self, new_n: u8) -> Result<(), Error> {
|
||||
warn!(new_n, "UpdateShamirParameters approved; Shamir re-keying must be performed out-of-band");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn execute_approve_sdk_client(&self, client_id: i32) -> Result<(), Error> {
|
||||
use arbiter_crypto::authn;
|
||||
use crate::{
|
||||
|
||||
@@ -56,6 +56,10 @@ async fn handle_create(
|
||||
Some(ProtoKind::ReplaceOperator(p)) => ProposalKind::ReplaceOperator {
|
||||
new_pubkey: p.new_pubkey,
|
||||
},
|
||||
Some(ProtoKind::UpdateShamirParameters(p)) => ProposalKind::UpdateShamirParameters {
|
||||
#[expect(clippy::cast_possible_truncation, clippy::as_conversions, reason = "new_n is always a small operator count")]
|
||||
new_n: p.new_n as u8,
|
||||
},
|
||||
None => return Err(Status::invalid_argument("Missing proposal kind")),
|
||||
};
|
||||
let ttl_secs = req.ttl_secs.map(i64::from);
|
||||
|
||||
@@ -550,6 +550,45 @@ async fn replace_operator_inserts_identity_row() {
|
||||
assert_eq!(count, 2); // original + new
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_shamir_parameters_reaches_quorum() {
|
||||
let db = db::create_test_pool().await;
|
||||
let actors = GlobalActors::spawn(db.clone()).await.unwrap();
|
||||
actors
|
||||
.vault
|
||||
.ask(Bootstrap { seal_key: KeyCell::from([0u8; 32]) })
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let signing_key = authn::SigningKey::generate();
|
||||
let op_id = register_operator(&db, &signing_key.public_key()).await;
|
||||
|
||||
let proposal_id = actors
|
||||
.proposal_manager
|
||||
.ask(CreateProposal {
|
||||
kind: ProposalKind::UpdateShamirParameters { new_n: 5 },
|
||||
initiator_id: op_id,
|
||||
ttl_secs: None,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let msg = make_vote_message(proposal_id, true);
|
||||
let sig = signing_key.sign_message(&msg, GOVERNANCE_CONTEXT).unwrap();
|
||||
let outcome = actors
|
||||
.proposal_manager
|
||||
.ask(CastVote {
|
||||
proposal_id,
|
||||
operator_id: op_id,
|
||||
approve: true,
|
||||
signature: sig.to_bytes(),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(outcome, VoteOutcome::QuorumApproved);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn approve_server_update_reaches_quorum() {
|
||||
let db = db::create_test_pool().await;
|
||||
|
||||
Reference in New Issue
Block a user