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
- `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 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)

View File

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

View File

@ -1,6 +1,6 @@
use super::{eip2930::AccessList, normalize_v, rlp_opt};
use crate::{
types::{Address, Bytes, NameOrAddress, Signature, H256, U256, U64},
types::{Address, Bytes, NameOrAddress, Signature, Transaction, H256, U256, U64},
utils::keccak256,
};
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},
};
use crate::{
types::{Address, Bytes, NameOrAddress, Signature, TransactionRequest, H256, U256, U64},
types::{
Address, Bytes, NameOrAddress, Signature, Transaction, TransactionRequest, H256, U256, U64,
},
utils::keccak256,
};
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)]
mod tests {
use rlp::Decodable;

View File

@ -1,5 +1,5 @@
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_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)]
mod tests {

View File

@ -1,7 +1,7 @@
//! Transaction types
use super::{extract_chain_id, rlp_opt, NUM_TX_FIELDS};
use crate::{
types::{Address, Bytes, NameOrAddress, Signature, H256, U256, U64},
types::{Address, Bytes, NameOrAddress, Signature, Transaction, H256, U256, U64},
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
#[cfg(feature = "celo")]
impl TransactionRequest {

View File

@ -1,7 +1,9 @@
//! 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::{
types::{Address, Bloom, Bytes, Log, H256, U256, U64},
types::{Address, Bloom, Bytes, Log, Signature, SignatureError, H256, U256, U64},
utils::keccak256,
};
use rlp::{Decodable, DecoderError, RlpStream};
@ -32,6 +34,7 @@ pub struct Transaction {
pub transaction_index: Option<U64>,
/// Sender
#[serde(default = "crate::types::Address::zero")]
pub from: Address,
/// Recipient (None when contract creation)
@ -311,6 +314,20 @@ impl Transaction {
*offset += 1;
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
@ -636,4 +653,46 @@ mod tests {
// we compare hash because the hash depends on the rlp encoding
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());
}
}