fix(core): decode `from` field for typed transactions (#1180)
* fix(core): add eip1559 prefix for sighash - add a test which properly tests the `from` address decoding, which was incorrect due to the tx type not being hashed before recovery * fix(core): decode from for EIP2930 transactions - add test which checks signed decoding for an EIP2930 transaction * ignore decoding tests for celo * remove type constant and use typed sighash * update CHANGELOG
This commit is contained in:
parent
8afd670a65
commit
3d9fc21ba9
|
@ -4,6 +4,9 @@
|
||||||
|
|
||||||
### Unreleased
|
### Unreleased
|
||||||
|
|
||||||
|
- Fix RLP decoding of `from` field for `Eip1559TransactionRequest` and
|
||||||
|
`Eip2930TransactionRequest`, remove `Eip1559TransactionRequest` `sighash`
|
||||||
|
method [1180](https://github.com/gakonst/ethers-rs/pull/1180)
|
||||||
- Fix RLP encoding of absent access list in `Transaction` [1137](https://github.com/gakonst/ethers-rs/pull/1137)
|
- Fix RLP encoding of absent access list in `Transaction` [1137](https://github.com/gakonst/ethers-rs/pull/1137)
|
||||||
- Pass compilation time as additional argument to `Reporter::on_solc_success` [1098](https://github.com/gakonst/ethers-rs/pull/1098)
|
- Pass compilation time as additional argument to `Reporter::on_solc_success` [1098](https://github.com/gakonst/ethers-rs/pull/1098)
|
||||||
- Fix aws signer bug which maps un-normalized signature to error if no normalization occurs (in `aws::utils::decode_signature`)
|
- Fix aws signer bug which maps un-normalized signature to error if no normalization occurs (in `aws::utils::decode_signature`)
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
use super::{decode_to, eip2930::AccessList, normalize_v, rlp_opt};
|
use super::{decode_to, eip2718::TypedTransaction, eip2930::AccessList, normalize_v, rlp_opt};
|
||||||
use crate::{
|
use crate::types::{
|
||||||
types::{
|
Address, Bytes, NameOrAddress, Signature, SignatureError, Transaction, U256, U64,
|
||||||
Address, Bytes, NameOrAddress, Signature, SignatureError, Transaction, H256, U256, U64,
|
|
||||||
},
|
|
||||||
utils::keccak256,
|
|
||||||
};
|
};
|
||||||
use rlp::{Decodable, DecoderError, RlpStream};
|
use rlp::{Decodable, DecoderError, RlpStream};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
@ -157,11 +154,6 @@ impl Eip1559TransactionRequest {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hashes the transaction's data with the provided chain id
|
|
||||||
pub fn sighash(&self) -> H256 {
|
|
||||||
keccak256(self.rlp().as_ref()).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the unsigned transaction's RLP encoding
|
/// Gets the unsigned transaction's RLP encoding
|
||||||
pub fn rlp(&self) -> Bytes {
|
pub fn rlp(&self) -> Bytes {
|
||||||
let mut rlp = RlpStream::new();
|
let mut rlp = RlpStream::new();
|
||||||
|
@ -234,14 +226,14 @@ impl Eip1559TransactionRequest {
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
let mut txn = Self::decode_base_rlp(rlp, &mut offset)?;
|
let mut txn = Self::decode_base_rlp(rlp, &mut offset)?;
|
||||||
|
|
||||||
let v = rlp.at(offset)?.as_val()?;
|
let v = rlp.val_at(offset)?;
|
||||||
offset += 1;
|
offset += 1;
|
||||||
let r = rlp.at(offset)?.as_val()?;
|
let r = rlp.val_at(offset)?;
|
||||||
offset += 1;
|
offset += 1;
|
||||||
let s = rlp.at(offset)?.as_val()?;
|
let s = rlp.val_at(offset)?;
|
||||||
|
|
||||||
let sig = Signature { r, s, v };
|
let sig = Signature { r, s, v };
|
||||||
txn.from = Some(sig.recover(txn.sighash())?);
|
txn.from = Some(sig.recover(TypedTransaction::Eip1559(txn.clone()).sighash())?);
|
||||||
|
|
||||||
Ok((txn, sig))
|
Ok((txn, sig))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use super::{
|
use super::{
|
||||||
eip1559::{Eip1559RequestError, Eip1559TransactionRequest},
|
eip1559::{Eip1559RequestError, Eip1559TransactionRequest},
|
||||||
eip2930::{AccessList, Eip2930TransactionRequest},
|
eip2930::{AccessList, Eip2930RequestError, Eip2930TransactionRequest},
|
||||||
request::RequestError,
|
request::RequestError,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -48,6 +48,9 @@ pub enum TypedTransactionError {
|
||||||
/// When decoding a signed Eip1559 transaction
|
/// When decoding a signed Eip1559 transaction
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Eip1559Error(#[from] Eip1559RequestError),
|
Eip1559Error(#[from] Eip1559RequestError),
|
||||||
|
/// When decoding a signed Eip2930 transaction
|
||||||
|
#[error(transparent)]
|
||||||
|
Eip2930Error(#[from] Eip2930RequestError),
|
||||||
/// Error decoding the transaction type from the transaction's RLP encoding
|
/// Error decoding the transaction type from the transaction's RLP encoding
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
TypeDecodingError(#[from] rlp::DecoderError),
|
TypeDecodingError(#[from] rlp::DecoderError),
|
||||||
|
@ -397,6 +400,7 @@ impl From<&Transaction> for TypedTransaction {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use hex::ToHex;
|
||||||
use rlp::Decodable;
|
use rlp::Decodable;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -501,7 +505,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_signed_tx_decode() {
|
fn test_signed_tx_decode() {
|
||||||
let expected_tx = Eip1559TransactionRequest::new()
|
let expected_tx = Eip1559TransactionRequest::new()
|
||||||
.from(Address::from_str("0x27519a1d088898e04b12f9fb9733267a5e61481e").unwrap())
|
.from(Address::from_str("0x1acadd971da208d25122b645b2ef879868a83e21").unwrap())
|
||||||
.chain_id(1u64)
|
.chain_id(1u64)
|
||||||
.nonce(0u64)
|
.nonce(0u64)
|
||||||
.max_priority_fee_per_gas(413047990155u64)
|
.max_priority_fee_per_gas(413047990155u64)
|
||||||
|
@ -553,4 +557,61 @@ mod tests {
|
||||||
let tx_rlp = rlp::Rlp::new(typed_tx_hex.as_slice());
|
let tx_rlp = rlp::Rlp::new(typed_tx_hex.as_slice());
|
||||||
TypedTransaction::decode(&tx_rlp).unwrap();
|
TypedTransaction::decode(&tx_rlp).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_signed_tx_decode_all_fields() {
|
||||||
|
let typed_tx_hex = hex::decode("02f90188052b85012a05f20085012a05f2148301b3cd8080b9012d608060405234801561001057600080fd5b5061010d806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c8063cfae3217146037578063f8a8fd6d146066575b600080fd5b604080518082019091526003815262676d2160e81b60208201525b604051605d91906085565b60405180910390f35b6040805180820190915260048152636f6f662160e01b60208201526052565b600060208083528351808285015260005b8181101560b0578581018301518582016040015282016096565b8181111560c1576000604083870101525b50601f01601f191692909201604001939250505056fea2646970667358221220f89093a9819ba5d2a3384305511d0945ea94f36a8aa162ab62921b3841fe3afd64736f6c634300080c0033c080a08085850e935fd6af9ace1b0343b9e21d2dcc7e914c36cce61a4e32756c785980a04c57c184d5096263df981cb8a2f2c7f81640792856909dbf3295a2b7a1dc4a55").unwrap();
|
||||||
|
let tx_rlp = rlp::Rlp::new(typed_tx_hex.as_slice());
|
||||||
|
let (tx, sig) = TypedTransaction::decode_signed(&tx_rlp).unwrap();
|
||||||
|
|
||||||
|
let tx = match tx {
|
||||||
|
TypedTransaction::Eip1559(tx) => tx,
|
||||||
|
_ => panic!("The raw bytes should decode to an EIP1559 tranaction"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// pre-sighash fields - if a value here is incorrect it will show up before the sighash
|
||||||
|
// and from asserts fail
|
||||||
|
let data = Bytes::from_str("0x608060405234801561001057600080fd5b5061010d806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c8063cfae3217146037578063f8a8fd6d146066575b600080fd5b604080518082019091526003815262676d2160e81b60208201525b604051605d91906085565b60405180910390f35b6040805180820190915260048152636f6f662160e01b60208201526052565b600060208083528351808285015260005b8181101560b0578581018301518582016040015282016096565b8181111560c1576000604083870101525b50601f01601f191692909201604001939250505056fea2646970667358221220f89093a9819ba5d2a3384305511d0945ea94f36a8aa162ab62921b3841fe3afd64736f6c634300080c0033").unwrap();
|
||||||
|
assert_eq!(&data, tx.data.as_ref().unwrap());
|
||||||
|
|
||||||
|
let chain_id = U64::from(5u64);
|
||||||
|
assert_eq!(chain_id, tx.chain_id.unwrap());
|
||||||
|
|
||||||
|
let nonce = Some(43u64.into());
|
||||||
|
assert_eq!(nonce, tx.nonce);
|
||||||
|
|
||||||
|
let max_fee_per_gas = Some(5000000020u64.into());
|
||||||
|
assert_eq!(max_fee_per_gas, tx.max_fee_per_gas);
|
||||||
|
|
||||||
|
let max_priority_fee_per_gas = Some(5000000000u64.into());
|
||||||
|
assert_eq!(max_priority_fee_per_gas, tx.max_priority_fee_per_gas);
|
||||||
|
|
||||||
|
let gas = Some(111565u64.into());
|
||||||
|
assert_eq!(gas, tx.gas);
|
||||||
|
|
||||||
|
// empty fields
|
||||||
|
assert_eq!(None, tx.to);
|
||||||
|
assert_eq!(AccessList(vec![]), tx.access_list);
|
||||||
|
|
||||||
|
// compare rlp - sighash should then be the same
|
||||||
|
let tx_expected_rlp = "f90145052b85012a05f20085012a05f2148301b3cd8080b9012d608060405234801561001057600080fd5b5061010d806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c8063cfae3217146037578063f8a8fd6d146066575b600080fd5b604080518082019091526003815262676d2160e81b60208201525b604051605d91906085565b60405180910390f35b6040805180820190915260048152636f6f662160e01b60208201526052565b600060208083528351808285015260005b8181101560b0578581018301518582016040015282016096565b8181111560c1576000604083870101525b50601f01601f191692909201604001939250505056fea2646970667358221220f89093a9819ba5d2a3384305511d0945ea94f36a8aa162ab62921b3841fe3afd64736f6c634300080c0033c0";
|
||||||
|
let tx_real_rlp_vec = tx.rlp().to_vec();
|
||||||
|
let tx_real_rlp: String = tx_real_rlp_vec.encode_hex();
|
||||||
|
assert_eq!(tx_expected_rlp, tx_real_rlp);
|
||||||
|
|
||||||
|
let r =
|
||||||
|
U256::from_str("0x8085850e935fd6af9ace1b0343b9e21d2dcc7e914c36cce61a4e32756c785980")
|
||||||
|
.unwrap();
|
||||||
|
let s =
|
||||||
|
U256::from_str("0x4c57c184d5096263df981cb8a2f2c7f81640792856909dbf3295a2b7a1dc4a55")
|
||||||
|
.unwrap();
|
||||||
|
let v = 0;
|
||||||
|
assert_eq!(r, sig.r);
|
||||||
|
assert_eq!(s, sig.s);
|
||||||
|
assert_eq!(v, sig.v);
|
||||||
|
|
||||||
|
// finally check from
|
||||||
|
let addr = Address::from_str("0x216b32eCEbAe6aF164921D3943cd7A9634FcB199").unwrap();
|
||||||
|
assert_eq!(addr, tx.from.unwrap());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
use super::{extract_chain_id, normalize_v};
|
use super::{eip2718::TypedTransaction, normalize_v};
|
||||||
use crate::types::{Address, Bytes, Signature, Transaction, TransactionRequest, H256, U256, U64};
|
use crate::types::{
|
||||||
use rlp::{Decodable, DecoderError, RlpStream};
|
Address, Bytes, Signature, SignatureError, Transaction, TransactionRequest, H256, U256, U64,
|
||||||
|
};
|
||||||
|
use rlp::{Decodable, RlpStream};
|
||||||
use rlp_derive::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper};
|
use rlp_derive::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
const NUM_EIP2930_FIELDS: usize = 8;
|
const NUM_EIP2930_FIELDS: usize = 8;
|
||||||
|
|
||||||
|
@ -58,6 +61,17 @@ pub struct AccessListItem {
|
||||||
pub storage_keys: Vec<H256>,
|
pub storage_keys: Vec<H256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error involving an EIP2930 transaction request.
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum Eip2930RequestError {
|
||||||
|
/// When decoding a transaction request from RLP
|
||||||
|
#[error(transparent)]
|
||||||
|
DecodingError(#[from] rlp::DecoderError),
|
||||||
|
/// When recovering the address from a signature
|
||||||
|
#[error(transparent)]
|
||||||
|
RecoveryError(#[from] SignatureError),
|
||||||
|
}
|
||||||
|
|
||||||
/// An EIP-2930 transaction is a legacy transaction including an [`AccessList`].
|
/// An EIP-2930 transaction is a legacy transaction including an [`AccessList`].
|
||||||
#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
|
#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
|
||||||
pub struct Eip2930TransactionRequest {
|
pub struct Eip2930TransactionRequest {
|
||||||
|
@ -104,28 +118,36 @@ impl Eip2930TransactionRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decodes fields based on the RLP offset passed.
|
/// Decodes fields based on the RLP offset passed.
|
||||||
fn decode_base_rlp(rlp: &rlp::Rlp, offset: &mut usize) -> Result<Self, DecoderError> {
|
fn decode_base_rlp(rlp: &rlp::Rlp, offset: &mut usize) -> Result<Self, rlp::DecoderError> {
|
||||||
let request = TransactionRequest::decode_unsigned_rlp_base(rlp, offset)?;
|
let chain_id: u64 = rlp.val_at(*offset)?;
|
||||||
let access_list = rlp.val_at(*offset)?;
|
*offset += 1;
|
||||||
|
|
||||||
|
let mut request = TransactionRequest::decode_unsigned_rlp_base(rlp, offset)?;
|
||||||
|
request.chain_id = Some(U64::from(chain_id));
|
||||||
|
|
||||||
|
let al = rlp::Rlp::new(rlp.at(*offset)?.as_raw()).data()?;
|
||||||
|
let access_list = match al.len() {
|
||||||
|
0 => AccessList(vec![]),
|
||||||
|
_ => rlp.val_at(*offset)?,
|
||||||
|
};
|
||||||
*offset += 1;
|
*offset += 1;
|
||||||
|
|
||||||
Ok(Self { tx: request, access_list })
|
Ok(Self { tx: request, access_list })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decodes the given RLP into a transaction, attempting to decode its signature as well.
|
/// Decodes the given RLP into a transaction, attempting to decode its signature as well.
|
||||||
pub fn decode_signed_rlp(rlp: &rlp::Rlp) -> Result<(Self, Signature), rlp::DecoderError> {
|
pub fn decode_signed_rlp(rlp: &rlp::Rlp) -> Result<(Self, Signature), Eip2930RequestError> {
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
let mut txn = Self::decode_base_rlp(rlp, &mut offset)?;
|
let mut txn = Self::decode_base_rlp(rlp, &mut offset)?;
|
||||||
|
|
||||||
let v = rlp.at(offset)?.as_val()?;
|
let v = rlp.val_at(offset)?;
|
||||||
// populate chainid from v
|
|
||||||
txn.tx.chain_id = extract_chain_id(v);
|
|
||||||
offset += 1;
|
offset += 1;
|
||||||
let r = rlp.at(offset)?.as_val()?;
|
let r = rlp.val_at(offset)?;
|
||||||
offset += 1;
|
offset += 1;
|
||||||
let s = rlp.at(offset)?.as_val()?;
|
let s = rlp.val_at(offset)?;
|
||||||
|
|
||||||
let sig = Signature { r, s, v };
|
let sig = Signature { r, s, v };
|
||||||
|
txn.tx.from = Some(sig.recover(TypedTransaction::Eip2930(txn.clone()).sighash())?);
|
||||||
Ok((txn, sig))
|
Ok((txn, sig))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,6 +173,7 @@ mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::types::{transaction::eip2718::TypedTransaction, U256};
|
use crate::types::{transaction::eip2718::TypedTransaction, U256};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(feature = "celo", ignore)]
|
#[cfg_attr(feature = "celo", ignore)]
|
||||||
|
@ -198,4 +221,84 @@ mod tests {
|
||||||
let de: Eip2930TransactionRequest = serde_json::from_str(&serialized).unwrap();
|
let de: Eip2930TransactionRequest = serde_json::from_str(&serialized).unwrap();
|
||||||
assert_eq!(tx, TypedTransaction::Eip2930(de));
|
assert_eq!(tx, TypedTransaction::Eip2930(de));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(feature = "celo", ignore)]
|
||||||
|
fn decoding_eip2930_signed() {
|
||||||
|
let raw_tx = hex::decode("01f901ef018209068508d8f9fc0083124f8094f5b4f13bdbe12709bd3ea280ebf4b936e99b20f280b90184c5d404940000000000000000000000000000000000000000000000000c4d67a76e15d8190000000000000000000000000000000000000000000000000029d9d8fb7440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000007b73644935b8e68019ac6356c40661e1bc315860000000000000000000000000761d38e5ddf6ccf6cf7c55759d5210750b5d60f30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000381fe4eb128db1621647ca00965da3f9e09f4fac000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000ac001a0881e7f5298290794bcaa0294986db5c375cbf135dd3c21456b159c470568b687a061fc5f52abab723053fbedf29e1c60b89006416d6c86e1c54ef85a3e84f2dc6e").unwrap();
|
||||||
|
let expected_tx = TransactionRequest::new()
|
||||||
|
.chain_id(1u64)
|
||||||
|
.nonce(2310u64)
|
||||||
|
.gas_price(38_000_000_000u64)
|
||||||
|
.gas(1_200_000u64)
|
||||||
|
.to(Address::from_str("0xf5b4f13bdbe12709bd3ea280ebf4b936e99b20f2").unwrap())
|
||||||
|
.value(0u64)
|
||||||
|
.data(hex::decode("c5d404940000000000000000000000000000000000000000000000000c4d67a76e15d8190000000000000000000000000000000000000000000000000029d9d8fb7440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000007b73644935b8e68019ac6356c40661e1bc315860000000000000000000000000761d38e5ddf6ccf6cf7c55759d5210750b5d60f30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000381fe4eb128db1621647ca00965da3f9e09f4fac000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000a").unwrap())
|
||||||
|
.from(Address::from_str("0x82a33964706683db62b85a59128ce2fc07c91658").unwrap())
|
||||||
|
.with_access_list(AccessList(vec![]));
|
||||||
|
let r =
|
||||||
|
U256::from_str("0x881e7f5298290794bcaa0294986db5c375cbf135dd3c21456b159c470568b687")
|
||||||
|
.unwrap();
|
||||||
|
let s =
|
||||||
|
U256::from_str("0x61fc5f52abab723053fbedf29e1c60b89006416d6c86e1c54ef85a3e84f2dc6e")
|
||||||
|
.unwrap();
|
||||||
|
let v = 1;
|
||||||
|
let expected_sig = Signature { r, s, v };
|
||||||
|
|
||||||
|
let raw_tx_rlp = rlp::Rlp::new(&raw_tx[..]);
|
||||||
|
|
||||||
|
let (real_tx, real_sig) = TypedTransaction::decode_signed(&raw_tx_rlp).unwrap();
|
||||||
|
let real_tx = match real_tx {
|
||||||
|
TypedTransaction::Eip2930(tx) => tx,
|
||||||
|
_ => panic!("The raw bytes should decode to an EIP2930 tranaction"),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(expected_tx, real_tx);
|
||||||
|
assert_eq!(expected_sig, real_sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(feature = "celo", ignore)]
|
||||||
|
fn decoding_eip2930_with_access_list() {
|
||||||
|
let raw_tx = hex::decode("01f90126018223ff850a02ffee00830f4240940000000000a8fb09af944ab3baf7a9b3e1ab29d880b876200200001525000000000b69ffb300000000557b933a7c2c45672b610f8954a3deb39a51a8cae53ec727dbdeb9e2d5456c3be40cff031ab40a55724d5c9c618a2152e99a45649a3b8cf198321f46720b722f4ec38f99ba3bb1303258d2e816e6a95b25647e01bd0967c1b9599fa3521939871d1d0888f845d694724d5c9c618a2152e99a45649a3b8cf198321f46c0d694720b722f4ec38f99ba3bb1303258d2e816e6a95bc0d69425647e01bd0967c1b9599fa3521939871d1d0888c001a08323efae7b9993bd31a58da7924359d24b5504aa2b33194fcc5ae206e65d2e62a054ce201e3b4b5cd38eb17c56ee2f9111b2e164efcd57b3e70fa308a0a51f7014").unwrap();
|
||||||
|
let expected_tx = TransactionRequest::new()
|
||||||
|
.chain_id(1u64)
|
||||||
|
.nonce(9215u64)
|
||||||
|
.gas_price(43_000_000_000u64)
|
||||||
|
.gas(1_000_000)
|
||||||
|
.to(Address::from_str("0x0000000000a8fb09af944ab3baf7a9b3e1ab29d8").unwrap())
|
||||||
|
.value(0)
|
||||||
|
.data(Bytes::from_str("0x200200001525000000000b69ffb300000000557b933a7c2c45672b610f8954a3deb39a51a8cae53ec727dbdeb9e2d5456c3be40cff031ab40a55724d5c9c618a2152e99a45649a3b8cf198321f46720b722f4ec38f99ba3bb1303258d2e816e6a95b25647e01bd0967c1b9599fa3521939871d1d0888").unwrap())
|
||||||
|
.from(Address::from_str("0xe9c790e8fde820ded558a4771b72eec916c04763").unwrap())
|
||||||
|
.with_access_list(AccessList(vec![
|
||||||
|
AccessListItem {
|
||||||
|
address: Address::from_str("0x724d5c9c618a2152e99a45649a3b8cf198321f46").unwrap(),
|
||||||
|
storage_keys: vec![],
|
||||||
|
},
|
||||||
|
AccessListItem {
|
||||||
|
address: Address::from_str("0x720b722f4ec38f99ba3bb1303258d2e816e6a95b").unwrap(),
|
||||||
|
storage_keys: vec![],
|
||||||
|
},
|
||||||
|
AccessListItem {
|
||||||
|
address: Address::from_str("0x25647e01bd0967c1b9599fa3521939871d1d0888").unwrap(),
|
||||||
|
storage_keys: vec![],
|
||||||
|
},
|
||||||
|
]));
|
||||||
|
let expected_sig = Signature {
|
||||||
|
r: "0x8323efae7b9993bd31a58da7924359d24b5504aa2b33194fcc5ae206e65d2e62".into(),
|
||||||
|
s: "0x54ce201e3b4b5cd38eb17c56ee2f9111b2e164efcd57b3e70fa308a0a51f7014".into(),
|
||||||
|
v: 1u64,
|
||||||
|
};
|
||||||
|
|
||||||
|
let raw_tx_rlp = rlp::Rlp::new(&raw_tx[..]);
|
||||||
|
|
||||||
|
let (real_tx, real_sig) = TypedTransaction::decode_signed(&raw_tx_rlp).unwrap();
|
||||||
|
let real_tx = match real_tx {
|
||||||
|
TypedTransaction::Eip2930(tx) => tx,
|
||||||
|
_ => panic!("The raw bytes should decode to an EIP2930 tranaction"),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(expected_tx, real_tx);
|
||||||
|
assert_eq!(expected_sig, real_sig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,15 +250,14 @@ impl TransactionRequest {
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
let mut txn = Self::decode_unsigned_rlp_base(rlp, &mut offset)?;
|
let mut txn = Self::decode_unsigned_rlp_base(rlp, &mut offset)?;
|
||||||
|
|
||||||
// If a signed transaction is passed to this method, the chainid would be set to the v value
|
// If the transaction includes more info, like the chainid, as we serialize in `rlp`, this
|
||||||
// of the signature.
|
// will decode that value.
|
||||||
if let Ok(chainid) = rlp.at(offset)?.as_val() {
|
if let Ok(chainid) = rlp.val_at(offset) {
|
||||||
|
// If a signed transaction is passed to this method, the chainid would be set to the v
|
||||||
|
// value of the signature.
|
||||||
txn.chain_id = Some(chainid);
|
txn.chain_id = Some(chainid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse the last two elements so we return an error if a signed transaction is passed
|
|
||||||
let _first_zero: u8 = rlp.at(offset + 1)?.as_val()?;
|
|
||||||
let _second_zero: u8 = rlp.at(offset + 2)?.as_val()?;
|
|
||||||
Ok(txn)
|
Ok(txn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,7 +350,7 @@ impl TransactionRequest {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[cfg(not(feature = "celo"))]
|
#[cfg(not(feature = "celo"))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::types::{NameOrAddress, Signature};
|
use crate::types::{Bytes, NameOrAddress, Signature};
|
||||||
use rlp::{Decodable, Rlp};
|
use rlp::{Decodable, Rlp};
|
||||||
|
|
||||||
use super::{Address, TransactionRequest, U256, U64};
|
use super::{Address, TransactionRequest, U256, U64};
|
||||||
|
@ -429,6 +428,31 @@ mod tests {
|
||||||
assert_eq!(got_tx.sighash(), tx.sighash());
|
assert_eq!(got_tx.sighash(), tx.sighash());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decode_unsigned_rlp_no_chainid() {
|
||||||
|
// unlike the corresponding transaction
|
||||||
|
// 0x02c563d96acaf8c157d08db2228c84836faaf3dd513fc959a54ed4ca6c72573e, this doesn't have a
|
||||||
|
// `from` field because the `from` field is only obtained via signature recovery
|
||||||
|
let expected_tx = TransactionRequest::new()
|
||||||
|
.to(Address::from_str("0xc7696b27830dd8aa4823a1cba8440c27c36adec4").unwrap())
|
||||||
|
.gas(3_000_000)
|
||||||
|
.gas_price(20_000_000_000u64)
|
||||||
|
.value(0)
|
||||||
|
.nonce(6306u64)
|
||||||
|
.data(
|
||||||
|
Bytes::from_str(
|
||||||
|
"0x91b7f5ed0000000000000000000000000000000000000000000000000000000000000372",
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// manually stripped the signature off the end and modified length
|
||||||
|
let expected_rlp = hex::decode("f8488218a28504a817c800832dc6c094c7696b27830dd8aa4823a1cba8440c27c36adec480a491b7f5ed0000000000000000000000000000000000000000000000000000000000000372").unwrap();
|
||||||
|
let real_tx = TransactionRequest::decode(&Rlp::new(&expected_rlp)).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(real_tx, expected_tx);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_eip155_encode() {
|
fn test_eip155_encode() {
|
||||||
let tx = TransactionRequest::new()
|
let tx = TransactionRequest::new()
|
||||||
|
|
Loading…
Reference in New Issue