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
|
### 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)
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue