Skip to content

Commit

Permalink
implement PooledTransactionsElement::signature_hash
Browse files Browse the repository at this point in the history
 * refactors out the relevant encoding and payload length methods into
   the individual transaction types
 * implements Transaction::signature_hash with the methods on individual
   types
 * finally implements PooledTransactionsElement::signature_hash using
   these individual methods
  • Loading branch information
Rjected committed Aug 18, 2023
1 parent a2e2380 commit 329c9be
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 77 deletions.
25 changes: 24 additions & 1 deletion crates/primitives/src/transaction/eip1559.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::access_list::AccessList;
use crate::{Bytes, ChainId, Signature, TransactionKind, TxType};
use crate::{keccak256, Bytes, ChainId, Signature, TransactionKind, TxType, H256};
use bytes::BytesMut;
use reth_codecs::{main_codec, Compact};
use reth_rlp::{length_of_length, Decodable, DecodeError, Encodable, Header};
use std::mem;
Expand Down Expand Up @@ -188,6 +189,28 @@ impl TxEip1559 {
self.access_list.size() + // access_list
self.input.len() // input
}

/// Encodes the legacy transaction in RLP for signing.
pub(crate) fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) {
out.put_u8(self.tx_type() as u8);
Header { list: true, payload_length: self.fields_len() }.encode(out);
self.encode_fields(out);
}

/// Outputs the length of the signature RLP encoding for the transaction.
pub(crate) fn payload_len_for_signature(&self) -> usize {
let payload_length = self.fields_len();
// 'transaction type byte length' + 'header length' + 'payload length'
1 + length_of_length(payload_length) + payload_length
}

/// Outputs the signature hash of the transaction by first encoding without a signature, then
/// hashing.
pub(crate) fn signature_hash(&self) -> H256 {
let mut buf = BytesMut::with_capacity(self.payload_len_for_signature());
self.encode_for_signing(&mut buf);
keccak256(&buf)
}
}

#[cfg(test)]
Expand Down
25 changes: 24 additions & 1 deletion crates/primitives/src/transaction/eip2930.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::access_list::AccessList;
use crate::{Bytes, ChainId, Signature, TransactionKind, TxType};
use crate::{keccak256, Bytes, ChainId, Signature, TransactionKind, TxType, H256};
use bytes::BytesMut;
use reth_codecs::{main_codec, Compact};
use reth_rlp::{length_of_length, Decodable, DecodeError, Encodable, Header};
use std::mem;
Expand Down Expand Up @@ -153,6 +154,28 @@ impl TxEip2930 {
pub(crate) fn tx_type(&self) -> TxType {
TxType::EIP2930
}

/// Encodes the legacy transaction in RLP for signing.
pub(crate) fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) {
out.put_u8(self.tx_type() as u8);
Header { list: true, payload_length: self.fields_len() }.encode(out);
self.encode_fields(out);
}

/// Outputs the length of the signature RLP encoding for the transaction.
pub(crate) fn payload_len_for_signature(&self) -> usize {
let payload_length = self.fields_len();
// 'transaction type byte length' + 'header length' + 'payload length'
1 + length_of_length(payload_length) + payload_length
}

/// Outputs the signature hash of the transaction by first encoding without a signature, then
/// hashing.
pub(crate) fn signature_hash(&self) -> H256 {
let mut buf = BytesMut::with_capacity(self.payload_len_for_signature());
self.encode_for_signing(&mut buf);
keccak256(&buf)
}
}

#[cfg(test)]
Expand Down
23 changes: 23 additions & 0 deletions crates/primitives/src/transaction/eip4844.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::{
kzg_to_versioned_hash, Bytes, ChainId, Signature, Transaction, TransactionKind,
TransactionSigned, TxHash, TxType, EIP4844_TX_TYPE_ID, H256,
};
use bytes::BytesMut;
use reth_codecs::{main_codec, Compact};
use reth_rlp::{length_of_length, Decodable, DecodeError, Encodable, Header};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -226,6 +227,28 @@ impl TxEip4844 {
pub(crate) fn tx_type(&self) -> TxType {
TxType::EIP4844
}

/// Encodes the legacy transaction in RLP for signing.
pub(crate) fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) {
out.put_u8(self.tx_type() as u8);
Header { list: true, payload_length: self.fields_len() }.encode(out);
self.encode_fields(out);
}

/// Outputs the length of the signature RLP encoding for the transaction.
pub(crate) fn payload_len_for_signature(&self) -> usize {
let payload_length = self.fields_len();
// 'transaction type byte length' + 'header length' + 'payload length'
1 + length_of_length(payload_length) + payload_length
}

/// Outputs the signature hash of the transaction by first encoding without a signature, then
/// hashing.
pub(crate) fn signature_hash(&self) -> H256 {
let mut buf = BytesMut::with_capacity(self.payload_len_for_signature());
self.encode_for_signing(&mut buf);
keccak256(&buf)
}
}

/// An error that can occur when validating a [BlobTransaction].
Expand Down
56 changes: 55 additions & 1 deletion crates/primitives/src/transaction/legacy.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{Bytes, ChainId, Signature, TransactionKind, TxType};
use crate::{keccak256, Bytes, ChainId, Signature, TransactionKind, TxType, H256};
use bytes::BytesMut;
use reth_codecs::{main_codec, Compact};
use reth_rlp::{length_of_length, Encodable, Header};
use std::mem;
Expand Down Expand Up @@ -105,6 +106,59 @@ impl TxLegacy {
pub(crate) fn tx_type(&self) -> TxType {
TxType::Legacy
}

/// 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 Some(id) = self.chain_id {
// 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 Some(id) = self.chain_id {
// 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
}
}

/// Encodes the legacy transaction in RLP for signing, including the EIP-155 fields if possible.
pub(crate) fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) {
Header { list: true, payload_length: self.fields_len() + self.eip155_fields_len() }
.encode(out);
self.encode_fields(out);
self.encode_eip155_fields(out);
}

/// Outputs the length of the signature RLP encoding for the transaction, including the length
/// of the EIP-155 fields if possible.
pub(crate) fn payload_len_for_signature(&self) -> usize {
let payload_length = self.fields_len() + self.eip155_fields_len();
// 'header length' + 'payload length'
length_of_length(payload_length) + payload_length
}

/// Outputs the signature hash of the transaction by first encoding without a signature, then
/// hashing.
///
/// See [LegacyTx::encode_for_signing] for more information on the encoding format.
pub(crate) fn signature_hash(&self) -> H256 {
let mut buf = BytesMut::with_capacity(self.payload_len_for_signature());
self.encode_for_signing(&mut buf);
keccak256(&buf)
}
}

#[cfg(test)]
Expand Down
94 changes: 21 additions & 73 deletions crates/primitives/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ pub use meta::TransactionMeta;
use once_cell::sync::Lazy;
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
use reth_codecs::{add_arbitrary_tests, derive_arbitrary, Compact};
use reth_rlp::{
length_of_length, Decodable, DecodeError, Encodable, Header, EMPTY_LIST_CODE, EMPTY_STRING_CODE,
};
use reth_rlp::{Decodable, DecodeError, Encodable, Header, EMPTY_LIST_CODE, EMPTY_STRING_CODE};
use serde::{Deserialize, Serialize};
pub use signature::Signature;
use std::mem;
Expand Down Expand Up @@ -100,9 +98,12 @@ impl Transaction {
/// Heavy operation that return signature hash over rlp encoded transaction.
/// It is only for signature signing or signer recovery.
pub fn signature_hash(&self) -> H256 {
let mut buf = BytesMut::new();
self.encode(&mut buf);
keccak256(&buf)
match self {
Transaction::Legacy(tx) => tx.signature_hash(),
Transaction::Eip2930(tx) => tx.signature_hash(),
Transaction::Eip1559(tx) => tx.signature_hash(),
Transaction::Eip4844(tx) => tx.signature_hash(),
}
}

/// Get chain_id.
Expand Down Expand Up @@ -316,54 +317,6 @@ impl Transaction {
}
}

