refactor(server): typed pubkey len via u32::try_from in ReplaceOperator
This commit is contained in:
@@ -41,7 +41,10 @@ impl ProposalKind {
|
|||||||
pub fn encode_payload(&self) -> Vec<u8> {
|
pub fn encode_payload(&self) -> Vec<u8> {
|
||||||
match self {
|
match self {
|
||||||
Self::ApproveSdkClient { client_id } => client_id.to_be_bytes().to_vec(),
|
Self::ApproveSdkClient { client_id } => client_id.to_be_bytes().to_vec(),
|
||||||
Self::GrantWalletAccess { wallet_id, client_id } => {
|
Self::GrantWalletAccess {
|
||||||
|
wallet_id,
|
||||||
|
client_id,
|
||||||
|
} => {
|
||||||
let mut buf = Vec::with_capacity(8);
|
let mut buf = Vec::with_capacity(8);
|
||||||
buf.extend_from_slice(&wallet_id.to_be_bytes());
|
buf.extend_from_slice(&wallet_id.to_be_bytes());
|
||||||
buf.extend_from_slice(&client_id.to_be_bytes());
|
buf.extend_from_slice(&client_id.to_be_bytes());
|
||||||
@@ -49,8 +52,7 @@ impl ProposalKind {
|
|||||||
}
|
}
|
||||||
Self::ApproveServerUpdate => vec![],
|
Self::ApproveServerUpdate => vec![],
|
||||||
Self::ReplaceOperator { new_pubkey } => {
|
Self::ReplaceOperator { new_pubkey } => {
|
||||||
#[expect(clippy::cast_possible_truncation, clippy::as_conversions, reason = "pubkey is always 32 bytes")]
|
let len = u32::try_from(new_pubkey.len()).expect("pubkey len fits in u32");
|
||||||
let len = new_pubkey.len() as u32;
|
|
||||||
let mut buf = Vec::with_capacity(4 + new_pubkey.len());
|
let mut buf = Vec::with_capacity(4 + new_pubkey.len());
|
||||||
buf.extend_from_slice(&len.to_be_bytes());
|
buf.extend_from_slice(&len.to_be_bytes());
|
||||||
buf.extend_from_slice(new_pubkey);
|
buf.extend_from_slice(new_pubkey);
|
||||||
@@ -153,7 +155,11 @@ pub struct ProposalManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 }
|
Self { db, vault, evm }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,10 +168,7 @@ impl kameo::Actor for ProposalManager {
|
|||||||
type Args = Self;
|
type Args = Self;
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
async fn on_start(
|
async fn on_start(args: Self::Args, actor_ref: ActorRef<Self>) -> Result<Self, Self::Error> {
|
||||||
args: Self::Args,
|
|
||||||
actor_ref: ActorRef<Self>,
|
|
||||||
) -> Result<Self, Self::Error> {
|
|
||||||
let weak = actor_ref.downgrade();
|
let weak = actor_ref.downgrade();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
@@ -429,9 +432,10 @@ impl ProposalManager {
|
|||||||
ProposalKind::ApproveSdkClient { client_id } => {
|
ProposalKind::ApproveSdkClient { client_id } => {
|
||||||
self.execute_approve_sdk_client(client_id).await
|
self.execute_approve_sdk_client(client_id).await
|
||||||
}
|
}
|
||||||
ProposalKind::GrantWalletAccess { wallet_id, client_id } => {
|
ProposalKind::GrantWalletAccess {
|
||||||
self.execute_grant_wallet_access(wallet_id, client_id).await
|
wallet_id,
|
||||||
}
|
client_id,
|
||||||
|
} => self.execute_grant_wallet_access(wallet_id, client_id).await,
|
||||||
ProposalKind::ApproveServerUpdate => Ok(()),
|
ProposalKind::ApproveServerUpdate => Ok(()),
|
||||||
ProposalKind::ReplaceOperator { new_pubkey } => {
|
ProposalKind::ReplaceOperator { new_pubkey } => {
|
||||||
self.execute_replace_operator(new_pubkey).await
|
self.execute_replace_operator(new_pubkey).await
|
||||||
@@ -443,12 +447,17 @@ impl ProposalManager {
|
|||||||
self.execute_approve_persistent_grant(payload_bytes).await
|
self.execute_approve_persistent_grant(payload_bytes).await
|
||||||
}
|
}
|
||||||
ProposalKind::ApproveOneOffTransaction { payload_bytes } => {
|
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;
|
use crate::db::models::EvmWalletId;
|
||||||
|
|
||||||
let mut conn = self.db.get().await.map_err(Error::DatabaseConnection)?;
|
let mut conn = self.db.get().await.map_err(Error::DatabaseConnection)?;
|
||||||
@@ -481,7 +490,10 @@ impl ProposalManager {
|
|||||||
reason = "signature must match other execute_* methods"
|
reason = "signature must match other execute_* methods"
|
||||||
)]
|
)]
|
||||||
fn execute_update_shamir_parameters(&self, new_n: u8) -> Result<(), Error> {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,7 +502,6 @@ impl ProposalManager {
|
|||||||
proposal_id: i32,
|
proposal_id: i32,
|
||||||
payload_bytes: Vec<u8>,
|
payload_bytes: Vec<u8>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
use arbiter_proto::proto::operator::governance::ApproveOneOffTransactionPayload;
|
|
||||||
use crate::actors::evm::ClientSignTransaction;
|
use crate::actors::evm::ClientSignTransaction;
|
||||||
use crate::db::models::NewProposalResult;
|
use crate::db::models::NewProposalResult;
|
||||||
use alloy::{
|
use alloy::{
|
||||||
@@ -498,6 +509,7 @@ impl ProposalManager {
|
|||||||
eips::eip2930::AccessList,
|
eips::eip2930::AccessList,
|
||||||
primitives::{Address, Bytes, TxKind, U256},
|
primitives::{Address, Bytes, TxKind, U256},
|
||||||
};
|
};
|
||||||
|
use arbiter_proto::proto::operator::governance::ApproveOneOffTransactionPayload;
|
||||||
use prost::Message as _;
|
use prost::Message as _;
|
||||||
|
|
||||||
let p = ApproveOneOffTransactionPayload::decode(payload_bytes.as_slice())
|
let p = ApproveOneOffTransactionPayload::decode(payload_bytes.as_slice())
|
||||||
@@ -520,7 +532,9 @@ impl ProposalManager {
|
|||||||
p.max_priority_fee_per_gas
|
p.max_priority_fee_per_gas
|
||||||
.as_slice()
|
.as_slice()
|
||||||
.try_into()
|
.try_into()
|
||||||
.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),
|
to: TxKind::Call(to),
|
||||||
value: U256::from_be_slice(p.value.as_slice()),
|
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> {
|
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::{
|
use crate::{
|
||||||
actors::evm::OperatorCreateGrant,
|
actors::evm::OperatorCreateGrant,
|
||||||
evm::policies::{
|
evm::policies::{
|
||||||
@@ -564,6 +574,9 @@ impl ProposalManager {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use alloy::primitives::{Address, U256};
|
use alloy::primitives::{Address, U256};
|
||||||
|
use arbiter_proto::proto::operator::governance::{
|
||||||
|
ApprovePersistentGrantPayload, approve_persistent_grant_payload::Specific,
|
||||||
|
};
|
||||||
use chrono::Duration;
|
use chrono::Duration;
|
||||||
use prost::Message as _;
|
use prost::Message as _;
|
||||||
|
|
||||||
@@ -573,10 +586,18 @@ impl ProposalManager {
|
|||||||
let basic = SharedGrantSettings {
|
let basic = SharedGrantSettings {
|
||||||
wallet_access_id: payload.wallet_access_id,
|
wallet_access_id: payload.wallet_access_id,
|
||||||
chain: payload.chain_id,
|
chain: payload.chain_id,
|
||||||
valid_from: payload.valid_from_secs.and_then(|s| chrono::DateTime::from_timestamp(s, 0)),
|
valid_from: payload
|
||||||
valid_until: payload.valid_until_secs.and_then(|s| chrono::DateTime::from_timestamp(s, 0)),
|
.valid_from_secs
|
||||||
max_gas_fee_per_gas: payload.max_gas_fee_per_gas.map(|b| U256::from_be_slice(b.as_slice())),
|
.and_then(|s| chrono::DateTime::from_timestamp(s, 0)),
|
||||||
max_priority_fee_per_gas: payload.max_priority_fee_per_gas.map(|b| U256::from_be_slice(b.as_slice())),
|
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 {
|
rate_limit: payload.rate_limit.map(|r| TransactionRateLimit {
|
||||||
count: r.count,
|
count: r.count,
|
||||||
window: Duration::seconds(r.window_secs),
|
window: Duration::seconds(r.window_secs),
|
||||||
@@ -585,22 +606,27 @@ impl ProposalManager {
|
|||||||
|
|
||||||
let grant = match payload.specific {
|
let grant = match payload.specific {
|
||||||
Some(Specific::EtherTransfer(spec)) => {
|
Some(Specific::EtherTransfer(spec)) => {
|
||||||
let target: Vec<Address> = spec.targets
|
let target: Vec<Address> = spec
|
||||||
|
.targets
|
||||||
.iter()
|
.iter()
|
||||||
.map(|b| Address::from_slice(b.as_slice()))
|
.map(|b| Address::from_slice(b.as_slice()))
|
||||||
.collect();
|
.collect();
|
||||||
let limit = spec.limit
|
let limit = spec
|
||||||
|
.limit
|
||||||
.map(|l| VolumeRateLimit {
|
.map(|l| VolumeRateLimit {
|
||||||
max_volume: U256::from_be_slice(l.max_volume.as_slice()),
|
max_volume: U256::from_be_slice(l.max_volume.as_slice()),
|
||||||
window: Duration::seconds(l.window_secs),
|
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 })
|
SpecificGrant::EtherTransfer(ether_transfer::Settings { target, limit })
|
||||||
}
|
}
|
||||||
Some(Specific::TokenTransfer(spec)) => {
|
Some(Specific::TokenTransfer(spec)) => {
|
||||||
let token_contract = Address::from_slice(spec.token_contract.as_slice());
|
let token_contract = Address::from_slice(spec.token_contract.as_slice());
|
||||||
let target = spec.target.map(|b| Address::from_slice(b.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()
|
.iter()
|
||||||
.map(|l| VolumeRateLimit {
|
.map(|l| VolumeRateLimit {
|
||||||
max_volume: U256::from_be_slice(l.max_volume.as_slice()),
|
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> {
|
async fn execute_approve_sdk_client(&self, client_id: i32) -> Result<(), Error> {
|
||||||
|
use crate::{crypto::integrity, peers::client::ClientCredentials};
|
||||||
use arbiter_crypto::authn;
|
use arbiter_crypto::authn;
|
||||||
use crate::{
|
|
||||||
crypto::integrity,
|
|
||||||
peers::client::ClientCredentials,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut conn = self.db.get().await.map_err(Error::DatabaseConnection)?;
|
let mut conn = self.db.get().await.map_err(Error::DatabaseConnection)?;
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ async fn handle_create(
|
|||||||
},
|
},
|
||||||
Some(ProtoKind::ApproveServerUpdate(_)) => ProposalKind::ApproveServerUpdate,
|
Some(ProtoKind::ApproveServerUpdate(_)) => ProposalKind::ApproveServerUpdate,
|
||||||
Some(ProtoKind::ReplaceOperator(p)) => ProposalKind::ReplaceOperator {
|
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 {
|
Some(ProtoKind::UpdateShamirParameters(p)) => ProposalKind::UpdateShamirParameters {
|
||||||
#[expect(clippy::cast_possible_truncation, clippy::as_conversions, reason = "new_n is always a small operator count")]
|
#[expect(clippy::cast_possible_truncation, clippy::as_conversions, reason = "new_n is always a small operator count")]
|
||||||
|
|||||||
@@ -717,7 +717,7 @@ async fn replace_operator_inserts_identity_row() {
|
|||||||
let op_id = register_operator(&db, &signing_key.public_key()).await;
|
let op_id = register_operator(&db, &signing_key.public_key()).await;
|
||||||
|
|
||||||
let new_op_key = authn::SigningKey::generate();
|
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
|
let proposal_id = actors
|
||||||
.proposal_manager
|
.proposal_manager
|
||||||
|
|||||||
Reference in New Issue
Block a user