WIP: feat-shamir (old) #103
@@ -314,9 +314,17 @@ create table if not exists proposal_vote (
|
||||
) STRICT;
|
||||
|
||||
|
||||
create table if not exists proposal_result (
|
||||
proposal_id integer not null primary key references proposal(id) on delete cascade,
|
||||
data blob not null,
|
||||
-- The signature the vault produced for an approved transaction, by component.
|
||||
--
|
||||
-- secp256k1 signatures have three common encodings (Electrum v=27/28, raw parity,
|
||||
-- ERC-2098 compact); a single blob would not say which one it holds. `y_parity` is
|
||||
-- the raw bit -- add 27 to rebuild the Electrum form `Signature::as_bytes` emits.
|
||||
create table if not exists proposal_one_off_transaction_result (
|
||||
proposal_id integer not null primary key
|
||||
references proposal_one_off_transaction (proposal_id) on delete cascade,
|
||||
r blob not null check (length(r) = 32),
|
||||
s blob not null check (length(s) = 32),
|
||||
y_parity integer not null check (y_parity in (0, 1)),
|
||||
created_at integer not null default(unixepoch('now'))
|
||||
) STRICT;
|
||||
|
||||
|
||||
@@ -661,7 +661,6 @@ impl ProposalManager {
|
||||
tx: one_off_transaction::Settings,
|
||||
) -> Result<(), Error> {
|
||||
use crate::actors::evm::ClientSignTransaction;
|
||||
use crate::db::models::NewProposalResult;
|
||||
use alloy::{
|
||||
consensus::TxEip1559,
|
||||
eips::eip2930::AccessList,
|
||||
@@ -691,12 +690,7 @@ impl ProposalManager {
|
||||
.map_err(|e| Error::ExecutionFailed(format!("sign one-off tx: {e}")))?;
|
||||
|
||||
let mut conn = self.db.get().await.map_err(Error::DatabaseConnection)?;
|
||||
diesel::insert_into(schema::proposal_result::table)
|
||||
.values(NewProposalResult {
|
||||
proposal_id,
|
||||
data: sig.as_bytes().to_vec(),
|
||||
})
|
||||
.execute(&mut conn)
|
||||
one_off_transaction::store_signature(proposal_id, &sig, &mut conn)
|
||||
.await
|
||||
.map_err(|e| Error::ExecutionFailed(format!("store proposal result: {e}")))?;
|
||||
|
||||
|
||||
@@ -515,13 +515,6 @@ pub struct NewProposalVote {
|
||||
pub signature: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Insertable)]
|
||||
#[diesel(table_name = schema::proposal_result, check_for_backend(Sqlite))]
|
||||
pub struct NewProposalResult {
|
||||
pub proposal_id: i32,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Insertable)]
|
||||
#[diesel(table_name = schema::recovery_proposal_vote, check_for_backend(Sqlite))]
|
||||
pub struct NewRecoveryProposalVote {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
//! Signing a single EIP-1559 transaction.
|
||||
|
||||
use super::{Proposal, ProposalKindTag, as_i64, as_u64, fixed};
|
||||
use crate::db::{DatabaseConnection, schema::proposal_one_off_transaction};
|
||||
use crate::db::{
|
||||
DatabaseConnection,
|
||||
schema::{proposal_one_off_transaction, proposal_one_off_transaction_result},
|
||||
};
|
||||
use diesel::{
|
||||
Insertable, QueryDsl as _, QueryResult, Queryable, Selectable, SelectableHelper as _,
|
||||
sqlite::Sqlite,
|
||||
@@ -100,3 +103,32 @@ impl Proposal for OneOffTransaction {
|
||||
row.into_settings()
|
||||
}
|
||||
}
|
||||
|
||||
/// The signature the vault produced for an approved transaction.
|
||||
#[derive(Debug, Insertable)]
|
||||
#[diesel(table_name = proposal_one_off_transaction_result, check_for_backend(Sqlite))]
|
||||
struct SignatureRow {
|
||||
proposal_id: i32,
|
||||
r: Vec<u8>,
|
||||
s: Vec<u8>,
|
||||
y_parity: i32,
|
||||
}
|
||||
|
||||
/// 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,
|
||||
signature: &alloy::signers::Signature,
|
||||
conn: &mut DatabaseConnection,
|
||||
) -> QueryResult<()> {
|
||||
diesel::insert_into(proposal_one_off_transaction_result::table)
|
||||
.values(&SignatureRow {
|
||||
proposal_id,
|
||||
r: signature.r().to_be_bytes::<32>().to_vec(),
|
||||
s: signature.s().to_be_bytes::<32>().to_vec(),
|
||||
y_parity: i32::from(signature.v()),
|
||||
})
|
||||
.execute(conn)
|
||||
.await
|
||||
.map(drop)
|
||||
}
|
||||
|
||||
@@ -270,9 +270,11 @@ diesel::table! {
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
proposal_result (proposal_id) {
|
||||
proposal_one_off_transaction_result (proposal_id) {
|
||||
proposal_id -> Integer,
|
||||
data -> Binary,
|
||||
r -> Binary,
|
||||
s -> Binary,
|
||||
y_parity -> Integer,
|
||||
created_at -> Integer,
|
||||
}
|
||||
}
|
||||
@@ -383,7 +385,7 @@ diesel::joinable!(evm_wallet_access -> program_client (client_id));
|
||||
diesel::joinable!(operator -> operator_identity (id));
|
||||
diesel::joinable!(program_client -> client_metadata (metadata_id));
|
||||
diesel::joinable!(proposal -> operator_identity (initiator_id));
|
||||
diesel::joinable!(proposal_result -> proposal (proposal_id));
|
||||
diesel::joinable!(proposal_one_off_transaction_result -> proposal_one_off_transaction (proposal_id));
|
||||
diesel::joinable!(proposal_approve_sdk_client -> proposal (proposal_id));
|
||||
diesel::joinable!(proposal_grant_wallet_access -> proposal (proposal_id));
|
||||
diesel::joinable!(proposal_replace_operator -> proposal (proposal_id));
|
||||
@@ -400,7 +402,7 @@ diesel::joinable!(recovery_wakeup_request -> operator_identity (requested_by));
|
||||
|
||||
diesel::allow_tables_to_appear_in_same_query!(
|
||||
aead_encrypted,
|
||||
proposal_result,
|
||||
proposal_one_off_transaction_result,
|
||||
proposal_approve_sdk_client,
|
||||
proposal_grant_wallet_access,
|
||||
proposal_replace_operator,
|
||||
|
||||
@@ -19,7 +19,7 @@ use arbiter_server::{
|
||||
use arbiter_server::actors::vault::Bootstrap;
|
||||
use arbiter_server::db::schema::{
|
||||
aead_encrypted, evm_basic_grant, evm_wallet, evm_wallet_access, operator_identity,
|
||||
proposal_result, recovery_operator_identity,
|
||||
proposal_one_off_transaction_result, recovery_operator_identity,
|
||||
};
|
||||
use diesel::{ExpressionMethods, QueryDsl, insert_into};
|
||||
use diesel_async::RunQueryDsl;
|
||||
@@ -790,13 +790,20 @@ async fn approve_one_off_transaction_stores_result() {
|
||||
assert_eq!(outcome, VoteOutcome::Approved);
|
||||
|
||||
let mut conn = db.get().await.unwrap();
|
||||
let count: i64 = proposal_result::table
|
||||
.filter(proposal_result::proposal_id.eq(proposal_id))
|
||||
.count()
|
||||
.get_result(&mut conn)
|
||||
let (r, s, y_parity): (Vec<u8>, Vec<u8>, i32) = proposal_one_off_transaction_result::table
|
||||
.find(proposal_id)
|
||||
.select((
|
||||
proposal_one_off_transaction_result::r,
|
||||
proposal_one_off_transaction_result::s,
|
||||
proposal_one_off_transaction_result::y_parity,
|
||||
))
|
||||
.first(&mut conn)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(count, 1);
|
||||
.expect("an approved transaction must leave its signature");
|
||||
|
||||
assert_eq!(r.len(), 32, "r must be a 32-byte scalar");
|
||||
assert_eq!(s.len(), 32, "s must be a 32-byte scalar");
|
||||
assert!(y_parity == 0 || y_parity == 1, "y_parity must be a bit");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
Reference in New Issue
Block a user