WIP: feat-shamir (old) #103

Draft
CleverWild wants to merge 66 commits from feat-shamir into main
3 changed files with 58 additions and 34 deletions
Showing only changes of commit 291ef2e831 - Show all commits

View File

@@ -41,7 +41,10 @@ impl ProposalKind {
pub fn encode_payload(&self) -> Vec<u8> {
match self {
Self::ApproveSdkClient { client_id } => client_id.to_be_bytes().to_vec(),
Self::GrantWalletAccess { wallet_id, client_id } => {
Self::GrantWalletAccess {
wallet_id,
client_id,
CleverWild marked this conversation as resolved Outdated

Scope creep, remove this for now

Scope creep, remove this for now
} => {
let mut buf = Vec::with_capacity(8);
buf.extend_from_slice(&wallet_id.to_be_bytes());
buf.extend_from_slice(&client_id.to_be_bytes());
@@ -49,8 +52,7 @@ impl ProposalKind {
}
Self::ApproveServerUpdate => vec![],
Self::ReplaceOperator { new_pubkey } => {
#[expect(clippy::cast_possible_truncation, clippy::as_conversions, reason = "pubkey is always 32 bytes")]
let len = new_pubkey.len() as u32;
let len = u32::try_from(new_pubkey.len()).expect("pubkey len fits in u32");
let mut buf = Vec::with_capacity(4 + new_pubkey.len());
buf.extend_from_slice(&len.to_be_bytes());
buf.extend_from_slice(new_pubkey);
@@ -153,7 +155,11 @@ pub struct ProposalManager {
}
impl ProposalManager {
pub const fn new(db: db::DatabasePool, vault: ActorRef<Vault>, evm: ActorRef<EvmActor>) -> Self {
pub const fn new(
db: db::DatabasePool,
vault: ActorRef<Vault>,
evm: ActorRef<EvmActor>,
) -> Self {
Self { db, vault, evm }
}
}
@@ -162,10 +168,7 @@ impl kameo::Actor for ProposalManager {
type Args = Self;
type Error = ();
async fn on_start(
args: Self::Args,
actor_ref: ActorRef<Self>,
) -> Result<Self, Self::Error> {
async fn on_start(args: Self::Args, actor_ref: ActorRef<Self>) -> Result<Self, Self::Error> {
let weak = actor_ref.downgrade();
tokio::spawn(async move {
loop {
@@ -429,9 +432,10 @@ impl ProposalManager {
ProposalKind::ApproveSdkClient { client_id } => {
self.execute_approve_sdk_client(client_id).await
}
ProposalKind::GrantWalletAccess { wallet_id, client_id } => {
self.execute_grant_wallet_access(wallet_id, client_id).await
}
ProposalKind::GrantWalletAccess {
wallet_id,
client_id,
} => self.execute_grant_wallet_access(wallet_id, client_id).await,
ProposalKind::ApproveServerUpdate => Ok(()),
ProposalKind::ReplaceOperator { new_pubkey } => {
self.execute_replace_operator(new_pubkey).await
@@ -443,12 +447,17 @@ impl ProposalManager {
self.execute_approve_persistent_grant(payload_bytes).await
}
ProposalKind::ApproveOneOffTransaction { payload_bytes } => {
self.execute_approve_one_off_transaction(proposal.id, payload_bytes).await
self.execute_approve_one_off_transaction(proposal.id, payload_bytes)
.await
}
}
}
async fn execute_grant_wallet_access(&self, wallet_id: i32, client_id: i32) -> Result<(), Error> {
async fn execute_grant_wallet_access(
&self,
wallet_id: i32,
client_id: i32,
) -> Result<(), Error> {
use crate::db::models::EvmWalletId;
let mut conn = self.db.get().await.map_err(Error::DatabaseConnection)?;
@@ -481,7 +490,10 @@ impl ProposalManager {
reason = "signature must match other execute_* methods"
)]
fn execute_update_shamir_parameters(&self, new_n: u8) -> Result<(), Error> {
warn!(new_n, "UpdateShamirParameters approved; Shamir re-keying must be performed out-of-band");
warn!(
new_n,
"UpdateShamirParameters approved; Shamir re-keying must be performed out-of-band"
);
Ok(())
}
@@ -490,7 +502,6 @@ impl ProposalManager {
proposal_id: i32,
payload_bytes: Vec<u8>,
) -> Result<(), Error> {
use arbiter_proto::proto::operator::governance::ApproveOneOffTransactionPayload;
use crate::actors::evm::ClientSignTransaction;
use crate::db::models::NewProposalResult;
use alloy::{
@@ -498,6 +509,7 @@ impl ProposalManager {
eips::eip2930::AccessList,
primitives::{Address, Bytes, TxKind, U256},
};
use arbiter_proto::proto::operator::governance::ApproveOneOffTransactionPayload;
use prost::Message as _;
let p = ApproveOneOffTransactionPayload::decode(payload_bytes.as_slice())
@@ -520,7 +532,9 @@ impl ProposalManager {
p.max_priority_fee_per_gas
.as_slice()
.try_into()
CleverWild marked this conversation as resolved Outdated

OperatorId unit type

`OperatorId` unit type
.map_err(|_| Error::ExecutionFailed("invalid max_priority_fee_per_gas".to_owned()))?,
.map_err(|_| {
Error::ExecutionFailed("invalid max_priority_fee_per_gas".to_owned())
})?,
),
to: TxKind::Call(to),
value: U256::from_be_slice(p.value.as_slice()),
@@ -552,10 +566,6 @@ impl ProposalManager {
}
async fn execute_approve_persistent_grant(&self, payload_bytes: Vec<u8>) -> Result<(), Error> {
use arbiter_proto::proto::operator::governance::{
ApprovePersistentGrantPayload,
approve_persistent_grant_payload::Specific,
};
use crate::{
actors::evm::OperatorCreateGrant,
evm::policies::{
@@ -564,6 +574,9 @@ impl ProposalManager {
},
};
use alloy::primitives::{Address, U256};
use arbiter_proto::proto::operator::governance::{
ApprovePersistentGrantPayload, approve_persistent_grant_payload::Specific,
};
use chrono::Duration;
use prost::Message as _;
@@ -573,10 +586,18 @@ impl ProposalManager {
let basic = SharedGrantSettings {
wallet_access_id: payload.wallet_access_id,
chain: payload.chain_id,
valid_from: payload.valid_from_secs.and_then(|s| chrono::DateTime::from_timestamp(s, 0)),
valid_until: payload.valid_until_secs.and_then(|s| chrono::DateTime::from_timestamp(s, 0)),
max_gas_fee_per_gas: payload.max_gas_fee_per_gas.map(|b| U256::from_be_slice(b.as_slice())),
max_priority_fee_per_gas: payload.max_priority_fee_per_gas.map(|b| U256::from_be_slice(b.as_slice())),
valid_from: payload
.valid_from_secs
.and_then(|s| chrono::DateTime::from_timestamp(s, 0)),
valid_until: payload
.valid_until_secs
.and_then(|s| chrono::DateTime::from_timestamp(s, 0)),
max_gas_fee_per_gas: payload
.max_gas_fee_per_gas
.map(|b| U256::from_be_slice(b.as_slice())),
max_priority_fee_per_gas: payload
.max_priority_fee_per_gas
.map(|b| U256::from_be_slice(b.as_slice())),
rate_limit: payload.rate_limit.map(|r| TransactionRateLimit {
count: r.count,
window: Duration::seconds(r.window_secs),
@@ -585,22 +606,27 @@ impl ProposalManager {
let grant = match payload.specific {
Some(Specific::EtherTransfer(spec)) => {
let target: Vec<Address> = spec.targets
let target: Vec<Address> = spec
.targets
.iter()
.map(|b| Address::from_slice(b.as_slice()))
CleverWild marked this conversation as resolved Outdated

signature check is quite complex, so separate it into several helpers and put them into crypto submodule

signature check is quite complex, so separate it into several helpers and put them into `crypto` submodule
.collect();
let limit = spec.limit
let limit = spec
.limit
.map(|l| VolumeRateLimit {
max_volume: U256::from_be_slice(l.max_volume.as_slice()),
window: Duration::seconds(l.window_secs),
})
.ok_or_else(|| Error::ExecutionFailed("missing ether transfer limit".to_owned()))?;
.ok_or_else(|| {
Error::ExecutionFailed("missing ether transfer limit".to_owned())
})?;
SpecificGrant::EtherTransfer(ether_transfer::Settings { target, limit })
}
Some(Specific::TokenTransfer(spec)) => {
let token_contract = Address::from_slice(spec.token_contract.as_slice());
let target = spec.target.map(|b| Address::from_slice(b.as_slice()));
let volume_limits: Vec<VolumeRateLimit> = spec.volume_limits
let volume_limits: Vec<VolumeRateLimit> = spec
.volume_limits
.iter()
.map(|l| VolumeRateLimit {
max_volume: U256::from_be_slice(l.max_volume.as_slice()),
@@ -625,11 +651,8 @@ impl ProposalManager {
}
async fn execute_approve_sdk_client(&self, client_id: i32) -> Result<(), Error> {
use crate::{crypto::integrity, peers::client::ClientCredentials};
use arbiter_crypto::authn;
use crate::{
crypto::integrity,
peers::client::ClientCredentials,
};
let mut conn = self.db.get().await.map_err(Error::DatabaseConnection)?;

View File

@@ -54,7 +54,8 @@ async fn handle_create(
},
Some(ProtoKind::ApproveServerUpdate(_)) => ProposalKind::ApproveServerUpdate,
Some(ProtoKind::ReplaceOperator(p)) => ProposalKind::ReplaceOperator {
new_pubkey: p.new_pubkey,
new_pubkey: p.new_pubkey.try_into()
.map_err(|_| Status::invalid_argument("replace_operator: pubkey must be 32 bytes"))?,
},
Some(ProtoKind::UpdateShamirParameters(p)) => ProposalKind::UpdateShamirParameters {
#[expect(clippy::cast_possible_truncation, clippy::as_conversions, reason = "new_n is always a small operator count")]

View File

@@ -717,7 +717,7 @@ async fn replace_operator_inserts_identity_row() {
let op_id = register_operator(&db, &signing_key.public_key()).await;
let new_op_key = authn::SigningKey::generate();
let new_pubkey = new_op_key.public_key().to_bytes().to_vec();
let new_pubkey = new_op_key.public_key().to_bytes();
let proposal_id = actors
.proposal_manager