Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use EncodableSignature for tx encoding #1100

Merged
merged 2 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions crates/consensus/src/encodable_signature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use alloy_primitives::{Parity, SignatureError, U256};

/// Helper trait used to streamline signatures encoding.
pub trait EncodableSignature: Sized {
/// Instantiate from v, r, s.
fn from_rs_and_parity<P: TryInto<Parity, Error = E>, E: Into<SignatureError>>(
r: U256,
s: U256,
parity: P,
) -> Result<Self, SignatureError>;

/// Returns the `r` component of this signature.
fn r(&self) -> U256;

/// Returns the `s` component of this signature.
fn s(&self) -> U256;

/// Returns the recovery ID as a `u8`.
fn v(&self) -> Parity;

/// Sets the recovery ID by normalizing a `v` value.
fn with_parity<T: Into<Parity>>(self, parity: T) -> Self;

/// Modifies the recovery ID by applying [EIP-155] to a `v` value.
///
/// [EIP-155]: https://eips.ethereum.org/EIPS/eip-155
#[inline]
fn with_chain_id(self, chain_id: u64) -> Self
where
Self: Copy,
{
self.with_parity(self.v().with_chain_id(chain_id))
}

/// Modifies the recovery ID by dropping any [EIP-155] v value, converting
/// to a simple parity bool.
fn with_parity_bool(self) -> Self
where
Self: Copy,
{
self.with_parity(self.v().to_parity_bool())
}

/// Decode an RLP-encoded VRS signature.
fn decode_rlp_vrs(buf: &mut &[u8]) -> Result<Self, alloy_rlp::Error> {
use alloy_rlp::Decodable;

let parity: Parity = Decodable::decode(buf)?;
let r = Decodable::decode(buf)?;
let s = Decodable::decode(buf)?;

Self::from_rs_and_parity(r, s, parity)
.map_err(|_| alloy_rlp::Error::Custom("attempted to decode invalid field element"))
}

/// Length of RLP RS field encoding
fn rlp_rs_len(&self) -> usize {
alloy_rlp::Encodable::length(&self.r()) + alloy_rlp::Encodable::length(&self.s())
}

/// Length of RLP V field encoding
fn rlp_vrs_len(&self) -> usize {
self.rlp_rs_len() + alloy_rlp::Encodable::length(&self.v())
}

/// Write R and S to an RLP buffer in progress.
fn write_rlp_rs(&self, out: &mut dyn alloy_rlp::BufMut) {
alloy_rlp::Encodable::encode(&self.r(), out);
alloy_rlp::Encodable::encode(&self.s(), out);
}

/// Write the V to an RLP buffer without using EIP-155.
fn write_rlp_v(&self, out: &mut dyn alloy_rlp::BufMut) {
alloy_rlp::Encodable::encode(&self.v(), out);
}

/// Write the VRS to the output. The V will always be 27 or 28.
fn write_rlp_vrs(&self, out: &mut dyn alloy_rlp::BufMut) {
self.write_rlp_v(out);
self.write_rlp_rs(out);
}
}

impl EncodableSignature for alloy_primitives::Signature {
fn from_rs_and_parity<P: TryInto<Parity, Error = E>, E: Into<SignatureError>>(
r: U256,
s: U256,
parity: P,
) -> Result<Self, SignatureError> {
Self::from_rs_and_parity(r, s, parity)
}

fn r(&self) -> U256 {
self.r()
}

fn s(&self) -> U256 {
self.s()
}

fn v(&self) -> Parity {
self.v()
}

fn with_parity<T: Into<Parity>>(self, parity: T) -> Self {
self.with_parity(parity)
}
}
3 changes: 3 additions & 0 deletions crates/consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ pub use account::Account;

pub mod constants;

mod encodable_signature;
pub use encodable_signature::EncodableSignature;

mod header;
pub use header::{Header, EMPTY_OMMER_ROOT_HASH, EMPTY_ROOT_HASH};

