mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(primitives): transaction encoding and decoding (#102)
* feat(core): transaction encoding and decoding * cargo fmt * remove printlns * fix tx conversion
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
use reth_interfaces::executor::ExecutorDb;
|
||||
use reth_primitives::{BlockLocked, Transaction, H160, H256, U256};
|
||||
use reth_primitives::{BlockLocked, Transaction, TransactionKind, H160, H256, U256};
|
||||
use revm::{
|
||||
db::{CacheDB, Database, EmptyDB},
|
||||
BlockEnv, TransactTo, TxEnv,
|
||||
@ -58,8 +58,10 @@ pub fn fill_tx_env(tx_env: &mut TxEnv, transaction: &Transaction) {
|
||||
tx_env.gas_limit = *gas_limit;
|
||||
tx_env.gas_price = (*gas_price).into();
|
||||
tx_env.gas_priority_fee = None;
|
||||
tx_env.transact_to =
|
||||
if let Some(to) = to { TransactTo::Call(*to) } else { TransactTo::create() };
|
||||
tx_env.transact_to = match to {
|
||||
TransactionKind::Call(to) => TransactTo::Call(*to),
|
||||
TransactionKind::Create => TransactTo::create(),
|
||||
};
|
||||
tx_env.value = *value;
|
||||
tx_env.data = input.0.clone();
|
||||
tx_env.chain_id = *chain_id;
|
||||
@ -78,13 +80,16 @@ pub fn fill_tx_env(tx_env: &mut TxEnv, transaction: &Transaction) {
|
||||
tx_env.gas_limit = *gas_limit;
|
||||
tx_env.gas_price = (*gas_price).into();
|
||||
tx_env.gas_priority_fee = None;
|
||||
tx_env.transact_to =
|
||||
if let Some(to) = to { TransactTo::Call(*to) } else { TransactTo::create() };
|
||||
tx_env.transact_to = match to {
|
||||
TransactionKind::Call(to) => TransactTo::Call(*to),
|
||||
TransactionKind::Create => TransactTo::create(),
|
||||
};
|
||||
tx_env.value = *value;
|
||||
tx_env.data = input.0.clone();
|
||||
tx_env.chain_id = Some(*chain_id);
|
||||
tx_env.nonce = Some(*nonce);
|
||||
tx_env.access_list = access_list
|
||||
.0
|
||||
.iter()
|
||||
.map(|l| {
|
||||
(
|
||||
@ -108,13 +113,16 @@ pub fn fill_tx_env(tx_env: &mut TxEnv, transaction: &Transaction) {
|
||||
tx_env.gas_limit = *gas_limit;
|
||||
tx_env.gas_price = (*max_fee_per_gas).into();
|
||||
tx_env.gas_priority_fee = Some((*max_priority_fee_per_gas).into());
|
||||
tx_env.transact_to =
|
||||
if let Some(to) = to { TransactTo::Call(*to) } else { TransactTo::create() };
|
||||
tx_env.transact_to = match to {
|
||||
TransactionKind::Call(to) => TransactTo::Call(*to),
|
||||
TransactionKind::Create => TransactTo::create(),
|
||||
};
|
||||
tx_env.value = *value;
|
||||
tx_env.data = input.0.clone();
|
||||
tx_env.chain_id = Some(*chain_id);
|
||||
tx_env.nonce = Some(*nonce);
|
||||
tx_env.access_list = access_list
|
||||
.0
|
||||
.iter()
|
||||
.map(|l| {
|
||||
(
|
||||
|
||||
@ -7,7 +7,7 @@ use std::ops::Deref;
|
||||
|
||||
/// Block header
|
||||
#[main_codec]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash)]
|
||||
pub struct Header {
|
||||
/// The Keccak 256-bit hash of the parent
|
||||
/// block’s header, in its entirety; formally Hp.
|
||||
@ -174,7 +174,7 @@ impl Decodable for Header {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash)]
|
||||
/// HeaderLocked that has precalculated hash, use unlock if you want to modify header.
|
||||
pub struct HeaderLocked {
|
||||
/// Locked Header fields.
|
||||
@ -213,7 +213,7 @@ impl HeaderLocked {
|
||||
mod tests {
|
||||
use crate::Address;
|
||||
|
||||
use super::{Decodable, Encodable, Header, H160, H256};
|
||||
use super::{Decodable, Encodable, Header, H256};
|
||||
use ethers_core::{
|
||||
types::Bytes,
|
||||
utils::hex::{self, FromHex},
|
||||
|
||||
@ -23,7 +23,9 @@ pub use header::{Header, HeaderLocked};
|
||||
pub use jsonu256::JsonU256;
|
||||
pub use log::Log;
|
||||
pub use receipt::Receipt;
|
||||
pub use transaction::{AccessList, AccessListItem, Transaction, TransactionSigned, TxType};
|
||||
pub use transaction::{
|
||||
AccessList, AccessListItem, Transaction, TransactionKind, TransactionSigned, TxType,
|
||||
};
|
||||
|
||||
/// Block hash.
|
||||
pub type BlockHash = H256;
|
||||
@ -45,5 +47,5 @@ pub type StorageValue = H256;
|
||||
// NOTE: There is a benefit of using wrapped Bytes as it gives us serde and debug
|
||||
pub use ethers_core::{
|
||||
types as rpc,
|
||||
types::{Bloom, Bytes, H128, H160, H256, H512, H64, U256, U64},
|
||||
types::{Bloom, Bytes, H128, H160, H256, H512, H64, U128, U256, U64},
|
||||
};
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
use reth_rlp::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper};
|
||||
|
||||
use crate::{Address, H256};
|
||||
|
||||
/// A list of addresses and storage keys that the transaction plans to access.
|
||||
/// Accesses outside the list are possible, but become more expensive.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, RlpDecodable, RlpEncodable)]
|
||||
pub struct AccessListItem {
|
||||
/// Account addresses that would be loaded at the start of execution
|
||||
pub address: Address,
|
||||
@ -11,4 +13,5 @@ pub struct AccessListItem {
|
||||
}
|
||||
|
||||
/// AccessList as defined in EIP-2930
|
||||
pub type AccessList = Vec<AccessListItem>;
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, RlpDecodableWrapper, RlpEncodableWrapper)]
|
||||
pub struct AccessList(pub Vec<AccessListItem>);
|
||||
|
||||
@ -4,13 +4,16 @@ mod tx_type;
|
||||
|
||||
use crate::{Address, Bytes, TxHash, U256};
|
||||
pub use access_list::{AccessList, AccessListItem};
|
||||
use bytes::Buf;
|
||||
use ethers_core::utils::keccak256;
|
||||
use reth_rlp::{length_of_length, Decodable, DecodeError, Encodable, Header, EMPTY_STRING_CODE};
|
||||
use signature::Signature;
|
||||
use std::ops::Deref;
|
||||
pub use tx_type::TxType;
|
||||
|
||||
/// Raw Transaction.
|
||||
/// Transaction type is introduced in EIP-2718: https://eips.ethereum.org/EIPS/eip-2718
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Transaction {
|
||||
/// Legacy transaciton.
|
||||
Legacy {
|
||||
@ -30,7 +33,7 @@ pub enum Transaction {
|
||||
gas_limit: u64,
|
||||
/// The 160-bit address of the message call’s recipient or, for a contract creation
|
||||
/// transaction, ∅, used here to denote the only member of B0 ; formally Tt.
|
||||
to: Option<Address>,
|
||||
to: TransactionKind,
|
||||
/// A scalar value equal to the number of Wei to
|
||||
/// be transferred to the message call’s recipient or,
|
||||
/// in the case of contract creation, as an endowment
|
||||
@ -61,7 +64,7 @@ pub enum Transaction {
|
||||
gas_limit: u64,
|
||||
/// The 160-bit address of the message call’s recipient or, for a contract creation
|
||||
/// transaction, ∅, used here to denote the only member of B0 ; formally Tt.
|
||||
to: Option<Address>,
|
||||
to: TransactionKind,
|
||||
/// A scalar value equal to the number of Wei to
|
||||
/// be transferred to the message call’s recipient or,
|
||||
/// in the case of contract creation, as an endowment
|
||||
@ -102,7 +105,7 @@ pub enum Transaction {
|
||||
max_priority_fee_per_gas: u64,
|
||||
/// The 160-bit address of the message call’s recipient or, for a contract creation
|
||||
/// transaction, ∅, used here to denote the only member of B0 ; formally Tt.
|
||||
to: Option<Address>,
|
||||
to: TransactionKind,
|
||||
/// A scalar value equal to the number of Wei to
|
||||
/// be transferred to the message call’s recipient or,
|
||||
/// in the case of contract creation, as an endowment
|
||||
@ -127,13 +130,262 @@ impl Transaction {
|
||||
/// Heavy operation that return hash over rlp encoded transaction.
|
||||
/// It is only used for signature signing.
|
||||
pub fn signature_hash(&self) -> TxHash {
|
||||
todo!()
|
||||
let mut encoded = vec![];
|
||||
self.encode(&mut encoded);
|
||||
keccak256(encoded).into()
|
||||
}
|
||||
|
||||
/// Sets the transaction's chain id to the provided value.
|
||||
pub fn set_chain_id(&mut self, chain_id: u64) {
|
||||
match self {
|
||||
Transaction::Legacy { chain_id: ref mut c, .. } => *c = Some(chain_id),
|
||||
Transaction::Eip2930 { chain_id: ref mut c, .. } => *c = chain_id,
|
||||
Transaction::Eip1559 { chain_id: ref mut c, .. } => *c = chain_id,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the transaction's [`TransactionKind`], which is the address of the recipient or
|
||||
/// [`TransactionKind::Create`] if the transaction is a contract creation.
|
||||
pub fn kind(&self) -> &TransactionKind {
|
||||
match self {
|
||||
Transaction::Legacy { to, .. } => to,
|
||||
Transaction::Eip2930 { to, .. } => to,
|
||||
Transaction::Eip1559 { to, .. } => to,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the transaction's value field.
|
||||
pub fn value(&self) -> &U256 {
|
||||
match self {
|
||||
Transaction::Legacy { value, .. } => value,
|
||||
Transaction::Eip2930 { value, .. } => value,
|
||||
Transaction::Eip1559 { value, .. } => value,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the transaction's nonce.
|
||||
pub fn nonce(&self) -> u64 {
|
||||
match self {
|
||||
Transaction::Legacy { nonce, .. } => *nonce,
|
||||
Transaction::Eip2930 { nonce, .. } => *nonce,
|
||||
Transaction::Eip1559 { nonce, .. } => *nonce,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the transaction's input field.
|
||||
pub fn input(&self) -> &Bytes {
|
||||
match self {
|
||||
Transaction::Legacy { input, .. } => input,
|
||||
Transaction::Eip2930 { input, .. } => input,
|
||||
Transaction::Eip1559 { input, .. } => input,
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes individual transaction fields into the desired buffer, without a RLP header or
|
||||
/// EIP-155 fields.
|
||||
pub(crate) fn encode_inner(&self, out: &mut dyn bytes::BufMut) {
|
||||
match self {
|
||||
Transaction::Legacy { chain_id: _, nonce, gas_price, gas_limit, to, value, input } => {
|
||||
nonce.encode(out);
|
||||
gas_price.encode(out);
|
||||
gas_limit.encode(out);
|
||||
to.encode(out);
|
||||
value.encode(out);
|
||||
input.0.encode(out);
|
||||
}
|
||||
Transaction::Eip2930 {
|
||||
chain_id,
|
||||
nonce,
|
||||
gas_price,
|
||||
gas_limit,
|
||||
to,
|
||||
value,
|
||||
input,
|
||||
access_list,
|
||||
} => {
|
||||
out.put_u8(1);
|
||||
chain_id.encode(out);
|
||||
nonce.encode(out);
|
||||
gas_price.encode(out);
|
||||
gas_limit.encode(out);
|
||||
to.encode(out);
|
||||
value.encode(out);
|
||||
input.0.encode(out);
|
||||
access_list.encode(out);
|
||||
}
|
||||
Transaction::Eip1559 {
|
||||
chain_id,
|
||||
nonce,
|
||||
gas_limit,
|
||||
max_fee_per_gas,
|
||||
max_priority_fee_per_gas,
|
||||
to,
|
||||
value,
|
||||
input,
|
||||
access_list,
|
||||
} => {
|
||||
out.put_u8(2);
|
||||
chain_id.encode(out);
|
||||
nonce.encode(out);
|
||||
gas_limit.encode(out);
|
||||
max_fee_per_gas.encode(out);
|
||||
max_priority_fee_per_gas.encode(out);
|
||||
to.encode(out);
|
||||
value.encode(out);
|
||||
input.0.encode(out);
|
||||
access_list.encode(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes EIP-155 arguments into the desired buffer. Only encodes values for legacy
|
||||
/// transactions.
|
||||
pub(crate) fn encode_eip155_fields(&self, out: &mut dyn bytes::BufMut) {
|
||||
// if this is a legacy transaction without a chain ID, it must be pre-EIP-155
|
||||
// and does not need to encode the chain ID for the signature hash encoding
|
||||
if let Transaction::Legacy { chain_id: Some(id), .. } = self {
|
||||
// EIP-155 encodes the chain ID and two zeroes
|
||||
id.encode(out);
|
||||
0x00u8.encode(out);
|
||||
0x00u8.encode(out);
|
||||
}
|
||||
}
|
||||
|
||||
/// Outputs the length of EIP-155 fields. Only outputs a non-zero value for EIP-155 legacy
|
||||
/// transactions.
|
||||
pub(crate) fn eip155_fields_len(&self) -> usize {
|
||||
if let Transaction::Legacy { chain_id: Some(id), .. } = self {
|
||||
// EIP-155 encodes the chain ID and two zeroes, so we add 2 to the length of the chain
|
||||
// ID to get the length of all 3 fields
|
||||
// len(chain_id) + (0x00) + (0x00)
|
||||
id.length() + 2
|
||||
} else {
|
||||
// this is either a pre-EIP-155 legacy transaction or a typed transaction
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Outputs the length of the transaction payload without the length of the RLP header or
|
||||
/// EIP-155 fields.
|
||||
pub(crate) fn payload_len(&self) -> usize {
|
||||
match self {
|
||||
Transaction::Legacy { chain_id: _, nonce, gas_price, gas_limit, to, value, input } => {
|
||||
let mut len = 0;
|
||||
len += nonce.length();
|
||||
len += gas_price.length();
|
||||
len += gas_limit.length();
|
||||
len += to.length();
|
||||
len += value.length();
|
||||
len += input.0.length();
|
||||
len
|
||||
}
|
||||
Transaction::Eip2930 {
|
||||
chain_id,
|
||||
nonce,
|
||||
gas_price,
|
||||
gas_limit,
|
||||
to,
|
||||
value,
|
||||
input,
|
||||
access_list,
|
||||
} => {
|
||||
let mut len = 0;
|
||||
len += chain_id.length();
|
||||
len += nonce.length();
|
||||
len += gas_price.length();
|
||||
len += gas_limit.length();
|
||||
len += to.length();
|
||||
len += value.length();
|
||||
len += input.0.length();
|
||||
len += access_list.length();
|
||||
// add 1 for the transaction type
|
||||
len + 1
|
||||
}
|
||||
Transaction::Eip1559 {
|
||||
chain_id,
|
||||
nonce,
|
||||
gas_limit,
|
||||
max_fee_per_gas,
|
||||
max_priority_fee_per_gas,
|
||||
to,
|
||||
value,
|
||||
input,
|
||||
access_list,
|
||||
} => {
|
||||
let mut len = 0;
|
||||
len += chain_id.length();
|
||||
len += nonce.length();
|
||||
len += gas_limit.length();
|
||||
len += max_fee_per_gas.length();
|
||||
len += max_priority_fee_per_gas.length();
|
||||
len += to.length();
|
||||
len += value.length();
|
||||
len += input.0.length();
|
||||
len += access_list.length();
|
||||
// add 1 for the transaction type
|
||||
len + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This encodes the transaction _without_ the signature, and is only suitable for creating a hash
|
||||
/// intended for signing.
|
||||
impl Encodable for Transaction {
|
||||
fn length(&self) -> usize {
|
||||
let len = self.payload_len() + self.eip155_fields_len();
|
||||
len + length_of_length(len)
|
||||
}
|
||||
fn encode(&self, out: &mut dyn bytes::BufMut) {
|
||||
let header = Header { list: true, payload_length: self.length() };
|
||||
header.encode(out);
|
||||
self.encode_inner(out);
|
||||
self.encode_eip155_fields(out);
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether or not the transaction is a contract creation.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum TransactionKind {
|
||||
/// A transaction that creates a contract.
|
||||
Create,
|
||||
/// A transaction that calls a contract or transfer.
|
||||
Call(Address),
|
||||
}
|
||||
|
||||
impl Encodable for TransactionKind {
|
||||
fn length(&self) -> usize {
|
||||
match self {
|
||||
TransactionKind::Call(to) => to.length(),
|
||||
TransactionKind::Create => EMPTY_STRING_CODE.length(),
|
||||
}
|
||||
}
|
||||
fn encode(&self, out: &mut dyn reth_rlp::BufMut) {
|
||||
match self {
|
||||
TransactionKind::Call(to) => to.encode(out),
|
||||
TransactionKind::Create => EMPTY_STRING_CODE.encode(out),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for TransactionKind {
|
||||
fn decode(buf: &mut &[u8]) -> Result<Self, DecodeError> {
|
||||
if let Some(&first) = buf.first() {
|
||||
if first == EMPTY_STRING_CODE {
|
||||
buf.advance(1);
|
||||
Ok(TransactionKind::Create)
|
||||
} else {
|
||||
let addr = <Address as Decodable>::decode(buf)?;
|
||||
Ok(TransactionKind::Call(addr))
|
||||
}
|
||||
} else {
|
||||
Err(DecodeError::InputTooShort)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Signed transaction.
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TransactionSigned {
|
||||
transaction: Transaction,
|
||||
hash: TxHash,
|
||||
@ -154,6 +406,111 @@ impl Deref for TransactionSigned {
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for TransactionSigned {
|
||||
fn length(&self) -> usize {
|
||||
let mut len = self.transaction.payload_len();
|
||||
if let Transaction::Legacy { chain_id: None, .. } = self.transaction {
|
||||
// if the transaction has no chain id then it is a pre-EIP-155 transaction
|
||||
len += self.signature.payload_len();
|
||||
} else {
|
||||
let id = match self.transaction {
|
||||
Transaction::Legacy { chain_id: Some(id), .. } => id,
|
||||
Transaction::Eip2930 { chain_id, .. } => chain_id,
|
||||
Transaction::Eip1559 { chain_id, .. } => chain_id,
|
||||
// we handled this case above
|
||||
_ => unreachable!(
|
||||
"legacy transaction without chain id should have been handled above"
|
||||
),
|
||||
};
|
||||
len += self.signature.eip155_payload_len(id);
|
||||
}
|
||||
|
||||
// add the length of the RLP header
|
||||
len + length_of_length(len)
|
||||
}
|
||||
fn encode(&self, out: &mut dyn bytes::BufMut) {
|
||||
let header = Header { list: true, payload_length: self.length() };
|
||||
header.encode(out);
|
||||
self.transaction.encode_inner(out);
|
||||
if let Transaction::Legacy { chain_id: None, .. } = self.transaction {
|
||||
// if the transaction has no chain id then it is a pre-EIP-155 transaction
|
||||
self.signature.encode_inner(out);
|
||||
} else {
|
||||
let id = match self.transaction {
|
||||
Transaction::Legacy { chain_id: Some(id), .. } => id,
|
||||
Transaction::Eip2930 { chain_id, .. } => chain_id,
|
||||
Transaction::Eip1559 { chain_id, .. } => chain_id,
|
||||
// we handled this case above
|
||||
_ => unreachable!(
|
||||
"legacy transaction without chain id should have been handled above"
|
||||
),
|
||||
};
|
||||
self.signature.encode_eip155_inner(out, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This `Decodable` implementation only supports decoding the transaction format sent over p2p.
|
||||
impl Decodable for TransactionSigned {
|
||||
fn decode(buf: &mut &[u8]) -> Result<Self, DecodeError> {
|
||||
// keep this around so we can use it to calculate the hash
|
||||
let original_encoding = *buf;
|
||||
|
||||
let header = Header::decode(buf)?;
|
||||
// if the transaction is encoded as a string then it is a typed transaction
|
||||
if !header.list {
|
||||
let tx_type = *buf
|
||||
.first()
|
||||
.ok_or(DecodeError::Custom("typed tx cannot be decoded from an empty slice"))?;
|
||||
buf.advance(1);
|
||||
// decode common fields
|
||||
let transaction = match tx_type {
|
||||
1 => Transaction::Eip2930 {
|
||||
chain_id: Decodable::decode(buf)?,
|
||||
nonce: Decodable::decode(buf)?,
|
||||
gas_price: Decodable::decode(buf)?,
|
||||
gas_limit: Decodable::decode(buf)?,
|
||||
to: Decodable::decode(buf)?,
|
||||
value: Decodable::decode(buf)?,
|
||||
input: Bytes(Decodable::decode(buf)?),
|
||||
access_list: Decodable::decode(buf)?,
|
||||
},
|
||||
2 => Transaction::Eip1559 {
|
||||
chain_id: Decodable::decode(buf)?,
|
||||
nonce: Decodable::decode(buf)?,
|
||||
gas_limit: Decodable::decode(buf)?,
|
||||
to: Decodable::decode(buf)?,
|
||||
value: Decodable::decode(buf)?,
|
||||
input: Bytes(Decodable::decode(buf)?),
|
||||
access_list: Decodable::decode(buf)?,
|
||||
max_fee_per_gas: Decodable::decode(buf)?,
|
||||
max_priority_fee_per_gas: Decodable::decode(buf)?,
|
||||
},
|
||||
_ => return Err(DecodeError::Custom("unsupported typed transaction type")),
|
||||
};
|
||||
let (signature, _) = Signature::decode_eip155_inner(buf)?;
|
||||
let hash = keccak256(original_encoding).into();
|
||||
Ok(TransactionSigned { transaction, hash, signature })
|
||||
} else {
|
||||
let mut transaction = Transaction::Legacy {
|
||||
nonce: Decodable::decode(buf)?,
|
||||
gas_price: Decodable::decode(buf)?,
|
||||
gas_limit: Decodable::decode(buf)?,
|
||||
to: Decodable::decode(buf)?,
|
||||
value: Decodable::decode(buf)?,
|
||||
input: Bytes(Decodable::decode(buf)?),
|
||||
chain_id: None,
|
||||
};
|
||||
let (signature, extracted_id) = Signature::decode_eip155_inner(buf)?;
|
||||
if let Some(id) = extracted_id {
|
||||
transaction.set_chain_id(id);
|
||||
}
|
||||
let hash = keccak256(original_encoding).into();
|
||||
Ok(TransactionSigned { transaction, hash, signature })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactionSigned {
|
||||
/// Transaction signature.
|
||||
pub fn signature(&self) -> &Signature {
|
||||
@ -164,4 +521,14 @@ impl TransactionSigned {
|
||||
pub fn hash(&self) -> TxHash {
|
||||
self.hash
|
||||
}
|
||||
|
||||
/// Create a new signed transaction from a transaction and its signature.
|
||||
/// This will also calculate the transaction hash using its encoding.
|
||||
pub fn from_transaction_and_signature(transaction: Transaction, signature: Signature) -> Self {
|
||||
let mut initial_tx = Self { transaction, hash: Default::default(), signature };
|
||||
let mut buf = Vec::new();
|
||||
initial_tx.encode(&mut buf);
|
||||
initial_tx.hash = keccak256(&buf).into();
|
||||
initial_tx
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user