Skip to content

Commit

Permalink
Remove Transaction new-type
Browse files Browse the repository at this point in the history
  • Loading branch information
paulhauner committed Nov 5, 2021
1 parent b9ee00e commit 63b01d5
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 106 deletions.
63 changes: 37 additions & 26 deletions beacon_node/execution_layer/src/engine_api/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,8 @@ pub struct JsonExecutionPayload<T: EthSpec> {
pub base_fee_per_gas: Uint256,
pub block_hash: Hash256,
#[serde(with = "serde_transactions")]
pub transactions: VariableList<Transaction<T>, T::MaxTransactionsPerPayload>,
pub transactions:
VariableList<Transaction<T::MaxBytesPerTransaction>, T::MaxTransactionsPerPayload>,
}

impl<T: EthSpec> From<ExecutionPayload<T>> for JsonExecutionPayload<T> {
Expand Down Expand Up @@ -410,16 +411,16 @@ pub mod serde_transactions {
use serde::{de, Deserializer, Serializer};
use std::marker::PhantomData;

type Value<T, N> = VariableList<Transaction<T>, N>;
type Value<M, N> = VariableList<Transaction<M>, N>;

#[derive(Default)]
pub struct ListOfBytesListVisitor<T, N> {
_phantom_t: PhantomData<T>,
pub struct ListOfBytesListVisitor<M, N> {
_phantom_m: PhantomData<M>,
_phantom_n: PhantomData<N>,
}

impl<'a, T: EthSpec, N: Unsigned> serde::de::Visitor<'a> for ListOfBytesListVisitor<T, N> {
type Value = Value<T, N>;
impl<'a, M: Unsigned, N: Unsigned> serde::de::Visitor<'a> for ListOfBytesListVisitor<M, N> {
type Value = Value<M, N>;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a list of 0x-prefixed byte lists")
Expand All @@ -436,7 +437,7 @@ pub mod serde_transactions {
let transaction = VariableList::new(inner_vec).map_err(|e| {
serde::de::Error::custom(format!("transaction too large: {:?}", e))
})?;
outer.push(Transaction::new(transaction)).map_err(|e| {
outer.push(transaction).map_err(|e| {
serde::de::Error::custom(format!("too many transactions: {:?}", e))
})?;
}
Expand All @@ -445,8 +446,8 @@ pub mod serde_transactions {
}
}

pub fn serialize<S, T: EthSpec, N: Unsigned>(
value: &Value<T, N>,
pub fn serialize<S, M: Unsigned, N: Unsigned>(
value: &Value<M, N>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
Expand All @@ -457,19 +458,19 @@ pub mod serde_transactions {
// It's important to match on the inner values of the transaction. Serializing the
// entire `Transaction` will result in appending the SSZ union prefix byte. The
// execution node does not want that.
let hex = hex::encode(transaction.as_bytes());
let hex = hex::encode(&transaction[..]);
seq.serialize_element(&hex)?;
}
seq.end()
}

pub fn deserialize<'de, D, T: EthSpec, N: Unsigned>(
pub fn deserialize<'de, D, M: Unsigned, N: Unsigned>(
deserializer: D,
) -> Result<Value<T, N>, D::Error>
) -> Result<Value<M, N>, D::Error>
where
D: Deserializer<'de>,
{
let visitor: ListOfBytesListVisitor<T, N> = <_>::default();
let visitor: ListOfBytesListVisitor<M, N> = <_>::default();
deserializer.deserialize_any(visitor)
}
}
Expand Down Expand Up @@ -555,7 +556,10 @@ mod test {
const LOGS_BLOOM_01: &str = "0x01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101";

fn encode_transactions<E: EthSpec>(
transactions: VariableList<Transaction<E>, E::MaxTransactionsPerPayload>,
transactions: VariableList<
Transaction<E::MaxBytesPerTransaction>,
E::MaxTransactionsPerPayload,
>,
) -> Result<serde_json::Value, serde_json::Error> {
let ep: JsonExecutionPayload<E> = JsonExecutionPayload {
transactions,
Expand All @@ -567,7 +571,10 @@ mod test {

fn decode_transactions<E: EthSpec>(
transactions: serde_json::Value,
) -> Result<VariableList<Transaction<E>, E::MaxTransactionsPerPayload>, serde_json::Error> {
) -> Result<
VariableList<Transaction<E::MaxBytesPerTransaction>, E::MaxTransactionsPerPayload>,
serde_json::Error,
> {
let json = json!({
"parentHash": HASH_00,
"coinbase": ADDRESS_01,
Expand All @@ -590,17 +597,17 @@ mod test {

fn assert_transactions_serde<E: EthSpec>(
name: &str,
as_obj: VariableList<Transaction<E>, E::MaxTransactionsPerPayload>,
as_obj: VariableList<Transaction<E::MaxBytesPerTransaction>, E::MaxTransactionsPerPayload>,
as_json: serde_json::Value,
) {
assert_eq!(
encode_transactions(as_obj.clone()).unwrap(),
encode_transactions::<E>(as_obj.clone()).unwrap(),
as_json,
"encoding for {}",
name
);
assert_eq!(
decode_transactions(as_json).unwrap(),
decode_transactions::<E>(as_json).unwrap(),
as_obj,
"decoding for {}",
name
Expand All @@ -610,46 +617,50 @@ mod test {
/// Example: if `spec == &[1, 1]`, then two one-byte transactions will be created.
fn generate_transactions<E: EthSpec>(
spec: &[usize],
) -> VariableList<Transaction<E>, E::MaxTransactionsPerPayload> {
) -> VariableList<Transaction<E::MaxBytesPerTransaction>, E::MaxTransactionsPerPayload> {
let mut txs = VariableList::default();

for &num_bytes in spec {
let mut tx = VariableList::default();
for _ in 0..num_bytes {
tx.push(0).unwrap();
}
txs.push(Transaction::new(tx)).unwrap();
txs.push(tx).unwrap();
}

txs
}

#[test]
fn transaction_serde() {
assert_transactions_serde::<MainnetEthSpec>("empty", generate_transactions(&[]), json!([]));
assert_transactions_serde::<MainnetEthSpec>(
"empty",
generate_transactions::<MainnetEthSpec>(&[]),
json!([]),
);
assert_transactions_serde::<MainnetEthSpec>(
"one empty tx",
generate_transactions(&[0]),
generate_transactions::<MainnetEthSpec>(&[0]),
json!(["0x"]),
);
assert_transactions_serde::<MainnetEthSpec>(
"two empty txs",
generate_transactions(&[0, 0]),
generate_transactions::<MainnetEthSpec>(&[0, 0]),
json!(["0x", "0x"]),
);
assert_transactions_serde::<MainnetEthSpec>(
"one one-byte tx",
generate_transactions(&[1]),
generate_transactions::<MainnetEthSpec>(&[1]),
json!(["0x00"]),
);
assert_transactions_serde::<MainnetEthSpec>(
"two one-byte txs",
generate_transactions(&[1, 1]),
generate_transactions::<MainnetEthSpec>(&[1, 1]),
json!(["0x00", "0x00"]),
);
assert_transactions_serde::<MainnetEthSpec>(
"mixed bag",
generate_transactions(&[0, 1, 3, 0]),
generate_transactions::<MainnetEthSpec>(&[0, 1, 3, 0]),
json!(["0x", "0x00", "0x000000", "0x"]),
);

Expand Down
77 changes: 77 additions & 0 deletions consensus/ssz_types/src/serde_utils/list_of_hex_var_list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//! Serialize `VaraibleList<VariableList<u8, M>, N>` as list of 0x-prefixed hex string.
use crate::VariableList;
use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer};
use std::marker::PhantomData;
use typenum::Unsigned;

#[derive(Deserialize)]
#[serde(transparent)]
pub struct WrappedListOwned<N: Unsigned>(
#[serde(with = "crate::serde_utils::hex_var_list")] VariableList<u8, N>,
);

#[derive(Serialize)]
#[serde(transparent)]
pub struct WrappedListRef<'a, N: Unsigned>(
#[serde(with = "crate::serde_utils::hex_var_list")] &'a VariableList<u8, N>,
);

pub fn serialize<S, M, N>(
list: &VariableList<VariableList<u8, M>, N>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
M: Unsigned,
N: Unsigned,
{
let mut seq = serializer.serialize_seq(Some(list.len()))?;
for bytes in list {
seq.serialize_element(&WrappedListRef(bytes))?;
}
seq.end()
}

#[derive(Default)]
pub struct Visitor<M, N> {
_phantom_m: PhantomData<M>,
_phantom_n: PhantomData<N>,
}

impl<'a, M, N> serde::de::Visitor<'a> for Visitor<M, N>
where
M: Unsigned,
N: Unsigned,
{
type Value = VariableList<VariableList<u8, M>, N>;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a list of 0x-prefixed hex bytes")
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'a>,
{
let mut list: VariableList<VariableList<u8, M>, N> = <_>::default();

while let Some(val) = seq.next_element::<WrappedListOwned<M>>()? {
list.push(val.0).map_err(|e| {
serde::de::Error::custom(format!("failed to push value to list: {:?}.", e))
})?;
}

Ok(list)
}
}

pub fn deserialize<'de, D, M, N>(
deserializer: D,
) -> Result<VariableList<VariableList<u8, M>, N>, D::Error>
where
D: Deserializer<'de>,
M: Unsigned,
N: Unsigned,
{
deserializer.deserialize_seq(Visitor::default())
}
1 change: 1 addition & 0 deletions consensus/ssz_types/src/serde_utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod hex_fixed_vec;
pub mod hex_var_list;
pub mod list_of_hex_var_list;
pub mod quoted_u64_fixed_vec;
pub mod quoted_u64_var_list;
84 changes: 4 additions & 80 deletions consensus/types/src/execution_payload.rs
Original file line number Diff line number Diff line change
@@ -1,87 +1,10 @@
use crate::{test_utils::TestRandom, *};
use rand::RngCore;
use serde_derive::{Deserialize, Serialize};
use ssz::{Decode, DecodeError, Encode};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;

#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
#[serde(bound = "T: EthSpec")]
pub struct Transaction<T: EthSpec>(
#[serde(with = "ssz_types::serde_utils::hex_var_list")]
VariableList<u8, T::MaxBytesPerTransaction>,
);

impl<T: EthSpec> Transaction<T> {
pub fn new(tx: VariableList<u8, T::MaxBytesPerTransaction>) -> Transaction<T> {
Self(tx)
}

pub fn as_bytes(&self) -> &[u8] {
&self.0[..]
}
}

impl<T: EthSpec> Encode for Transaction<T> {
fn is_ssz_fixed_len() -> bool {
<VariableList<u8, T::MaxBytesPerTransaction> as Encode>::is_ssz_fixed_len()
}

fn ssz_append(&self, buf: &mut Vec<u8>) {
self.0.ssz_append(buf)
}

fn ssz_fixed_len() -> usize {
<VariableList<u8, T::MaxBytesPerTransaction> as Encode>::ssz_fixed_len()
}

fn ssz_bytes_len(&self) -> usize {
self.0.ssz_bytes_len()
}
}

impl<T: EthSpec> Decode for Transaction<T> {
fn is_ssz_fixed_len() -> bool {
<VariableList<u8, T::MaxBytesPerTransaction> as Decode>::is_ssz_fixed_len()
}

fn ssz_fixed_len() -> usize {
<VariableList<u8, T::MaxBytesPerTransaction> as Decode>::ssz_fixed_len()
}

fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
Ok(Transaction(VariableList::from_ssz_bytes(bytes)?))
}
}

impl<T: EthSpec> tree_hash::TreeHash for Transaction<T> {
fn tree_hash_type() -> tree_hash::TreeHashType {
tree_hash::TreeHashType::Container
}

fn tree_hash_packed_encoding(&self) -> Vec<u8> {
unreachable!("List should never be packed.")
}

fn tree_hash_packing_factor() -> usize {
unreachable!("List should never be packed.")
}

fn tree_hash_root(&self) -> tree_hash::Hash256 {
self.0.tree_hash_root()
}
}

impl<T: EthSpec> SignedRoot for Transaction<T> {}

impl<T: EthSpec> TestRandom for Transaction<T> {
fn random_for_test(rng: &mut impl RngCore) -> Self {
Transaction(VariableList::random_for_test(rng))
}
}
pub type Transaction<T> = VariableList<u8, T>;

#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Expand All @@ -108,8 +31,9 @@ pub struct ExecutionPayload<T: EthSpec> {
pub extra_data: VariableList<u8, T::MaxExtraDataBytes>,
pub base_fee_per_gas: Hash256,
pub block_hash: Hash256,
#[test_random(default)]
pub transactions: VariableList<Transaction<T>, T::MaxTransactionsPerPayload>,
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
pub transactions:
VariableList<Transaction<T::MaxBytesPerTransaction>, T::MaxTransactionsPerPayload>,
}

impl<T: EthSpec> ExecutionPayload<T> {
Expand Down

0 comments on commit 63b01d5

Please sign in to comment.