refactor(proposal): use id newtypes across the governance path

This commit is contained in:
CleverWild
2026-08-27 13:42:14 +02:00
parent 8421a09d9f
commit 71081b6ee7
12 changed files with 127 additions and 84 deletions

View File

@@ -8,7 +8,8 @@ use crate::{
self,
models::{
NewProposal, NewProposalVote, NewRecoveryProposalVote, NewRecoveryWakeupRequest,
Proposal, ProposalStatus, SqliteTimestamp,
OperatorIdentityId, Proposal, ProposalId, ProposalStatus, RecoveryOperatorIdentityId,
SqliteTimestamp,
},
proposal::{ProposalKind, ProposalKindTag, one_off_transaction, persistent_grant},
schema,
@@ -65,9 +66,9 @@ pub enum Error {
#[derive(Debug)]
pub struct ProposalSummary {
pub id: i32,
pub id: ProposalId,
pub kind: ProposalKindTag,
pub initiator_id: i32,
pub initiator_id: OperatorIdentityId,
pub expires_at: SqliteTimestamp,
pub approve_count: i64,
pub reject_count: i64,
@@ -103,9 +104,9 @@ impl ProposalManager {
pub async fn create_proposal(
&mut self,
kind: ProposalKind,
initiator_id: i32,
initiator_id: OperatorIdentityId,
ttl_secs: Option<u32>,
) -> Result<i32, Error> {
) -> Result<ProposalId, Error> {
let ttl = ttl_secs.unwrap_or(DEFAULT_TTL_SECS);
if ttl > MAX_TTL_SECS {
return Err(Error::TtlTooLong);
@@ -113,12 +114,12 @@ impl ProposalManager {
let expires_at =
SqliteTimestamp::from(Utc::now() + chrono::Duration::seconds(i64::from(ttl)));
let id: i32 = self
let id: ProposalId = self
.db
.get()
.await?
.transaction(async |conn| {
let id: i32 = diesel::insert_into(schema::proposal::table)
let id: ProposalId = diesel::insert_into(schema::proposal::table)
.values(&NewProposal {
kind: kind.discriminant(),
initiator_id,
@@ -136,7 +137,7 @@ impl ProposalManager {
}
#[message]
pub async fn query_pending(&mut self, operator_id: i32) -> Vec<ProposalSummary> {
pub async fn query_pending(&mut self, operator_id: OperatorIdentityId) -> Vec<ProposalSummary> {
#[expect(
clippy::cast_possible_truncation,
clippy::as_conversions,
@@ -149,7 +150,7 @@ impl ProposalManager {
return vec![];
};
let voted_ids: Vec<i32> = schema::proposal_vote::table
let voted_ids: Vec<ProposalId> = schema::proposal_vote::table
.filter(schema::proposal_vote::operator_id.eq(operator_id))
.select(schema::proposal_vote::proposal_id)
.load(&mut conn)
@@ -195,8 +196,8 @@ impl ProposalManager {
#[message]
pub async fn cast_vote(
&mut self,
proposal_id: i32,
operator_id: i32,
proposal_id: ProposalId,
operator_id: OperatorIdentityId,
approve: bool,
signature: Vec<u8>,
) -> Result<VoteOutcome, Error> {
@@ -249,7 +250,7 @@ impl ProposalManager {
// Canonical vote message: proposal_id (i64 big-endian) || approve (u8)
let mut vote_msg = Vec::with_capacity(9);
vote_msg.extend_from_slice(&i64::from(proposal_id).to_be_bytes());
vote_msg.extend_from_slice(&i64::from(proposal_id.to_raw()).to_be_bytes());
vote_msg.push(u8::from(approve));
let auth_sig = authn::Signature::try_from(signature.as_slice())
@@ -358,7 +359,10 @@ impl ProposalManager {
/// §3.6: Any ordinary operator may request recovery wake-up.
/// Fails if a wake-up is already pending or active.
#[message]
pub async fn request_recovery_wakeup(&mut self, operator_id: i32) -> Result<(), Error> {
pub async fn request_recovery_wakeup(
&mut self,
operator_id: OperatorIdentityId,
) -> Result<(), Error> {
let mut conn = self.db.get().await?;
if Self::has_uncancelled_wakeup(&mut conn).await? {
return Err(Error::WakeupAlreadyPending);
@@ -375,7 +379,10 @@ impl ProposalManager {
/// §3.6: Any ordinary operator may cancel a pending wake-up request.
/// Fails if there is no uncancelled request.
#[message]
pub async fn cancel_recovery_wakeup(&mut self, operator_id: i32) -> Result<(), Error> {
pub async fn cancel_recovery_wakeup(
&mut self,
operator_id: OperatorIdentityId,
) -> Result<(), Error> {
let mut conn = self.db.get().await?;
let rows_updated = diesel::update(schema::recovery_wakeup_request::table)
.filter(schema::recovery_wakeup_request::cancelled_at.is_null())
@@ -396,8 +403,8 @@ impl ProposalManager {
#[message]
pub async fn cast_recovery_vote(
&mut self,
proposal_id: i32,
recovery_operator_id: i32,
proposal_id: ProposalId,
recovery_operator_id: RecoveryOperatorIdentityId,
approve: bool,
signature: Vec<u8>,
) -> Result<VoteOutcome, Error> {
@@ -454,7 +461,7 @@ impl ProposalManager {
.map_err(|()| Error::InvalidSignature)?;
let mut vote_msg = Vec::with_capacity(9);
vote_msg.extend_from_slice(&i64::from(proposal_id).to_be_bytes());
vote_msg.extend_from_slice(&i64::from(proposal_id.to_raw()).to_be_bytes());
vote_msg.push(u8::from(approve));
let auth_sig = authn::Signature::try_from(signature.as_slice())
@@ -617,7 +624,7 @@ impl ProposalManager {
/// removes their old Shamir share, then begins a coordinated re-key (§3.3).
async fn execute_replace_operator(
&self,
old_operator_id: i32,
old_operator_id: OperatorIdentityId,
new_pubkey: Vec<u8>,
) -> Result<(), Error> {
let mut conn = self.db.get().await.map_err(Error::DatabaseConnection)?;
@@ -657,7 +664,7 @@ impl ProposalManager {
async fn execute_approve_one_off_transaction(
&self,
proposal_id: i32,
proposal_id: ProposalId,
tx: one_off_transaction::Settings,
) -> Result<(), Error> {
use crate::actors::evm::ClientSignTransaction;

View File

@@ -143,6 +143,8 @@ pub mod types {
declare_id!(TlsHistoryId);
declare_id!(EvmWalletId);
declare_id!(ClientId);
declare_id!(ProposalId);
declare_id!(RecoveryOperatorIdentityId);
#[derive(Debug, Clone, PartialEq, Eq, AsExpression, FromSqlRow)]
#[diesel(sql_type = Text)]
@@ -478,9 +480,9 @@ pub struct IntegrityEnvelope {
#[derive(Debug, Queryable, Selectable, Identifiable)]
#[diesel(table_name = schema::proposal, check_for_backend(Sqlite))]
pub struct Proposal {
pub id: i32,
pub id: ProposalId,
pub kind: ProposalKindTag,
pub initiator_id: i32,
pub initiator_id: OperatorIdentityId,
pub created_at: SqliteTimestamp,
pub expires_at: SqliteTimestamp,
pub status: ProposalStatus,
@@ -490,7 +492,7 @@ pub struct Proposal {
#[diesel(table_name = schema::proposal, check_for_backend(Sqlite))]
pub struct NewProposal {
pub kind: ProposalKindTag,
pub initiator_id: i32,
pub initiator_id: OperatorIdentityId,
// status defaults to 'pending' at the DB layer
pub expires_at: SqliteTimestamp,
}
@@ -499,8 +501,8 @@ pub struct NewProposal {
#[diesel(table_name = schema::proposal_vote, check_for_backend(Sqlite))]
pub struct ProposalVote {
pub id: i32,
pub proposal_id: i32,
pub operator_id: i32,
pub proposal_id: ProposalId,
pub operator_id: OperatorIdentityId,
pub approve: bool,
pub signature: Vec<u8>,
pub voted_at: SqliteTimestamp,
@@ -509,8 +511,8 @@ pub struct ProposalVote {
#[derive(Debug, Insertable)]
#[diesel(table_name = schema::proposal_vote, check_for_backend(Sqlite))]
pub struct NewProposalVote {
pub proposal_id: i32,
pub operator_id: i32,
pub proposal_id: ProposalId,
pub operator_id: OperatorIdentityId,
pub approve: bool,
pub signature: Vec<u8>,
}
@@ -518,8 +520,8 @@ pub struct NewProposalVote {
#[derive(Debug, Insertable)]
#[diesel(table_name = schema::recovery_proposal_vote, check_for_backend(Sqlite))]
pub struct NewRecoveryProposalVote {
pub proposal_id: i32,
pub recovery_operator_id: i32,
pub proposal_id: ProposalId,
pub recovery_operator_id: RecoveryOperatorIdentityId,
pub approve: bool,
pub signature: Vec<u8>,
}
@@ -527,5 +529,5 @@ pub struct NewRecoveryProposalVote {
#[derive(Debug, Insertable)]
#[diesel(table_name = schema::recovery_wakeup_request, check_for_backend(Sqlite))]
pub struct NewRecoveryWakeupRequest {
pub requested_by: i32,
pub requested_by: OperatorIdentityId,
}

View File

@@ -1,7 +1,9 @@
//! Approving an SDK client so it may authenticate against the vault.
use super::{Proposal, ProposalKindTag};
use crate::db::{DatabaseConnection, schema::proposal_approve_sdk_client as table};
use crate::db::{
DatabaseConnection, models::ProposalId, schema::proposal_approve_sdk_client as table,
};
use diesel::{
ExpressionMethods as _, Insertable, QueryDsl as _, QueryResult, Queryable, Selectable,
SelectableHelper as _, sqlite::Sqlite,
@@ -22,7 +24,7 @@ impl Proposal for ApproveSdkClient {
type Settings = Settings;
async fn insert(
proposal_id: i32,
proposal_id: ProposalId,
settings: &Self::Settings,
conn: &mut DatabaseConnection,
) -> QueryResult<()> {
@@ -33,7 +35,10 @@ impl Proposal for ApproveSdkClient {
.map(drop)
}
async fn load(proposal_id: i32, conn: &mut DatabaseConnection) -> QueryResult<Self::Settings> {
async fn load(
proposal_id: ProposalId,
conn: &mut DatabaseConnection,
) -> QueryResult<Self::Settings> {
table::table
.find(proposal_id)
.select(Settings::as_select())

View File

@@ -1,7 +1,9 @@
//! Granting an SDK client visibility of a wallet.
use super::{Proposal, ProposalKindTag};
use crate::db::{DatabaseConnection, schema::proposal_grant_wallet_access as table};
use crate::db::{
DatabaseConnection, models::ProposalId, schema::proposal_grant_wallet_access as table,
};
use diesel::{
ExpressionMethods as _, Insertable, QueryDsl as _, QueryResult, Queryable, Selectable,
SelectableHelper as _, sqlite::Sqlite,
@@ -23,7 +25,7 @@ impl Proposal for GrantWalletAccess {
type Settings = Settings;
async fn insert(
proposal_id: i32,
proposal_id: ProposalId,
settings: &Self::Settings,
conn: &mut DatabaseConnection,
) -> QueryResult<()> {
@@ -34,7 +36,10 @@ impl Proposal for GrantWalletAccess {
.map(drop)
}
async fn load(proposal_id: i32, conn: &mut DatabaseConnection) -> QueryResult<Self::Settings> {
async fn load(
proposal_id: ProposalId,
conn: &mut DatabaseConnection,
) -> QueryResult<Self::Settings> {
table::table
.find(proposal_id)
.select(Settings::as_select())

View File

@@ -6,7 +6,7 @@
//! new kind is a new module plus one arm in each dispatcher -- nothing else in the
//! codebase has to learn about it.
use crate::db::DatabaseConnection;
use crate::db::{DatabaseConnection, models::ProposalId};
use diesel::{
QueryResult,
backend::Backend,
@@ -42,7 +42,7 @@ pub trait Proposal: Sized {
/// Writes the child row carrying `settings`.
fn insert(
proposal_id: i32,
proposal_id: ProposalId,
settings: &Self::Settings,
conn: &mut DatabaseConnection,
) -> impl Future<Output = QueryResult<()>> + Send;
@@ -50,7 +50,7 @@ pub trait Proposal: Sized {
/// Reads the child row back. A missing row surfaces as [`diesel::result::Error::NotFound`],
/// which is what a proposal without its parameters is.
fn load(
proposal_id: i32,
proposal_id: ProposalId,
conn: &mut DatabaseConnection,
) -> impl Future<Output = QueryResult<Self::Settings>> + Send;
}
@@ -126,7 +126,7 @@ const _: () = {
/// to the implementation that owns the table.
pub async fn insert_kind(
conn: &mut DatabaseConnection,
proposal_id: i32,
proposal_id: ProposalId,
kind: &ProposalKind,
) -> QueryResult<()> {
match kind {
@@ -146,7 +146,7 @@ pub async fn insert_kind(
/// Reads the parameters back for a `proposal.kind` that is only known at runtime.
pub async fn load_kind(
conn: &mut DatabaseConnection,
proposal_id: i32,
proposal_id: ProposalId,
tag: ProposalKindTag,
) -> QueryResult<ProposalKind> {
Ok(match tag {

View File

@@ -3,6 +3,7 @@
use super::{Proposal, ProposalKindTag, as_i64, as_u64, fixed};
use crate::db::{
DatabaseConnection,
models::ProposalId,
schema::{proposal_one_off_transaction, proposal_one_off_transaction_result},
};
use diesel::{
@@ -28,7 +29,7 @@ pub struct Settings {
#[derive(Debug, Queryable, Selectable, Insertable)]
#[diesel(table_name = proposal_one_off_transaction, check_for_backend(Sqlite))]
struct Row {
proposal_id: i32,
proposal_id: ProposalId,
client_id: i32,
wallet_address: Vec<u8>,
chain_id: i64,
@@ -42,7 +43,7 @@ struct Row {
}
impl Row {
fn new(proposal_id: i32, settings: &Settings) -> QueryResult<Self> {
fn new(proposal_id: ProposalId, settings: &Settings) -> QueryResult<Self> {
Ok(Self {
proposal_id,
client_id: settings.client_id,
@@ -82,7 +83,7 @@ impl Proposal for OneOffTransaction {
type Settings = Settings;
async fn insert(
proposal_id: i32,
proposal_id: ProposalId,
settings: &Self::Settings,
conn: &mut DatabaseConnection,
) -> QueryResult<()> {
@@ -93,7 +94,10 @@ impl Proposal for OneOffTransaction {
.map(drop)
}
async fn load(proposal_id: i32, conn: &mut DatabaseConnection) -> QueryResult<Self::Settings> {
async fn load(
proposal_id: ProposalId,
conn: &mut DatabaseConnection,
) -> QueryResult<Self::Settings> {
let row: Row = proposal_one_off_transaction::table
.find(proposal_id)
.select(Row::as_select())
@@ -108,7 +112,7 @@ impl Proposal for OneOffTransaction {
#[derive(Debug, Insertable)]
#[diesel(table_name = proposal_one_off_transaction_result, check_for_backend(Sqlite))]
struct SignatureRow {
proposal_id: i32,
proposal_id: ProposalId,
r: Vec<u8>,
s: Vec<u8>,
y_parity: i32,
@@ -117,7 +121,7 @@ struct SignatureRow {
/// Records the signature produced for an approved transaction, by component, so what
/// came back is as readable as what was signed.
pub async fn store_signature(
proposal_id: i32,
proposal_id: ProposalId,
signature: &alloy::signers::Signature,
conn: &mut DatabaseConnection,
) -> QueryResult<()> {

View File

@@ -2,6 +2,7 @@
use super::{Proposal, ProposalKindTag, as_i64, as_u64, fixed};
use crate::db::{
DatabaseConnection,
models::ProposalId,
schema::{
proposal_persistent_grant, proposal_persistent_grant_ether,
proposal_persistent_grant_ether_target, proposal_persistent_grant_token,
@@ -55,7 +56,7 @@ pub enum Specific {
#[derive(Debug, Queryable, Selectable, Insertable)]
#[diesel(table_name = proposal_persistent_grant, check_for_backend(Sqlite))]
struct BaseRow {
proposal_id: i32,
proposal_id: ProposalId,
wallet_access_id: i32,
chain_id: i64,
valid_from: Option<i64>,
@@ -69,7 +70,7 @@ struct BaseRow {
#[derive(Debug, Queryable, Selectable, Insertable)]
#[diesel(table_name = proposal_persistent_grant_ether, check_for_backend(Sqlite))]
struct EtherRow {
proposal_id: i32,
proposal_id: ProposalId,
window_secs: i64,
max_volume: Vec<u8>,
}
@@ -77,14 +78,14 @@ struct EtherRow {
#[derive(Debug, Insertable)]
#[diesel(table_name = proposal_persistent_grant_ether_target, check_for_backend(Sqlite))]
struct NewEtherTarget {
proposal_id: i32,
proposal_id: ProposalId,
address: Vec<u8>,
}
#[derive(Debug, Queryable, Selectable, Insertable)]
#[diesel(table_name = proposal_persistent_grant_token, check_for_backend(Sqlite))]
struct TokenRow {
proposal_id: i32,
proposal_id: ProposalId,
token_contract: Vec<u8>,
receiver: Option<Vec<u8>>,
}
@@ -92,13 +93,13 @@ struct TokenRow {
#[derive(Debug, Insertable)]
#[diesel(table_name = proposal_persistent_grant_token_limit, check_for_backend(Sqlite))]
struct NewTokenLimit {
proposal_id: i32,
proposal_id: ProposalId,
window_secs: i64,
max_volume: Vec<u8>,
}
impl BaseRow {
fn new(proposal_id: i32, settings: &Settings) -> QueryResult<Self> {
fn new(proposal_id: ProposalId, settings: &Settings) -> QueryResult<Self> {
Ok(Self {
proposal_id,
wallet_access_id: settings.wallet_access_id,
@@ -141,7 +142,7 @@ impl Proposal for PersistentGrant {
type Settings = Settings;
async fn insert(
proposal_id: i32,
proposal_id: ProposalId,
settings: &Self::Settings,
conn: &mut DatabaseConnection,
) -> QueryResult<()> {
@@ -201,7 +202,10 @@ impl Proposal for PersistentGrant {
Ok(())
}
async fn load(proposal_id: i32, conn: &mut DatabaseConnection) -> QueryResult<Self::Settings> {
async fn load(
proposal_id: ProposalId,
conn: &mut DatabaseConnection,
) -> QueryResult<Self::Settings> {
let base: BaseRow = proposal_persistent_grant::table
.find(proposal_id)
.select(BaseRow::as_select())

View File

@@ -1,7 +1,11 @@
//! Replacing an operator's key, which also triggers a Shamir re-key (§3.3).
use super::{Proposal, ProposalKindTag};
use crate::db::{DatabaseConnection, schema::proposal_replace_operator as table};
use crate::db::{
DatabaseConnection,
models::{OperatorIdentityId, ProposalId},
schema::proposal_replace_operator as table,
};
use diesel::{
ExpressionMethods as _, Insertable, QueryDsl as _, QueryResult, Queryable, Selectable,
SelectableHelper as _, sqlite::Sqlite,
@@ -11,7 +15,7 @@ use diesel_async::RunQueryDsl as _;
#[derive(Debug, Clone, PartialEq, Eq, Queryable, Selectable, Insertable)]
#[diesel(table_name = table, check_for_backend(Sqlite))]
pub struct Settings {
pub old_operator_id: i32,
pub old_operator_id: OperatorIdentityId,
pub new_pubkey: Vec<u8>,
}
@@ -23,7 +27,7 @@ impl Proposal for ReplaceOperator {
type Settings = Settings;
async fn insert(
proposal_id: i32,
proposal_id: ProposalId,
settings: &Self::Settings,
conn: &mut DatabaseConnection,
) -> QueryResult<()> {
@@ -34,7 +38,10 @@ impl Proposal for ReplaceOperator {
.map(drop)
}
async fn load(proposal_id: i32, conn: &mut DatabaseConnection) -> QueryResult<Self::Settings> {
async fn load(
proposal_id: ProposalId,
conn: &mut DatabaseConnection,
) -> QueryResult<Self::Settings> {
table::table
.find(proposal_id)
.select(Settings::as_select())

View File

@@ -1,7 +1,7 @@
//! A Shamir re-key over the current operator set (§3.3).
use super::{Proposal, ProposalKindTag};
use crate::db::DatabaseConnection;
use crate::db::{DatabaseConnection, models::ProposalId};
use diesel::QueryResult;
pub struct TriggerRekey;
@@ -12,7 +12,7 @@ impl Proposal for TriggerRekey {
type Settings = ();
async fn insert(
_proposal_id: i32,
_proposal_id: ProposalId,
_settings: &Self::Settings,
_conn: &mut DatabaseConnection,
) -> QueryResult<()> {
@@ -20,7 +20,7 @@ impl Proposal for TriggerRekey {
}
async fn load(
_proposal_id: i32,
_proposal_id: ProposalId,
_conn: &mut DatabaseConnection,
) -> QueryResult<Self::Settings> {
Ok(())

View File

@@ -1,5 +1,6 @@
use crate::{
actors::proposal_manager::{Error as ProposalError, VoteOutcome},
db::models::{OperatorIdentityId, ProposalId},
db::proposal::{
ProposalKind, approve_sdk_client, grant_wallet_access, one_off_transaction,
persistent_grant, replace_operator,
@@ -62,7 +63,7 @@ async fn handle_create(
}
Some(ProtoKind::ReplaceOperator(p)) => {
ProposalKind::ReplaceOperator(replace_operator::Settings {
old_operator_id: p.old_operator_id,
old_operator_id: OperatorIdentityId::from_raw(p.old_operator_id),
new_pubkey: p.new_pubkey,
})
}
@@ -87,7 +88,9 @@ async fn handle_create(
})?;
Ok(Some(wrap(GovResponsePayload::Created(
proto_gov::CreateProposalResponse { proposal_id },
proto_gov::CreateProposalResponse {
proposal_id: proposal_id.to_raw(),
},
))))
}
@@ -195,7 +198,7 @@ async fn handle_vote(
) -> Result<Option<OperatorResponsePayload>, Status> {
let result = actor
.ask(HandleCastVote {
proposal_id: req.proposal_id,
proposal_id: ProposalId::from_raw(req.proposal_id),
approve: req.approve,
signature: req.signature,
})
@@ -235,9 +238,9 @@ async fn handle_query(
let proposals = summaries
.into_iter()
.map(|s| proto_gov::ProposalSummary {
id: s.id,
id: s.id.to_raw(),
kind: <&'static str>::from(s.kind).to_owned(),
initiator_id: s.initiator_id,
initiator_id: s.initiator_id.to_raw(),
expires_at: s.expires_at.0.timestamp(),
approve_count: s.approve_count,
reject_count: s.reject_count,

View File

@@ -1,4 +1,5 @@
use super::{Error, OperatorSession};
use crate::db::models::{OperatorIdentityId, ProposalId};
use crate::{
actors::{
evm::{
@@ -287,9 +288,9 @@ impl OperatorSession {
&mut self,
kind: crate::db::proposal::ProposalKind,
ttl_secs: Option<u32>,
) -> Result<i32, Error> {
) -> Result<ProposalId, Error> {
use crate::actors::proposal_manager::CreateProposal;
let initiator_id = self.credentials.id;
let initiator_id = OperatorIdentityId::from_raw(self.credentials.id);
self.props
.actors
.proposal_manager
@@ -304,12 +305,12 @@ impl OperatorSession {
#[message]
pub(crate) async fn handle_cast_vote(
&mut self,
proposal_id: i32,
proposal_id: ProposalId,
approve: bool,
signature: Vec<u8>,
) -> Result<crate::actors::proposal_manager::VoteOutcome, crate::actors::proposal_manager::Error> {
use crate::actors::proposal_manager::CastVote;
let operator_id = self.credentials.id;
let operator_id = OperatorIdentityId::from_raw(self.credentials.id);
self.props
.actors
.proposal_manager
@@ -326,7 +327,7 @@ impl OperatorSession {
&mut self,
) -> Vec<crate::actors::proposal_manager::ProposalSummary> {
use crate::actors::proposal_manager::QueryPending;
let operator_id = self.credentials.id;
let operator_id = OperatorIdentityId::from_raw(self.credentials.id);
self.props
.actors
.proposal_manager

View File

@@ -10,6 +10,7 @@ use arbiter_server::{
crypto::KeyCell,
db::{
self,
models::{OperatorIdentityId, ProposalId, RecoveryOperatorIdentityId},
proposal::{
ProposalKind, approve_sdk_client, grant_wallet_access, one_off_transaction,
persistent_grant, replace_operator,
@@ -24,41 +25,45 @@ use arbiter_server::db::schema::{
use diesel::{ExpressionMethods, QueryDsl, insert_into};
use diesel_async::RunQueryDsl;
async fn register_operator(db: &db::DatabasePool, pubkey: &authn::PublicKey) -> i32 {
async fn register_operator(db: &db::DatabasePool, pubkey: &authn::PublicKey) -> OperatorIdentityId {
let mut conn = db.get().await.unwrap();
insert_into(operator_identity::table)
.values(operator_identity::public_key.eq(pubkey.to_bytes()))
.returning(operator_identity::id)
.get_result::<i32>(&mut conn)
.get_result::<OperatorIdentityId>(&mut conn)
.await
.unwrap()
}
async fn register_recovery_operator(db: &db::DatabasePool, pubkey: &authn::PublicKey) -> i32 {
async fn register_recovery_operator(
db: &db::DatabasePool,
pubkey: &authn::PublicKey,
) -> RecoveryOperatorIdentityId {
let mut conn = db.get().await.unwrap();
insert_into(recovery_operator_identity::table)
.values(recovery_operator_identity::public_key.eq(pubkey.to_bytes()))
.returning(recovery_operator_identity::id)
.get_result::<i32>(&mut conn)
.get_result::<RecoveryOperatorIdentityId>(&mut conn)
.await
.unwrap()
}
/// Backdates a wakeup request so it appears to have passed the 14-day window.
async fn insert_active_wakeup(db: &db::DatabasePool, operator_id: i32) {
async fn insert_active_wakeup(db: &db::DatabasePool, operator_id: OperatorIdentityId) {
let mut conn = db.get().await.unwrap();
diesel::sql_query(format!(
"INSERT INTO recovery_wakeup_request (requested_by, requested_at) \
VALUES ({operator_id}, unixepoch('now') - 14*24*3600 - 1)"
VALUES ({}, unixepoch('now') - 14*24*3600 - 1)",
operator_id.to_raw()
))
.execute(&mut conn)
.await
.unwrap();
}
fn make_vote_message(proposal_id: i32, approve: bool) -> Vec<u8> {
fn make_vote_message(proposal_id: ProposalId, approve: bool) -> Vec<u8> {
let mut msg = Vec::with_capacity(9);
msg.extend_from_slice(&(proposal_id as i64).to_be_bytes());
msg.extend_from_slice(&i64::from(proposal_id.to_raw()).to_be_bytes());
msg.push(u8::from(approve));
msg
}
@@ -128,13 +133,13 @@ async fn create_proposal_returns_id() {
.proposal_manager
.ask(CreateProposal {
kind: ProposalKind::ApproveSdkClient(approve_sdk_client::Settings { client_id: 42 }),
initiator_id: 1,
initiator_id: OperatorIdentityId::from_raw(1),
ttl_secs: None,
})
.await
.unwrap();
assert!(proposal_id > 0);
assert!(proposal_id.to_raw() > 0);
}
#[tokio::test]
@@ -935,7 +940,7 @@ async fn key_rotation_requires_full_quorum() {
.proposal_manager
.ask(CreateProposal {
kind: ProposalKind::ReplaceOperator(replace_operator::Settings {
old_operator_id: 1,
old_operator_id: OperatorIdentityId::from_raw(1),
new_pubkey,
}),
initiator_id: op1,
@@ -983,7 +988,7 @@ async fn recovery_vote_rejected_when_sleeping() {
.proposal_manager
.ask(CreateProposal {
kind: ProposalKind::ReplaceOperator(replace_operator::Settings {
old_operator_id: 1,
old_operator_id: OperatorIdentityId::from_raw(1),
new_pubkey,
}),
initiator_id: op_id,
@@ -1137,7 +1142,7 @@ async fn recovery_operator_vote_contributes_to_replace_quorum() {
.proposal_manager
.ask(CreateProposal {
kind: ProposalKind::ReplaceOperator(replace_operator::Settings {
old_operator_id: 1,
old_operator_id: OperatorIdentityId::from_raw(1),
new_pubkey,
}),
initiator_id: op_id,