Expand Down
12 changes: 9 additions & 3 deletions crates/consensus/src/transaction/eip1559.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{SignableTransaction, Signed, Transaction, TxType};
use crate::{EncodableSignature, SignableTransaction, Signed, Transaction, TxType};
use alloy_eips::eip2930::AccessList;
use alloy_primitives::{keccak256, Bytes, ChainId, Signature, TxKind, U256};
use alloy_rlp::{BufMut, Decodable, Encodable, Header};
Expand Down Expand Up @@ -153,7 +153,10 @@ impl TxEip1559 {
///
/// If `with_header` is `true`, the payload length will include the RLP header length.
/// If `with_header` is `false`, the payload length will not include the RLP header length.
pub fn encoded_len_with_signature(&self, signature: &Signature, with_header: bool) -> usize {
pub fn encoded_len_with_signature<S>(&self, signature: &S, with_header: bool) -> usize
where
S: EncodableSignature,
{
// this counts the tx fields and signature fields
let payload_length = self.fields_len() + signature.rlp_vrs_len();

Expand Down Expand Up @@ -228,7 +231,10 @@ impl TxEip1559 {
/// tx type byte or string header.
///
/// This __does__ encode a list header and include a signature.
pub(crate) fn encode_with_signature_fields(&self, signature: &Signature, out: &mut dyn BufMut) {
pub fn encode_with_signature_fields<S>(&self, signature: &S, out: &mut dyn BufMut)
where
S: EncodableSignature,
{
let payload_length = self.fields_len() + signature.rlp_vrs_len();
let header = Header { list: true, payload_length };
header.encode(out);
Expand Down
12 changes: 9 additions & 3 deletions crates/consensus/src/transaction/eip2930.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{SignableTransaction, Signed, Transaction, TxType};
use crate::{EncodableSignature, SignableTransaction, Signed, Transaction, TxType};
use alloy_eips::eip2930::AccessList;
use alloy_primitives::{keccak256, Bytes, ChainId, Signature, TxKind, U256};
use alloy_rlp::{length_of_length, BufMut, Decodable, Encodable, Header};
Expand Down Expand Up @@ -131,7 +131,10 @@ impl TxEip2930 {
///
/// If `with_header` is `true`, the payload length will include the RLP header length.
/// If `with_header` is `false`, the payload length will not include the RLP header length.
pub fn encoded_len_with_signature(&self, signature: &Signature, with_header: bool) -> usize {
pub fn encoded_len_with_signature<S>(&self, signature: &S, with_header: bool) -> usize
where
S: EncodableSignature,
{
// this counts the tx fields and signature fields
let payload_length = self.fields_len() + signature.rlp_vrs_len();

Expand Down Expand Up @@ -176,7 +179,10 @@ impl TxEip2930 {
/// tx type byte or string header.
///
/// This __does__ encode a list header and include a signature.
pub(crate) fn encode_with_signature_fields(&self, signature: &Signature, out: &mut dyn BufMut) {
pub fn encode_with_signature_fields<S>(&self, signature: &S, out: &mut dyn BufMut)
where
S: EncodableSignature,
{
let payload_length = self.fields_len() + signature.rlp_vrs_len();
let header = Header { list: true, payload_length };
header.encode(out);
Expand Down
12 changes: 9 additions & 3 deletions crates/consensus/src/transaction/eip4844.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{SignableTransaction, Signed, Transaction, TxType};
use crate::{EncodableSignature, SignableTransaction, Signed, Transaction, TxType};

use alloy_eips::{eip2930::AccessList, eip4844::DATA_GAS_PER_BLOB};
use alloy_primitives::{keccak256, Address, Bytes, ChainId, Signature, TxKind, B256, U256};
Expand Down Expand Up @@ -493,7 +493,10 @@ impl TxEip4844 {
///
/// If `with_header` is `true`, the payload length will include the RLP header length.
/// If `with_header` is `false`, the payload length will not include the RLP header length.
pub fn encoded_len_with_signature(&self, signature: &Signature, with_header: bool) -> usize {
pub fn encoded_len_with_signature<S>(&self, signature: &S, with_header: bool) -> usize
where
S: EncodableSignature,
{
// this counts the tx fields and signature fields
let payload_length = self.fields_len() + signature.rlp_vrs_len();

Expand Down Expand Up @@ -538,7 +541,10 @@ impl TxEip4844 {
/// tx type byte or string header.
///
/// This __does__ encode a list header and include a signature.
pub(crate) fn encode_with_signature_fields(&self, signature: &Signature, out: &mut dyn BufMut) {
pub fn encode_with_signature_fields<S>(&self, signature: &S, out: &mut dyn BufMut)
where
S: EncodableSignature,
{
let payload_length = self.fields_len() + signature.rlp_vrs_len();
let header = Header { list: true, payload_length };
header.encode(out);
Expand Down
12 changes: 9 additions & 3 deletions crates/consensus/src/transaction/eip7702.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{SignableTransaction, Signed, Transaction, TxType};
use crate::{EncodableSignature, SignableTransaction, Signed, Transaction, TxType};
use alloy_eips::eip2930::AccessList;
use alloy_primitives::{keccak256, Bytes, ChainId, Signature, TxKind, U256};
use alloy_rlp::{BufMut, Decodable, Encodable, Header};
Expand Down Expand Up @@ -161,7 +161,10 @@ impl TxEip7702 {
///
/// If `with_header` is `true`, the payload length will include the RLP header length.
/// If `with_header` is `false`, the payload length will not include the RLP header length.
pub fn encoded_len_with_signature(&self, signature: &Signature, with_header: bool) -> usize {
pub fn encoded_len_with_signature<S>(&self, signature: &S, with_header: bool) -> usize
where
S: EncodableSignature,
{
// this counts the tx fields and signature fields
let payload_length = self.fields_len() + signature.rlp_vrs_len();

Expand Down Expand Up @@ -236,7 +239,10 @@ impl TxEip7702 {
/// tx type byte or string header.
///
/// This __does__ encode a list header and include a signature.
pub(crate) fn encode_with_signature_fields(&self, signature: &Signature, out: &mut dyn BufMut) {
pub fn encode_with_signature_fields<S>(&self, signature: &S, out: &mut dyn BufMut)
where
S: EncodableSignature,
{
let payload_length = self.fields_len() + signature.rlp_vrs_len();
let header = Header { list: true, payload_length };
header.encode(out);
Expand Down
16 changes: 9 additions & 7 deletions crates/consensus/src/transaction/legacy.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{SignableTransaction, Signed, Transaction};
use crate::{EncodableSignature, SignableTransaction, Signed, Transaction};
use alloy_primitives::{keccak256, Bytes, ChainId, Signature, TxKind, U256};
use alloy_rlp::{length_of_length, BufMut, Decodable, Encodable, Header, Result};
use core::mem;
Expand Down Expand Up @@ -104,11 +104,10 @@ impl TxLegacy {
/// tx type byte or string header.
///
/// This __does__ encode a list header and include a signature.
pub fn encode_with_signature_fields(
&self,
signature: &Signature,
out: &mut dyn alloy_rlp::BufMut,
) {
pub fn encode_with_signature_fields<S>(&self, signature: &S, out: &mut dyn alloy_rlp::BufMut)
where
S: EncodableSignature,
{
let payload_length = self.fields_len() + signature.rlp_vrs_len();
let header = Header { list: true, payload_length };
header.encode(out);
Expand All @@ -118,7 +117,10 @@ impl TxLegacy {

/// Returns what the encoded length should be, if the transaction were RLP encoded with the
/// given signature.
pub fn encoded_len_with_signature(&self, signature: &Signature) -> usize {
pub fn encoded_len_with_signature<S>(&self, signature: &S) -> usize
where
S: EncodableSignature,
{
let payload_length = self.fields_len() + signature.rlp_vrs_len();
Header { list: true, payload_length }.length() + payload_length
}
Expand Down
2 changes: 2 additions & 0 deletions crates/signer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ macro_rules! sign_transaction_with_chain_id {
// sign: lazy Signature,
// )
($signer:expr, $tx:expr, $sign:expr) => {{
use alloy_consensus::EncodableSignature;

if let Some(chain_id) = $signer.chain_id() {
if !$tx.set_chain_id_checked(chain_id) {
return Err(alloy_signer::Error::TransactionChainIdMismatch {
Expand Down