- Add `rekey.proto` with `ContributePassphrase` / `ContributeRecoveryPassphrase` / `RekeyResult`
- Wire `rekey` as a 4th vault stream payload in `vault.proto` and gRPC dispatch
- Add `RekeyRootKey` message to `Vault` actor: generates new random seal key, re-encrypts root key, writes new `root_key_history` row
- Add `StartRekey`, `ContributeRekey`, `ContributeRecoveryRekey` messages to `VaultCoordinator`; `finalize_rekey` uses threshold-1 fast path identical to bootstrap
- `execute_replace_operator` now UPDATEs `operator_identity.public_key` in-place (avoids FK constraint violation), deletes stale `operator` share row, then triggers `StartRekey`
- `execute_update_shamir_parameters` triggers `StartRekey` instead of warning stub
- `ProposalKind::ReplaceOperator` carries `old_operator_id`; encode/decode updated accordingly
- `GlobalActors::spawn` extracts `vault_coordinator` before `Ok(Self { … })` so it can be cloned into `ProposalManager::new`
- Add `handle_rekey` in session handlers forwarding passphrase contributions to `VaultCoordinator`
- Fix test: rename `replace_operator_inserts_identity_row` → `replace_operator_updates_pubkey_and_starts_rekey`, assert count stays 1 and pubkey is updated
77 lines
2.5 KiB
Rust
77 lines
2.5 KiB
Rust
use crate::{
|
|
actors::{
|
|
bootstrap::Bootstrapper, evm::EvmActor, flow_coordinator::FlowCoordinator,
|
|
operator_registry::OperatorRegistry, proposal_manager::ProposalManager, vault::Vault,
|
|
vault_coordinator::VaultCoordinator,
|
|
},
|
|
db,
|
|
};
|
|
|
|
use kameo::actor::{ActorRef, Spawn};
|
|
use kameo_actors::{DeliveryStrategy, message_bus::MessageBus};
|
|
use thiserror::Error;
|
|
|
|
pub mod bootstrap;
|
|
pub mod evm;
|
|
pub mod flow_coordinator;
|
|
pub mod operator_registry;
|
|
pub mod proposal_manager;
|
|
pub mod vault;
|
|
pub mod vault_coordinator;
|
|
|
|
#[derive(Error, Debug)]
|
|
pub enum SpawnError {
|
|
#[error("Failed to spawn Bootstrapper actor")]
|
|
Bootstrapper(#[from] bootstrap::Error),
|
|
|
|
#[error("Failed to spawn Vault actor")]
|
|
Vault(#[from] vault::Error),
|
|
}
|
|
|
|
/// Long-lived actors that are shared across all connections and handle global state and operations
|
|
#[derive(Clone)]
|
|
pub struct GlobalActors {
|
|
pub vault: ActorRef<Vault>,
|
|
pub bootstrapper: ActorRef<Bootstrapper>,
|
|
pub vault_coordinator: ActorRef<VaultCoordinator>,
|
|
pub flow_coordinator: ActorRef<FlowCoordinator>,
|
|
pub operator_registry: ActorRef<OperatorRegistry>,
|
|
pub evm: ActorRef<EvmActor>,
|
|
pub proposal_manager: ActorRef<ProposalManager>,
|
|
pub events: ActorRef<MessageBus>,
|
|
}
|
|
|
|
impl GlobalActors {
|
|
pub fn spawn_message_bus() -> ActorRef<MessageBus> {
|
|
MessageBus::spawn(MessageBus::new(DeliveryStrategy::Guaranteed))
|
|
}
|
|
|
|
pub async fn spawn(db: db::DatabasePool) -> Result<Self, SpawnError> {
|
|
let message_bus = Self::spawn_message_bus();
|
|
let key_holder = Vault::spawn(Vault::new(db.clone(), message_bus.clone()).await?);
|
|
let operator_registry = OperatorRegistry::spawn(OperatorRegistry::default());
|
|
let evm = EvmActor::spawn(EvmActor::new(key_holder.clone(), db.clone()));
|
|
let vault_coordinator = VaultCoordinator::spawn(VaultCoordinator::new(
|
|
db.clone(),
|
|
key_holder.clone(),
|
|
));
|
|
Ok(Self {
|
|
bootstrapper: Bootstrapper::spawn(Bootstrapper::new(&db).await?),
|
|
proposal_manager: ProposalManager::spawn(ProposalManager::new(
|
|
db,
|
|
key_holder.clone(),
|
|
evm.clone(),
|
|
vault_coordinator.clone(),
|
|
)),
|
|
vault: key_holder,
|
|
vault_coordinator,
|
|
flow_coordinator: FlowCoordinator::spawn(FlowCoordinator::new(
|
|
operator_registry.clone(),
|
|
)),
|
|
operator_registry,
|
|
events: message_bus,
|
|
evm,
|
|
})
|
|
}
|
|
}
|