From 8265281213e5a1049856c8cd4d9e8472eb919d0f Mon Sep 17 00:00:00 2001 From: CleverWild Date: Fri, 12 Jun 2026 19:43:17 +0200 Subject: [PATCH] feat(server::grpc): wire Shamir committee bootstrap and unseal proto messages Adds DeclareCommittee and ContributePassphrase variants to bootstrap.proto, ContributePassphrase to unseal.proto, and AwaitingContributions result codes to both. Implements corresponding inbound converters and outbound reply mappings. VaultGate handlers delegate to VaultCoordinator. --- .../src/grpc/operator/vault_gate/inbound.rs | 40 ++++++++++++-- .../src/grpc/operator/vault_gate/outbound.rs | 35 +++++++++++- .../src/peers/operator/vault_gate/mod.rs | 53 ++++++++++++++++++- 3 files changed, 121 insertions(+), 7 deletions(-) diff --git a/server/crates/arbiter-server/src/grpc/operator/vault_gate/inbound.rs b/server/crates/arbiter-server/src/grpc/operator/vault_gate/inbound.rs index 6a08235..b0955eb 100644 --- a/server/crates/arbiter-server/src/grpc/operator/vault_gate/inbound.rs +++ b/server/crates/arbiter-server/src/grpc/operator/vault_gate/inbound.rs @@ -1,14 +1,16 @@ use crate::{ grpc::{Convert, TryConvert}, peers::operator::vault_gate::{ - self as vault_gate, HandleBootstrapEncryptedKey, HandleHandshake, HandleUnsealEncryptedKey, + self as vault_gate, HandleBootstrapEncryptedKey, HandleContributeBootstrapPassphrase, + HandleContributeUnsealPassphrase, HandleDeclareCommittee, HandleHandshake, + HandleUnsealEncryptedKey, }, }; use arbiter_proto::proto::operator::{ operator_request::Payload as OperatorRequestPayload, vault::{ self as proto_vault, - bootstrap::{self as proto_bootstrap}, + bootstrap::{self as proto_bootstrap, request::Payload as BootstrapRequestPayload}, request::Payload as VaultRequestPayload, unseal::{self as proto_unseal, request::Payload as UnsealRequestPayload}, }, @@ -73,6 +75,13 @@ impl TryConvert for UnsealRequestPayload { match self { Self::Start(start) => start.try_convert(), Self::EncryptedKey(key) => Ok(key.convert()), + Self::ContributePassphrase(cp) => Ok( + vault_gate::Inbound::HandleContributeUnsealPassphrase( + HandleContributeUnsealPassphrase { + passphrase: cp.passphrase, + }, + ), + ), } } } @@ -107,12 +116,35 @@ impl TryConvert for proto_bootstrap::Request { type Error = Status; fn try_convert(self) -> Result { - self.encrypted_key - .ok_or_else(|| Status::invalid_argument("Missing bootstrap encrypted key"))? + self.payload + .ok_or_else(|| Status::invalid_argument("Missing bootstrap payload"))? .try_convert() } } +impl TryConvert for BootstrapRequestPayload { + type Output = vault_gate::Inbound; + type Error = Status; + + fn try_convert(self) -> Result { + match self { + Self::EncryptedKey(key) => key.try_convert(), + Self::DeclareCommittee(dc) => Ok( + vault_gate::Inbound::HandleDeclareCommittee(HandleDeclareCommittee { + count: dc.count as usize, + }), + ), + Self::ContributePassphrase(cp) => Ok( + vault_gate::Inbound::HandleContributeBootstrapPassphrase( + HandleContributeBootstrapPassphrase { + passphrase: cp.passphrase, + }, + ), + ), + } + } +} + impl TryConvert for proto_bootstrap::BootstrapEncryptedKey { type Output = vault_gate::Inbound; type Error = Status; diff --git a/server/crates/arbiter-server/src/grpc/operator/vault_gate/outbound.rs b/server/crates/arbiter-server/src/grpc/operator/vault_gate/outbound.rs index 268b7d5..1e44ca4 100644 --- a/server/crates/arbiter-server/src/grpc/operator/vault_gate/outbound.rs +++ b/server/crates/arbiter-server/src/grpc/operator/vault_gate/outbound.rs @@ -46,7 +46,6 @@ impl Convert for VaultState { fn convert(self) -> OperatorResponsePayload { let proto_state = match self { Self::Unbootstrapped => ProtoVaultState::Unbootstrapped, - Self::Bootstrapping => ProtoVaultState::Boostrapping, Self::Sealed => ProtoVaultState::Sealed, Self::Unsealed => ProtoVaultState::Unsealed, }; @@ -111,6 +110,40 @@ impl TryConvert for vault_gate::Outbound { }; Ok(wrap_bootstrap_response(proto_result)) } + Self::HandleDeclareCommittee(result) => { + let proto_result = match result { + Ok(()) => ProtoBootstrapResult::Success, + Err(err) => { + warn!(?err, "declare committee failed"); + return Err(Status::internal("Failed to declare committee")); + } + }; + Ok(wrap_bootstrap_response(proto_result)) + } + Self::HandleContributeBootstrapPassphrase(result) => { + let proto_result = match result { + Ok(true) => ProtoBootstrapResult::Success, + Ok(false) => ProtoBootstrapResult::AwaitingContributions, + Err(err) => { + warn!(?err, "contribute bootstrap passphrase failed"); + return Err(Status::internal("Failed to contribute bootstrap passphrase")); + } + }; + Ok(wrap_bootstrap_response(proto_result)) + } + Self::HandleContributeUnsealPassphrase(result) => { + let proto_result = match result { + Ok(true) => ProtoUnsealResult::Success, + Ok(false) => ProtoUnsealResult::AwaitingContributions, + Err(err) => { + warn!(?err, "contribute unseal passphrase failed"); + return Err(Status::internal("Failed to contribute unseal passphrase")); + } + }; + Ok(wrap_unseal_response(UnsealResponsePayload::Result( + proto_result.into(), + ))) + } } } } diff --git a/server/crates/arbiter-server/src/peers/operator/vault_gate/mod.rs b/server/crates/arbiter-server/src/peers/operator/vault_gate/mod.rs index 6a8a265..b74ff3e 100644 --- a/server/crates/arbiter-server/src/peers/operator/vault_gate/mod.rs +++ b/server/crates/arbiter-server/src/peers/operator/vault_gate/mod.rs @@ -3,6 +3,7 @@ use crate::{ actors::{ GlobalActors, vault::{self, Bootstrap, GetState, TryUnseal, VaultState, events}, + vault_coordinator::{ContributeBootstrap, ContributeUnseal, StartBootstrap}, }, crypto::integrity::{self}, db::DatabasePool, @@ -17,6 +18,9 @@ use tokio::sync::oneshot; use tracing::{error, info}; use x25519_dalek::{EphemeralSecret, PublicKey, SharedSecret}; +pub use VaultGateMessage as Inbound; +pub use VaultGateMessageReply as Outbound; + pub mod state; #[derive(Debug, thiserror::Error)] @@ -118,8 +122,7 @@ impl VaultGate { } } } - -#[messages(messages = Inbound, replies = Outbound)] +#[messages(enum)] impl VaultGate { #[message] pub fn handle_handshake( @@ -234,6 +237,52 @@ impl VaultGate { Ok(answer) } + + #[message] + pub async fn handle_declare_committee(&mut self, count: usize) -> Result<(), Error> { + self.actors + .vault_coordinator + .ask(StartBootstrap { + operator_id: self.auth_creds.id, + declared_count: count, + }) + .await + .map_err(|_| Error::internal("VaultCoordinator unavailable")) + } + + #[message] + pub async fn handle_contribute_bootstrap_passphrase( + &mut self, + passphrase: Vec, + ) -> Result { + use arbiter_crypto::safecell::SafeCell; + let passphrase_cell = SafeCell::new(passphrase); + self.actors + .vault_coordinator + .ask(ContributeBootstrap { + operator_id: self.auth_creds.id, + passphrase: passphrase_cell, + }) + .await + .map_err(|_| Error::internal("VaultCoordinator unavailable")) + } + + #[message] + pub async fn handle_contribute_unseal_passphrase( + &mut self, + passphrase: Vec, + ) -> Result { + use arbiter_crypto::safecell::SafeCell; + let passphrase_cell = SafeCell::new(passphrase); + self.actors + .vault_coordinator + .ask(ContributeUnseal { + operator_id: self.auth_creds.id, + passphrase: passphrase_cell, + }) + .await + .map_err(|_| Error::internal("VaultCoordinator unavailable")) + } } impl Message for VaultGate {