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:
parent
4ce373b856
commit
fa04247808
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue