Tx optional from (#1075)

* doc(core/Signature): methods are all using normalized v, dont need to be 0 or 1 only

* feat(core/Transaction): make from optional, add method to recover from

* fix recovery

* add recover_from_mut

* update changelog
This commit is contained in:
Meet Mangukiya 2022-03-24 10:36:55 +05:30 committed by GitHub
parent 4ce373b856
commit fa04247808
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 143 additions and 7 deletions

View File

@ -4,6 +4,9 @@
### Unreleased ### Unreleased
- `Transaction::from` will default to `Address::zero()`. Add `recover_from` and
`recover_from_mut` methods for recovering the sender from signature, and also
setting the same on tx [1075](https://github.com/gakonst/ethers-rs/pull/1075).
- Add Etherscan account API endpoints [939](https://github.com/gakonst/ethers-rs/pull/939) - Add Etherscan account API endpoints [939](https://github.com/gakonst/ethers-rs/pull/939)
- Add FTM Mainet and testnet to parse method "try_from" from Chain.rs and add cronos mainet and testnet to "from_str" - Add FTM Mainet and testnet to parse method "try_from" from Chain.rs and add cronos mainet and testnet to "from_str"
- Add FTM mainnet and testnet Multicall addresses [927](https://github.com/gakonst/ethers-rs/pull/927) - Add FTM mainnet and testnet Multicall addresses [927](https://github.com/gakonst/ethers-rs/pull/927)

View File

@ -60,7 +60,7 @@ pub struct Signature {
pub r: U256, pub r: U256,
/// S Value /// S Value
pub s: U256, pub s: U256,
/// V value in 'Electrum' notation. /// V value
pub v: u64, pub v: u64,
} }

View File

@ -1,6 +1,6 @@
use super::{eip2930::AccessList, normalize_v, rlp_opt}; use super::{eip2930::AccessList, normalize_v, rlp_opt};
use crate::{ use crate::{
types::{Address, Bytes, NameOrAddress, Signature, H256, U256, U64}, types::{Address, Bytes, NameOrAddress, Signature, Transaction, H256, U256, U64},
utils::keccak256, utils::keccak256,
}; };
use rlp::{Decodable, DecoderError, RlpStream}; use rlp::{Decodable, DecoderError, RlpStream};
@ -244,3 +244,20 @@ impl From<Eip1559TransactionRequest> for super::request::TransactionRequest {
} }
} }
} }
impl From<&Transaction> for Eip1559TransactionRequest {
fn from(tx: &Transaction) -> Eip1559TransactionRequest {
Eip1559TransactionRequest {
from: Some(tx.from),
to: tx.to.map(NameOrAddress::Address),
gas: Some(tx.gas),
value: Some(tx.value),
data: Some(Bytes(tx.input.0.clone())),
nonce: Some(tx.nonce),
access_list: tx.access_list.clone().unwrap_or_default(),
max_priority_fee_per_gas: tx.max_priority_fee_per_gas,
max_fee_per_gas: tx.max_fee_per_gas,
chain_id: tx.chain_id.map(|x| U64::from(x.as_u64())),
}
}
}

View File

