From 63b00a9241a871f24d9528c9453d61fb23c35cca Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 5 Jan 2024 22:05:03 +0100 Subject: [PATCH 1/2] feat: implement sign_transaction on Trezor --- crates/consensus/src/lib.rs | 4 +- crates/consensus/src/transaction/eip1559.rs | 20 ++- crates/consensus/src/transaction/eip2930.rs | 25 ++- crates/consensus/src/transaction/legacy.rs | 24 ++- crates/consensus/src/transaction/mod.rs | 3 - crates/network/src/lib.rs | 2 +- .../src/transaction/common.rs | 2 +- crates/network/src/transaction/mod.rs | 21 ++- crates/signer-trezor/Cargo.toml | 2 + crates/signer-trezor/src/signer.rs | 149 ++++++++++++------ crates/signer-trezor/src/types.rs | 100 +----------- crates/signer/src/signer.rs | 42 ++--- 12 files changed, 198 insertions(+), 196 deletions(-) rename crates/{consensus => network}/src/transaction/common.rs (97%) diff --git a/crates/consensus/src/lib.rs b/crates/consensus/src/lib.rs index 19d0326bc69..d3fa574e811 100644 --- a/crates/consensus/src/lib.rs +++ b/crates/consensus/src/lib.rs @@ -24,4 +24,6 @@ mod receipt; pub use receipt::{Receipt, ReceiptEnvelope, ReceiptWithBloom}; mod transaction; -pub use transaction::{TxEip1559, TxEip2930, TxEnvelope, TxKind, TxLegacy, TxType}; +pub use transaction::{TxEip1559, TxEip2930, TxEnvelope, TxLegacy, TxType}; + +pub use alloy_network::TxKind; diff --git a/crates/consensus/src/transaction/eip1559.rs b/crates/consensus/src/transaction/eip1559.rs index ad8372d066a..42b34afcbfc 100644 --- a/crates/consensus/src/transaction/eip1559.rs +++ b/crates/consensus/src/transaction/eip1559.rs @@ -1,4 +1,4 @@ -use crate::{ReceiptWithBloom, TxKind, TxType}; +use crate::{TxKind, TxType}; use alloy_eips::eip2930::AccessList; use alloy_network::{Signed, Transaction}; use alloy_primitives::{keccak256, Bytes, ChainId, Signature, B256, U256}; @@ -231,7 +231,7 @@ impl Decodable for TxEip1559 { impl Transaction for TxEip1559 { type Signature = Signature; - type Receipt = ReceiptWithBloom; + // type Receipt = ReceiptWithBloom; fn into_signed(self, signature: Signature) -> Signed { let mut buf = vec![]; @@ -274,6 +274,14 @@ impl Transaction for TxEip1559 { self.input = input; } + fn to(&self) -> TxKind { + self.to + } + + fn set_to(&mut self, to: TxKind) { + self.to = to; + } + fn value(&self) -> U256 { self.value } @@ -305,6 +313,14 @@ impl Transaction for TxEip1559 { fn set_gas_limit(&mut self, limit: u64) { self.gas_limit = limit; } + + fn gas_price(&self) -> Option { + None + } + + fn set_gas_price(&mut self, price: U256) { + let _ = price; + } } #[cfg(all(test, feature = "k256"))] diff --git a/crates/consensus/src/transaction/eip2930.rs b/crates/consensus/src/transaction/eip2930.rs index 814ac0eb3d8..9726199d2d8 100644 --- a/crates/consensus/src/transaction/eip2930.rs +++ b/crates/consensus/src/transaction/eip2930.rs @@ -1,4 +1,4 @@ -use crate::{transaction::TxKind, ReceiptWithBloom, TxType}; +use crate::{TxKind, TxType}; use alloy_eips::eip2930::AccessList; use alloy_network::{Signed, Transaction}; use alloy_primitives::{keccak256, Bytes, ChainId, Signature, B256, U256}; @@ -192,7 +192,7 @@ impl Decodable for TxEip2930 { impl Transaction for TxEip2930 { type Signature = Signature; - type Receipt = ReceiptWithBloom; + // type Receipt = ReceiptWithBloom; fn into_signed(self, signature: Signature) -> Signed { let mut buf = vec![]; @@ -236,6 +236,14 @@ impl Transaction for TxEip2930 { self.input = input; } + fn to(&self) -> TxKind { + self.to + } + + fn set_to(&mut self, to: TxKind) { + self.to = to; + } + fn value(&self) -> U256 { self.value } @@ -267,13 +275,22 @@ impl Transaction for TxEip2930 { fn set_gas_limit(&mut self, limit: u64) { self.gas_limit = limit; } + + fn gas_price(&self) -> Option { + Some(U256::from(self.gas_price)) + } + + fn set_gas_price(&mut self, price: U256) { + if let Ok(price) = price.try_into() { + self.gas_price = price; + } + } } #[cfg(test)] mod tests { - use super::TxEip2930; - use crate::{transaction::TxKind, TxEnvelope}; + use crate::{TxEnvelope, TxKind}; use alloy_network::{Signed, Transaction}; use alloy_primitives::{Address, Bytes, Signature, U256}; use alloy_rlp::{Decodable, Encodable}; diff --git a/crates/consensus/src/transaction/legacy.rs b/crates/consensus/src/transaction/legacy.rs index 4f32eb673c1..766ada86c4c 100644 --- a/crates/consensus/src/transaction/legacy.rs +++ b/crates/consensus/src/transaction/legacy.rs @@ -1,4 +1,4 @@ -use crate::{transaction::TxKind, ReceiptWithBloom}; +use crate::TxKind; use alloy_network::{Signed, Transaction}; use alloy_primitives::{keccak256, Bytes, ChainId, Signature, B256, U256}; use alloy_rlp::{length_of_length, BufMut, Decodable, Encodable, Header, Result}; @@ -208,7 +208,7 @@ impl Decodable for TxLegacy { impl Transaction for TxLegacy { type Signature = Signature; - type Receipt = ReceiptWithBloom; + // type Receipt = ReceiptWithBloom; fn into_signed(self, signature: Signature) -> Signed { let mut buf = vec![]; @@ -250,6 +250,14 @@ impl Transaction for TxLegacy { self.input = data; } + fn to(&self) -> TxKind { + self.to + } + + fn set_to(&mut self, to: TxKind) { + self.to = to; + } + fn value(&self) -> U256 { self.value } @@ -281,6 +289,16 @@ impl Transaction for TxLegacy { fn set_gas_limit(&mut self, gas_limit: u64) { self.gas_limit = gas_limit; } + + fn gas_price(&self) -> Option { + Some(U256::from(self.gas_price)) + } + + fn set_gas_price(&mut self, price: U256) { + if let Ok(price) = price.try_into() { + self.gas_price = price; + } + } } #[cfg(test)] @@ -288,7 +306,7 @@ mod tests { #[test] #[cfg(feature = "k256")] fn recover_signer_legacy() { - use crate::transaction::{TxKind, TxLegacy}; + use crate::{TxKind, TxLegacy}; use alloy_network::Transaction; use alloy_primitives::{b256, hex, Address, Signature, B256, U256}; diff --git a/crates/consensus/src/transaction/mod.rs b/crates/consensus/src/transaction/mod.rs index 4259d438385..ec5b99d0c9d 100644 --- a/crates/consensus/src/transaction/mod.rs +++ b/crates/consensus/src/transaction/mod.rs @@ -1,6 +1,3 @@ -mod common; -pub use common::TxKind; - mod eip1559; pub use eip1559::TxEip1559; diff --git a/crates/network/src/lib.rs b/crates/network/src/lib.rs index 88506721253..c7042b550d0 100644 --- a/crates/network/src/lib.rs +++ b/crates/network/src/lib.rs @@ -23,7 +23,7 @@ mod sealed; pub use sealed::{Sealable, Sealed}; mod transaction; -pub use transaction::{Eip1559Transaction, Signed, Transaction}; +pub use transaction::{Eip1559Transaction, Signed, Transaction, TxKind}; mod receipt; pub use receipt::Receipt; diff --git a/crates/consensus/src/transaction/common.rs b/crates/network/src/transaction/common.rs similarity index 97% rename from crates/consensus/src/transaction/common.rs rename to crates/network/src/transaction/common.rs index 802acb2f7c4..42bf4d89486 100644 --- a/crates/consensus/src/transaction/common.rs +++ b/crates/network/src/transaction/common.rs @@ -35,7 +35,7 @@ impl TxKind { /// Calculates a heuristic for the in-memory size of this object. #[inline] - pub(crate) const fn size(self) -> usize { + pub const fn size(self) -> usize { std::mem::size_of::() } } diff --git a/crates/network/src/transaction/mod.rs b/crates/network/src/transaction/mod.rs index a88e2d6abe6..66e0bdfc73b 100644 --- a/crates/network/src/transaction/mod.rs +++ b/crates/network/src/transaction/mod.rs @@ -1,12 +1,14 @@ -use crate::Receipt; use alloy_primitives::{Bytes, ChainId, Signature, B256, U256}; -use alloy_rlp::{BufMut, Decodable, Encodable}; +use alloy_rlp::{BufMut, Encodable}; + +mod common; +pub use common::TxKind; mod signed; pub use signed::Signed; /// Represents a minimal EVM transaction. -pub trait Transaction: Encodable + Decodable + Send + Sync + 'static { +pub trait Transaction: std::any::Any + Encodable + Send + Sync + 'static { /// The signature type for this transaction. /// /// This is usually [`alloy_primitives::Signature`], however, it may be different for future @@ -14,9 +16,6 @@ pub trait Transaction: Encodable + Decodable + Send + Sync + 'static { /// transaction signature is the unit type `()`. type Signature; - /// The receipt type for this transaction. - type Receipt: Receipt; - /// Convert to a signed transaction by adding a signature and computing the /// hash. fn into_signed(self, signature: Signature) -> Signed @@ -45,6 +44,11 @@ pub trait Transaction: Encodable + Decodable + Send + Sync + 'static { /// Set `data`. fn set_input(&mut self, data: Bytes); + /// Get `to`. + fn to(&self) -> TxKind; + /// Set `to`. + fn set_to(&mut self, to: TxKind); + /// Get `value`. fn value(&self) -> U256; /// Set `value`. @@ -64,6 +68,11 @@ pub trait Transaction: Encodable + Decodable + Send + Sync + 'static { fn gas_limit(&self) -> u64; /// Set `gas_limit`. fn set_gas_limit(&mut self, limit: u64); + + /// Get `gas_price`. + fn gas_price(&self) -> Option; + /// Set `gas_price`. + fn set_gas_price(&mut self, price: U256); } /// Captures getters and setters common across EIP-1559 transactions across all networks diff --git a/crates/signer-trezor/Cargo.toml b/crates/signer-trezor/Cargo.toml index 7be31ebef5c..f1e73c8779b 100644 --- a/crates/signer-trezor/Cargo.toml +++ b/crates/signer-trezor/Cargo.toml @@ -12,6 +12,8 @@ repository.workspace = true exclude.workspace = true [dependencies] +alloy-consensus.workspace = true +alloy-network.workspace = true alloy-primitives.workspace = true alloy-signer.workspace = true diff --git a/crates/signer-trezor/src/signer.rs b/crates/signer-trezor/src/signer.rs index ced843d825f..e2273a7c9ed 100644 --- a/crates/signer-trezor/src/signer.rs +++ b/crates/signer-trezor/src/signer.rs @@ -1,6 +1,8 @@ use super::types::{DerivationType, TrezorError}; -use alloy_primitives::{hex, Address, ChainId, B256, U256}; -use alloy_signer::{Result, Signature, Signer}; +use alloy_consensus::TxEip1559; +use alloy_network::{Transaction, TxKind}; +use alloy_primitives::{hex, Address, ChainId, Parity, B256, U256}; +use alloy_signer::{Result, SignableTx, Signature, Signer}; use async_trait::async_trait; use std::fmt; use trezor_client::client::Trezor; @@ -35,6 +37,7 @@ impl fmt::Debug for TrezorSigner { #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl Signer for TrezorSigner { + #[inline] async fn sign_hash(&self, _hash: B256) -> Result { Err(alloy_signer::Error::UnsupportedOperation( alloy_signer::UnsupportedSignerOperation::SignHash, @@ -43,13 +46,23 @@ impl Signer for TrezorSigner { #[inline] async fn sign_message(&self, message: &[u8]) -> Result { - self.sign_message_(message).await.map_err(alloy_signer::Error::other) + self.sign_message_inner(message).await.map_err(alloy_signer::Error::other) } #[inline] - #[cfg(TODO)] async fn sign_transaction(&self, tx: &mut dyn SignableTx) -> Result { - self.sign_tx(tx).await + // TODO: the Trezor Ethereum sign transaction protobufs don't require a chain ID, but the + // trezor-client API does not reflect this. + // https://github.com/trezor/trezor-firmware/pull/3482 + let chain_id = if let Some(chain_id) = self.chain_id { + tx.set_chain_id_checked(chain_id)?; + chain_id + } else { + tx.chain_id().ok_or(TrezorError::MissingChainId).map_err(alloy_signer::Error::other)? + }; + let mut sig = self.sign_tx_inner(tx, chain_id).await.map_err(alloy_signer::Error::other)?; + sig = sig.with_chain_id(chain_id); + Ok(sig) } #[inline] @@ -108,7 +121,7 @@ impl TrezorSigner { let mut client = trezor_client::unique(false)?; client.init_device(None)?; - let features = client.features().ok_or(TrezorError::FeaturesError)?; + let features = client.features().ok_or(TrezorError::Features)?; let version = semver::Version::new( features.major_version() as u64, features.minor_version() as u64, @@ -143,59 +156,78 @@ impl TrezorSigner { Ok(address_str.parse()?) } - /// Signs an Ethereum transaction (requires confirmation on the Trezor) - #[cfg(TODO)] - async fn sign_tx(&self, tx: &mut dyn SignableTx) -> Result { + /// Signs an Ethereum transaction (requires confirmation on the Trezor). + /// + /// Does not apply EIP-155. + async fn sign_tx_inner( + &self, + tx: &dyn Transaction, + chain_id: ChainId, + ) -> Result { let mut client = self.get_client()?; + let path = Self::convert_path(&self.derivation); - let arr_path = Self::convert_path(&self.derivation); + let nonce = tx.nonce(); + let nonce = u64_to_trezor(nonce); - let transaction = TrezorTransaction::load(tx)?; + let gas_price = U256::ZERO; + let gas_price = u256_to_trezor(gas_price); - let chain_id = tx.chain_id().unwrap_or(self.chain_id); + let gas_limit = tx.gas_limit(); + let gas_limit = u64_to_trezor(gas_limit); - let signature = match tx { - TypedTransaction::Eip2930(_) | TypedTransaction::Legacy(_) => client.ethereum_sign_tx( - arr_path, - transaction.nonce, - transaction.gas_price, - transaction.gas, - transaction.to, - transaction.value, - transaction.data, - chain_id, - )?, - TypedTransaction::Eip1559(eip1559_tx) => client.ethereum_sign_eip1559_tx( - arr_path, - transaction.nonce, - transaction.gas, - transaction.to, - transaction.value, - transaction.data, - chain_id, - transaction.max_fee_per_gas, - transaction.max_priority_fee_per_gas, - transaction.access_list, - )?, - #[cfg(feature = "optimism")] - TypedTransaction::DepositTransaction(tx) => { - trezor_client::client::Signature { r: 0.into(), s: 0.into(), v: 0 } - } + let to = match tx.to() { + TxKind::Call(to) => address_to_trezor(&to), + TxKind::Create => String::new(), }; - Ok(Signature { r: signature.r, s: signature.s, v: signature.v }) + let value = tx.value(); + let value = u256_to_trezor(value); + + let data = tx.input().to_vec(); + + let tx_any = tx as &dyn std::any::Any; + let signature = if let Some(tx) = tx_any.downcast_ref::() { + let max_gas_fee = tx.max_fee_per_gas; + let max_gas_fee = u128_to_trezor(max_gas_fee); + + let max_priority_fee = tx.max_priority_fee_per_gas; + let max_priority_fee = u128_to_trezor(max_priority_fee); + + let access_list = tx + .access_list + .0 + .iter() + .map(|item| trezor_client::client::AccessListItem { + address: address_to_trezor(&item.address), + storage_keys: item.storage_keys.iter().map(|key| key.to_vec()).collect(), + }) + .collect(); + + client.ethereum_sign_eip1559_tx( + path, + nonce, + gas_limit, + to, + value, + data, + chain_id, + max_gas_fee, + max_priority_fee, + access_list, + ) + } else { + client.ethereum_sign_tx(path, nonce, gas_price, gas_limit, to, value, data, chain_id) + }?; + signature_from_trezor(signature) } #[instrument(skip(message), fields(message=hex::encode(message)), ret)] - async fn sign_message_(&self, message: &[u8]) -> Result { + async fn sign_message_inner(&self, message: &[u8]) -> Result { let mut client = self.get_client()?; let apath = Self::convert_path(&self.derivation); - let signature = client.ethereum_sign_message(message.into(), apath)?; - - let r = U256::from_limbs(signature.r.0); - let s = U256::from_limbs(signature.s.0); - Signature::from_scalars_and_parity(r.into(), s.into(), signature.v).map_err(Into::into) + signature_from_trezor(signature) } // helper which converts a derivation path to [u32] @@ -217,6 +249,31 @@ impl TrezorSigner { } } +fn u64_to_trezor(x: u64) -> Vec { + let bytes = x.to_be_bytes(); + bytes[x.leading_zeros() as usize / 8..].to_vec() +} + +fn u128_to_trezor(x: u128) -> Vec { + let bytes = x.to_be_bytes(); + bytes[x.leading_zeros() as usize / 8..].to_vec() +} + +fn u256_to_trezor(x: U256) -> Vec { + let bytes = x.to_be_bytes::<32>(); + bytes[x.leading_zeros() / 8..].to_vec() +} + +fn address_to_trezor(x: &Address) -> String { + format!("{x:?}") +} + +fn signature_from_trezor(x: trezor_client::client::Signature) -> Result { + let s = U256::from_limbs(x.s.0); + let r = U256::from_limbs(x.r.0); + Signature::from_rs_and_parity(r, s, Parity::Eip155(x.v)).map_err(Into::into) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/signer-trezor/src/types.rs b/crates/signer-trezor/src/types.rs index b2dd4639df9..7ab3315b737 100644 --- a/crates/signer-trezor/src/types.rs +++ b/crates/signer-trezor/src/types.rs @@ -5,7 +5,6 @@ use alloy_primitives::hex; use std::fmt; use thiserror::Error; -use trezor_client::client::AccessListItem as Trezor_AccessListItem; /// Trezor wallet type. #[derive(Clone, Debug)] @@ -47,101 +46,10 @@ pub enum TrezorError { /// version. #[error("Trezor Ethereum app requires at least version {0:?}")] UnsupportedFirmwareVersion(String), + /// Need to provide a chain ID for EIP-155 signing. + #[error("missing Trezor signer chain ID")] + MissingChainId, /// Could not retrieve device features. #[error("could not retrieve device features")] - FeaturesError, -} - -/// Trezor transaction. -#[allow(dead_code)] -pub(crate) struct TrezorTransaction { - pub(crate) nonce: Vec, - pub(crate) gas: Vec, - pub(crate) gas_price: Vec, - pub(crate) value: Vec, - pub(crate) to: String, - pub(crate) data: Vec, - pub(crate) max_fee_per_gas: Vec, - pub(crate) max_priority_fee_per_gas: Vec, - pub(crate) access_list: Vec, -} - -impl TrezorTransaction { - #[cfg(TODO)] - fn to_trimmed_big_endian(value: &U256) -> Vec { - let trimmed_value = B256::from(*value); - trimmed_value[value.leading_zeros() / 8..].to_vec() - } - - #[cfg(TODO)] - pub fn load(tx: &TypedTransaction) -> Result { - let to: String = match tx.to() { - TxKind::Call(address) => hex::encode_prefixed(address), - TxKind::Create => String::new(), - }; - - let nonce = tx.nonce().map_or(vec![], Self::to_trimmed_big_endian); - let gas = tx.gas().map_or(vec![], Self::to_trimmed_big_endian); - let gas_price = tx.gas_price().map_or(vec![], |v| Self::to_trimmed_big_endian(&v)); - let value = tx.value().map_or(vec![], Self::to_trimmed_big_endian); - let data = tx.data().map_or(vec![], |v| v.to_vec()); - - match tx { - TypedTransaction::Eip2930(_) | TypedTransaction::Legacy(_) => Ok(Self { - nonce, - gas, - gas_price, - value, - to, - data, - max_fee_per_gas: vec![], - max_priority_fee_per_gas: vec![], - access_list: vec![], - }), - TypedTransaction::Eip1559(eip1559_tx) => { - let max_fee_per_gas = - eip1559_tx.max_fee_per_gas.map_or(vec![], |v| Self::to_trimmed_big_endian(&v)); - - let max_priority_fee_per_gas = eip1559_tx - .max_priority_fee_per_gas - .map_or(vec![], |v| Self::to_trimmed_big_endian(&v)); - - let mut access_list: Vec = Vec::new(); - for item in &eip1559_tx.access_list.0 { - let address: String = hex::encode_prefixed(item.address); - let mut storage_keys: Vec> = Vec::new(); - - for key in &item.storage_keys { - storage_keys.push(key.as_bytes().to_vec()) - } - - access_list.push(Trezor_AccessListItem { address, storage_keys }) - } - - Ok(Self { - nonce, - gas, - gas_price, - value, - to, - data, - max_fee_per_gas, - max_priority_fee_per_gas, - access_list, - }) - } - #[cfg(feature = "optimism")] - TypedTransaction::DepositTransaction(_) => Ok(Self { - nonce, - gas, - gas_price, - value, - to, - data, - max_fee_per_gas: vec![], - max_priority_fee_per_gas: vec![], - access_list: vec![], - }), - } - } + Features, } diff --git a/crates/signer/src/signer.rs b/crates/signer/src/signer.rs index 15acc5eea35..515af2d90d8 100644 --- a/crates/signer/src/signer.rs +++ b/crates/signer/src/signer.rs @@ -9,21 +9,15 @@ use alloy_sol_types::{Eip712Domain, SolStruct}; /// Trait for a signable transaction. /// -/// This trait allows us to generically sign transactions without knowing the -/// receipt type, and is blanket implemented for transactions which our signer -/// trait can produce signatures for. -pub trait SignableTx: Send + Sync + 'static { +/// This trait is implemented for all types that implement [`Transaction`] with [`Signature`] as the +/// signature associated type. +pub trait SignableTx: Transaction { /// Encode the transaction. - fn rlp_encode(&self) -> Vec; - - /// Calculate the signing hash for the transaction. - fn signature_hash(&self) -> B256; - - /// Returns the chain ID. Used for EIP-155 signing. - fn chain_id(&self) -> Option; - - /// Sets the chain ID. Used for EIP-155 signing. - fn set_chain_id(&mut self, chain_id: ChainId); + fn rlp_encode(&self) -> Vec { + let mut out = Vec::new(); + self.encode(&mut out); + out + } /// Set `chain_id` if it is not already set. Checks that the provided `chain_id` matches the /// existing `chain_id` if it is already set. @@ -45,25 +39,7 @@ pub trait SignableTx: Send + Sync + 'static { } } -impl> SignableTx for T { - fn rlp_encode(&self) -> Vec { - let mut out = Vec::new(); - self.encode(&mut out); - out - } - - fn signature_hash(&self) -> B256 { - Transaction::signature_hash(self) - } - - fn chain_id(&self) -> Option { - Transaction::chain_id(self) - } - - fn set_chain_id(&mut self, chain_id: ChainId) { - Transaction::set_chain_id(self, chain_id) - } -} +impl> SignableTx for T {} /// Asynchronous Ethereum signer. /// From cfeefc2e14b64805aa12a7c271f7f87d8101b105 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 5 Jan 2024 23:35:46 +0100 Subject: [PATCH 2/2] fix: dyn trait upcasting is not stable yet --- crates/network/src/transaction/mod.rs | 12 ++++++++++++ crates/signer-ledger/src/signer.rs | 4 ++-- crates/signer-trezor/src/signer.rs | 11 +++++++---- crates/signer/src/lib.rs | 2 +- crates/signer/src/signer.rs | 13 ++++++++----- crates/signer/src/wallet/private_key.rs | 2 +- 6 files changed, 31 insertions(+), 13 deletions(-) diff --git a/crates/network/src/transaction/mod.rs b/crates/network/src/transaction/mod.rs index 66e0bdfc73b..145fdf8186f 100644 --- a/crates/network/src/transaction/mod.rs +++ b/crates/network/src/transaction/mod.rs @@ -75,6 +75,18 @@ pub trait Transaction: std::any::Any + Encodable + Send + Sync + 'static { fn set_gas_price(&mut self, price: U256); } +// TODO: Remove in favor of dyn trait upcasting (1.76+) +#[doc(hidden)] +impl dyn Transaction { + pub fn __downcast_ref(&self) -> Option<&T> { + if std::any::Any::type_id(self) == std::any::TypeId::of::() { + unsafe { Some(&*(self as *const _ as *const T)) } + } else { + None + } + } +} + /// Captures getters and setters common across EIP-1559 transactions across all networks pub trait Eip1559Transaction: Transaction { /// Get `max_priority_fee_per_gas`. diff --git a/crates/signer-ledger/src/signer.rs b/crates/signer-ledger/src/signer.rs index cb93f8ff4ec..ef051ef30da 100644 --- a/crates/signer-ledger/src/signer.rs +++ b/crates/signer-ledger/src/signer.rs @@ -2,7 +2,7 @@ use crate::types::{DerivationType, LedgerError, INS, P1, P1_FIRST, P2}; use alloy_primitives::{hex, Address, ChainId, B256}; -use alloy_signer::{Result, SignableTx, Signature, Signer}; +use alloy_signer::{Result, SignableTx, Signature, Signer, TransactionExt}; use async_trait::async_trait; use coins_ledger::{ common::{APDUCommand, APDUData}, @@ -48,7 +48,7 @@ impl Signer for LedgerSigner { } #[inline] - async fn sign_transaction(&self, tx: &mut dyn SignableTx) -> Result { + async fn sign_transaction(&self, tx: &mut SignableTx) -> Result { let chain_id = self.chain_id(); if let Some(chain_id) = chain_id { tx.set_chain_id_checked(chain_id)?; diff --git a/crates/signer-trezor/src/signer.rs b/crates/signer-trezor/src/signer.rs index e2273a7c9ed..330eade994c 100644 --- a/crates/signer-trezor/src/signer.rs +++ b/crates/signer-trezor/src/signer.rs @@ -2,7 +2,7 @@ use super::types::{DerivationType, TrezorError}; use alloy_consensus::TxEip1559; use alloy_network::{Transaction, TxKind}; use alloy_primitives::{hex, Address, ChainId, Parity, B256, U256}; -use alloy_signer::{Result, SignableTx, Signature, Signer}; +use alloy_signer::{Result, SignableTx, Signature, Signer, TransactionExt}; use async_trait::async_trait; use std::fmt; use trezor_client::client::Trezor; @@ -50,7 +50,7 @@ impl Signer for TrezorSigner { } #[inline] - async fn sign_transaction(&self, tx: &mut dyn SignableTx) -> Result { + async fn sign_transaction(&self, tx: &mut SignableTx) -> Result { // TODO: the Trezor Ethereum sign transaction protobufs don't require a chain ID, but the // trezor-client API does not reflect this. // https://github.com/trezor/trezor-firmware/pull/3482 @@ -186,8 +186,11 @@ impl TrezorSigner { let data = tx.input().to_vec(); - let tx_any = tx as &dyn std::any::Any; - let signature = if let Some(tx) = tx_any.downcast_ref::() { + // TODO: Uncomment in 1.76 + /* + let signature = if let Some(tx) = (tx as &dyn std::any::Any).downcast_ref::() { + */ + let signature = if let Some(tx) = tx.__downcast_ref::() { let max_gas_fee = tx.max_fee_per_gas; let max_gas_fee = u128_to_trezor(max_gas_fee); diff --git a/crates/signer/src/lib.rs b/crates/signer/src/lib.rs index bb748167ffb..ab60a30af41 100644 --- a/crates/signer/src/lib.rs +++ b/crates/signer/src/lib.rs @@ -19,7 +19,7 @@ mod error; pub use error::{Error, Result, UnsupportedSignerOperation}; mod signer; -pub use signer::{SignableTx, Signer, SignerSync}; +pub use signer::{SignableTx, Signer, SignerSync, TransactionExt}; mod wallet; #[cfg(feature = "mnemonic")] diff --git a/crates/signer/src/signer.rs b/crates/signer/src/signer.rs index 515af2d90d8..709a1236005 100644 --- a/crates/signer/src/signer.rs +++ b/crates/signer/src/signer.rs @@ -7,11 +7,14 @@ use auto_impl::auto_impl; #[cfg(feature = "eip712")] use alloy_sol_types::{Eip712Domain, SolStruct}; -/// Trait for a signable transaction. +/// A signable transaction. +pub type SignableTx = dyn Transaction; + +/// Extension trait for utilities for signable transactions. /// /// This trait is implemented for all types that implement [`Transaction`] with [`Signature`] as the /// signature associated type. -pub trait SignableTx: Transaction { +pub trait TransactionExt: Transaction { /// Encode the transaction. fn rlp_encode(&self) -> Vec { let mut out = Vec::new(); @@ -39,7 +42,7 @@ pub trait SignableTx: Transaction { } } -impl> SignableTx for T {} +impl> TransactionExt for T {} /// Asynchronous Ethereum signer. /// @@ -75,7 +78,7 @@ pub trait Signer: Send + Sync { /// Signs the transaction. #[inline] - async fn sign_transaction(&self, tx: &mut dyn SignableTx) -> Result { + async fn sign_transaction(&self, tx: &mut SignableTx) -> Result { let chain_id = self.chain_id(); if let Some(chain_id) = chain_id { tx.set_chain_id_checked(chain_id)?; @@ -158,7 +161,7 @@ pub trait SignerSync { /// Signs the transaction. #[inline] - fn sign_transaction_sync(&self, tx: &mut dyn SignableTx) -> Result { + fn sign_transaction_sync(&self, tx: &mut SignableTx) -> Result { let chain_id = self.chain_id_sync(); if let Some(chain_id) = chain_id { tx.set_chain_id_checked(chain_id)?; diff --git a/crates/signer/src/wallet/private_key.rs b/crates/signer/src/wallet/private_key.rs index 7539d5628de..7391a9ee65f 100644 --- a/crates/signer/src/wallet/private_key.rs +++ b/crates/signer/src/wallet/private_key.rs @@ -238,7 +238,7 @@ mod tests { } async fn sign_dyn_tx_test( - tx: &mut dyn SignableTx, + tx: &mut SignableTx, chain_id: Option, ) -> Result { let mut wallet: Wallet =