WIP: feat-shamir (old) #103
@@ -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,
|
||||
|
CleverWild marked this conversation as resolved
Outdated
|
||||
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;
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<()> {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user
ProposalManagershould focus on one thing, and one thing only, the vote coordination: not the outcome execution.Otherwise, this actor becomes too bloated, like your code currently is. Wigga.