@ -3,7 +3,9 @@ use super::{
eip2930::{AccessList, Eip2930TransactionRequest}, eip2930::{AccessList, Eip2930TransactionRequest},
}; };
use crate::{ use crate::{
types::{Address, Bytes, NameOrAddress, Signature, TransactionRequest, H256, U256, U64}, types::{
Address, Bytes, NameOrAddress, Signature, Transaction, TransactionRequest, H256, U256, U64,
},
utils::keccak256, utils::keccak256,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -312,6 +314,28 @@ impl From<Eip1559TransactionRequest> for TypedTransaction {
} }
} }
impl From<&Transaction> for TypedTransaction {
fn from(tx: &Transaction) -> TypedTransaction {
match tx.transaction_type {
// EIP-2930 (0x01)
Some(x) if x == U64::from(1) => {
let request: Eip2930TransactionRequest = tx.into();
request.into()
}
// EIP-1559 (0x02)
Some(x) if x == U64::from(2) => {
let request: Eip1559TransactionRequest = tx.into();
request.into()
}
// Legacy (0x00)
_ => {
let request: TransactionRequest = tx.into();
request.into()
}
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rlp::Decodable; use rlp::Decodable;

View File

@ -1,5 +1,5 @@
use super::{normalize_v, request::TransactionRequest}; use super::{normalize_v, request::TransactionRequest};
use crate::types::{Address, Bytes, Signature, H256, U256, U64}; use crate::types::{Address, Bytes, Signature, Transaction, H256, U256, U64};
use rlp::{Decodable, DecoderError, RlpStream}; use rlp::{Decodable, DecoderError, RlpStream};
use rlp_derive::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper}; use rlp_derive::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper};
@ -144,6 +144,15 @@ impl Decodable for Eip2930TransactionRequest {
} }
} }
impl From<&Transaction> for Eip2930TransactionRequest {
fn from(tx: &Transaction) -> Eip2930TransactionRequest {
Eip2930TransactionRequest {
tx: tx.into(),
access_list: tx.access_list.clone().unwrap_or_default(),
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -1,7 +1,7 @@
//! Transaction types //! Transaction types
use super::{extract_chain_id, rlp_opt, NUM_TX_FIELDS}; use super::{extract_chain_id, rlp_opt, NUM_TX_FIELDS};
use crate::{ use crate::{
types::{Address, Bytes, NameOrAddress, Signature, H256, U256, U64}, types::{Address, Bytes, NameOrAddress, Signature, Transaction, H256, U256, U64},
utils::keccak256, utils::keccak256,
}; };
@ -274,6 +274,30 @@ impl Decodable for TransactionRequest {
} }
} }
impl From<&Transaction> for TransactionRequest {
fn from(tx: &Transaction) -> TransactionRequest {
TransactionRequest {
from: Some(tx.from),
to: tx.to.map(NameOrAddress::Address),
gas: Some(tx.gas),
gas_price: tx.gas_price,
value: Some(tx.value),
data: Some(Bytes(tx.input.0.clone())),
nonce: Some(tx.nonce),
chain_id: tx.chain_id.map(|x| U64::from(x.as_u64())),
#[cfg(feature = "celo")]
fee_currency: tx.fee_currency,
#[cfg(feature = "celo")]
gateway_fee_recipient: tx.gateway_fee_recipient,
#[cfg(feature = "celo")]
gateway_fee: tx.gateway_fee,
}
}
}
// Separate impl block for the celo-specific fields // Separate impl block for the celo-specific fields
#[cfg(feature = "celo")] #[cfg(feature = "celo")]
impl TransactionRequest { impl TransactionRequest {

View File

@ -1,7 +1,9 @@
//! Transaction types //! Transaction types
use super::{decode_signature, eip2930::AccessList, normalize_v, rlp_opt}; use super::{
decode_signature, eip2718::TypedTransaction, eip2930::AccessList, normalize_v, rlp_opt,
};
use crate::{ use crate::{
types::{Address, Bloom, Bytes, Log, H256, U256, U64}, types::{Address, Bloom, Bytes, Log, Signature, SignatureError, H256, U256, U64},
utils::keccak256, utils::keccak256,
}; };
use rlp::{Decodable, DecoderError, RlpStream}; use rlp::{Decodable, DecoderError, RlpStream};
@ -32,6 +34,7 @@ pub struct Transaction {
pub transaction_index: Option<U64>, pub transaction_index: Option<U64>,
/// Sender /// Sender
#[serde(default = "crate::types::Address::zero")]
pub from: Address, pub from: Address,
/// Recipient (None when contract creation) /// Recipient (None when contract creation)
@ -311,6 +314,20 @@ impl Transaction {
*offset += 1; *offset += 1;
Ok(()) Ok(())
} }
/// Recover the sender of the tx from signature
pub fn recover_from(&self) -> Result<Address, SignatureError> {
let signature = Signature { r: self.r, s: self.s, v: self.v.as_u64() };
let typed_tx: TypedTransaction = self.into();
signature.recover(typed_tx.sighash())
}
/// Recover the sender of the tx from signature and set the from field
pub fn recover_from_mut(&mut self) -> Result<Address, SignatureError> {
let from = self.recover_from()?;
self.from = from;
Ok(from)
}
} }
/// Get a TransactionReceipt directly from an rlp encoded byte stream /// Get a TransactionReceipt directly from an rlp encoded byte stream
@ -636,4 +653,46 @@ mod tests {
// we compare hash because the hash depends on the rlp encoding // we compare hash because the hash depends on the rlp encoding
assert_eq!(decoded_transaction.hash(), tx.hash()); assert_eq!(decoded_transaction.hash(), tx.hash());
} }
#[test]
fn recover_from() {
let tx = Transaction {
hash: H256::from_str(
"5e2fc091e15119c97722e9b63d5d32b043d077d834f377b91f80d32872c78109",
)
.unwrap(),
nonce: 65.into(),
block_hash: Some(
H256::from_str("f43869e67c02c57d1f9a07bb897b54bec1cfa1feb704d91a2ee087566de5df2c")
.unwrap(),
),
block_number: Some(6203173.into()),
transaction_index: Some(10.into()),
from: Address::from_str("e66b278fa9fbb181522f6916ec2f6d66ab846e04").unwrap(),
to: Some(Address::from_str("11d7c2ab0d4aa26b7d8502f6a7ef6844908495c2").unwrap()),
value: 0.into(),
gas_price: Some(1500000007.into()),
gas: 106703.into(),
input: hex::decode("e5225381").unwrap().into(),
v: 1.into(),
r: U256::from_str_radix(
"12010114865104992543118914714169554862963471200433926679648874237672573604889",
10,
)
.unwrap(),
s: U256::from_str_radix(
"22830728216401371437656932733690354795366167672037272747970692473382669718804",
10,
)
.unwrap(),
transaction_type: Some(2.into()),
access_list: Some(AccessList::default()),
max_priority_fee_per_gas: Some(1500000000.into()),
max_fee_per_gas: Some(1500000009.into()),
chain_id: Some(5.into()),
};
assert_eq!(tx.hash, tx.hash());
assert_eq!(tx.from, tx.recover_from().unwrap());
}
} }