/// 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(TxLegacy { 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(TxLegacy { 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's fields, without a RLP header or length of the
/// eip155 fields.
pub(crate) fn fields_len(&self) -> usize {
match self {
Transaction::Legacy(legacy_tx) => legacy_tx.fields_len(),
Transaction::Eip2930(access_list_tx) => access_list_tx.fields_len(),
Transaction::Eip1559(dynamic_fee_tx) => dynamic_fee_tx.fields_len(),
Transaction::Eip4844(blob_tx) => blob_tx.fields_len(),
}
}

/// Encodes only the transaction's fields into the desired buffer, without a RLP header.
pub(crate) fn encode_fields(&self, out: &mut dyn bytes::BufMut) {
match self {
Transaction::Legacy(legacy_tx) => legacy_tx.encode_fields(out),
Transaction::Eip2930(access_list_tx) => access_list_tx.encode_fields(out),
Transaction::Eip1559(dynamic_fee_tx) => dynamic_fee_tx.encode_fields(out),
Transaction::Eip4844(blob_tx) => blob_tx.encode_fields(out),
}
}

/// This encodes the transaction _without_ the signature, and is only suitable for creating a
/// hash intended for signing.
pub fn encode_without_signature(&self, out: &mut dyn bytes::BufMut) {
Expand Down Expand Up @@ -496,32 +449,27 @@ impl Default for Transaction {
impl Encodable for Transaction {
fn encode(&self, out: &mut dyn bytes::BufMut) {
match self {
Transaction::Legacy { .. } => {
Header { list: true, payload_length: self.fields_len() + self.eip155_fields_len() }
.encode(out);
self.encode_fields(out);
self.encode_eip155_fields(out);
Transaction::Legacy(legacy_tx) => {
legacy_tx.encode_for_signing(out);
}
_ => {
out.put_u8(self.tx_type() as u8);
Header { list: true, payload_length: self.fields_len() }.encode(out);
self.encode_fields(out);
Transaction::Eip2930(access_list_tx) => {
access_list_tx.encode_for_signing(out);
}
Transaction::Eip1559(dynamic_fee_tx) => {
dynamic_fee_tx.encode_for_signing(out);
}
Transaction::Eip4844(blob_tx) => {
blob_tx.encode_for_signing(out);
}
}
}

fn length(&self) -> usize {
match self {
Transaction::Legacy { .. } => {
let payload_length = self.fields_len() + self.eip155_fields_len();
// 'header length' + 'payload length'
length_of_length(payload_length) + payload_length
}
_ => {
let payload_length = self.fields_len();
// 'transaction type byte length' + 'header length' + 'payload length'
1 + length_of_length(payload_length) + payload_length
}
Transaction::Legacy(legacy_tx) => legacy_tx.payload_len_for_signature(),
Transaction::Eip2930(access_list_tx) => access_list_tx.payload_len_for_signature(),
Transaction::Eip1559(dynamic_fee_tx) => dynamic_fee_tx.payload_len_for_signature(),
Transaction::Eip4844(blob_tx) => blob_tx.payload_len_for_signature(),
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion crates/primitives/src/transaction/pooled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ impl PooledTransactionsElement {
/// Heavy operation that return signature hash over rlp encoded transaction.
/// It is only for signature signing or signer recovery.
pub fn signature_hash(&self) -> H256 {
todo!()
match self {
Self::Legacy { transaction, .. } => transaction.signature_hash(),
Self::Eip2930 { transaction, .. } => transaction.signature_hash(),
Self::Eip1559 { transaction, .. } => transaction.signature_hash(),
Self::BlobTransaction(blob_tx) => blob_tx.transaction.signature_hash(),
}
}

/// Returns the signature of the transaction.
Expand Down

0 comments on commit 329c9be

Please sign in to comment.