fix: use correct type for NewPooledTransactionHashes68 (#1548)

This commit is contained in:
Dan Cline
2023-02-28 15:20:02 -05:00
committed by GitHub
parent 3790a14268
commit 71bc1451af
3 changed files with 220 additions and 2 deletions

1
Cargo.lock generated
View File

@ -4564,6 +4564,7 @@ dependencies = [
"bytes",
"ethers-core",
"futures",
"hex",
"hex-literal",
"metrics",
"pin-project",

View File

@ -45,6 +45,7 @@ ethers-core = { git = "https://github.com/gakonst/ethers-rs", default-features =
test-fuzz = "3.0.4"
tokio-util = { version = "0.7.4", features = ["io", "codec"] }
hex-literal = "0.3"
hex = "0.4"
rand = "0.8"
secp256k1 = { version = "0.24.2", features = ["global-context", "rand-std", "recovery"] }

View File

@ -1,8 +1,11 @@
//! Types for broadcasting new data.
use crate::{EthMessage, EthVersion};
use bytes::Bytes;
use reth_codecs::derive_arbitrary;
use reth_primitives::{Block, TransactionSigned, H256, U128};
use reth_rlp::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper};
use reth_rlp::{
Decodable, Encodable, RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper,
};
use std::sync::Arc;
#[cfg(feature = "serde")]
@ -193,10 +196,32 @@ impl From<Vec<H256>> for NewPooledTransactionHashes66 {
/// Same as [`NewPooledTransactionHashes66`] but extends that that beside the transaction hashes,
/// the node sends the transaction types and their sizes (as defined in EIP-2718) as well.
#[derive_arbitrary(rlp)]
#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Default)]
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct NewPooledTransactionHashes68 {
/// Transaction types for new transactions that have appeared on the network.
///
/// ## Note on RLP encoding and decoding
///
/// In the [eth/68 spec](https://eips.ethereum.org/EIPS/eip-5793#specification) this is defined
/// the following way:
/// * `[type_0: B_1, type_1: B_1, ...]`
///
/// This would make it seem like the [`Encodable`](reth_rlp::Encodable) and
/// [`Decodable`](reth_rlp::Decodable) implementations should directly use a `Vec<u8>` for
/// encoding and decoding, because it looks like this field should be encoded as a _list_ of
/// bytes.
///
/// However, [this is implemented in geth as a `[]byte`
/// type](https://github.com/ethereum/go-ethereum/blob/82d934b1dd80cdd8190803ea9f73ed2c345e2576/eth/protocols/eth/protocol.go#L308-L313),
/// which [ends up being encoded as a RLP
/// string](https://github.com/ethereum/go-ethereum/blob/82d934b1dd80cdd8190803ea9f73ed2c345e2576/rlp/encode_test.go#L171-L176),
/// **not** a RLP list.
///
/// Because of this, we do not directly use the `Vec<u8>` when encoding and decoding, and
/// instead use the [`Encodable`](reth_rlp::Encodable) and [`Decodable`](reth_rlp::Decodable)
/// implementations for `&[u8]` instead, which encodes into a RLP string, and expects an RLP
/// string when decoding.
pub types: Vec<u8>,
/// Transaction sizes for new transactions that have appeared on the network.
pub sizes: Vec<usize>,
@ -204,10 +229,80 @@ pub struct NewPooledTransactionHashes68 {
pub hashes: Vec<H256>,
}
impl Encodable for NewPooledTransactionHashes68 {
fn length(&self) -> usize {
#[derive(RlpEncodable)]
struct EncodableNewPooledTransactionHashes68<'a> {
types: &'a [u8],
sizes: &'a Vec<usize>,
hashes: &'a Vec<H256>,
}
let encodable = EncodableNewPooledTransactionHashes68 {
types: &self.types[..],
sizes: &self.sizes,
hashes: &self.hashes,
};
encodable.length()
}
fn encode(&self, out: &mut dyn bytes::BufMut) {
#[derive(RlpEncodable)]
struct EncodableNewPooledTransactionHashes68<'a> {
types: &'a [u8],
sizes: &'a Vec<usize>,
hashes: &'a Vec<H256>,
}
let encodable = EncodableNewPooledTransactionHashes68 {
types: &self.types[..],
sizes: &self.sizes,
hashes: &self.hashes,
};
encodable.encode(out);
}
}
impl Decodable for NewPooledTransactionHashes68 {
fn decode(buf: &mut &[u8]) -> Result<Self, reth_rlp::DecodeError> {
#[derive(RlpDecodable)]
struct EncodableNewPooledTransactionHashes68 {
types: Bytes,
sizes: Vec<usize>,
hashes: Vec<H256>,
}
let encodable = EncodableNewPooledTransactionHashes68::decode(buf)?;
Ok(Self { types: encodable.types.into(), sizes: encodable.sizes, hashes: encodable.hashes })
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use bytes::BytesMut;
use hex_literal::hex;
use reth_rlp::{Decodable, Encodable};
use super::*;
/// Takes as input a struct / encoded hex message pair, ensuring that we encode to the exact hex
/// message, and decode to the exact struct.
fn test_encoding_vector<T: Encodable + Decodable + PartialEq + std::fmt::Debug>(
input: (T, &[u8]),
) {
let (expected_decoded, expected_encoded) = input;
let mut encoded = BytesMut::new();
expected_decoded.encode(&mut encoded);
assert_eq!(hex::encode(&encoded), hex::encode(expected_encoded));
let decoded = T::decode(&mut encoded.as_ref()).unwrap();
assert_eq!(expected_decoded, decoded);
}
#[test]
fn can_return_latest_block() {
let mut blocks = NewBlockHashes(vec![BlockHashNumber { hash: H256::random(), number: 0 }]);
@ -219,4 +314,125 @@ mod tests {
let latest = blocks.latest().unwrap();
assert_eq!(latest.number, 100);
}
#[test]
fn eth_68_tx_hash_roundtrip() {
let vectors = vec![
(
NewPooledTransactionHashes68 { types: vec![], sizes: vec![], hashes: vec![] },
&hex!("c380c0c0")[..],
),
(
NewPooledTransactionHashes68 {
types: vec![0x00],
sizes: vec![0x00],
hashes: vec![H256::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000000",
)
.unwrap()],
},
&hex!("e500c180e1a00000000000000000000000000000000000000000000000000000000000000000")[..],
),
(
NewPooledTransactionHashes68 {
types: vec![0x00, 0x00],
sizes: vec![0x00, 0x00],
hashes: vec![
H256::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000000",
)
.unwrap(),
H256::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000000",
)
.unwrap(),
],
},
&hex!("f84a820000c28080f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000")[..],
),
(
NewPooledTransactionHashes68 {
types: vec![0x02],
sizes: vec![0xb6],
hashes: vec![H256::from_str(
"0xfecbed04c7b88d8e7221a0a3f5dc33f220212347fc167459ea5cc9c3eb4c1124",
)
.unwrap()],
},
&hex!("e602c281b6e1a0fecbed04c7b88d8e7221a0a3f5dc33f220212347fc167459ea5cc9c3eb4c1124")[..],
),
(
NewPooledTransactionHashes68 {
types: vec![0xff, 0xff],
sizes: vec![0xffffffff, 0xffffffff],
hashes: vec![
H256::from_str(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
)
.unwrap(),
H256::from_str(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
)
.unwrap(),
],
},
&hex!("f85282ffffca84ffffffff84fffffffff842a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..],
),
(
NewPooledTransactionHashes68 {
types: vec![0xff, 0xff],
sizes: vec![0xffffffff, 0xffffffff],
hashes: vec![
H256::from_str(
"0xbeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafe",
)
.unwrap(),
H256::from_str(
"0xbeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafe",
)
.unwrap(),
],
},
&hex!("f85282ffffca84ffffffff84fffffffff842a0beefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafea0beefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafe")[..],
),
(
NewPooledTransactionHashes68 {
types: vec![0x10, 0x10],
sizes: vec![0xdeadc0de, 0xdeadc0de],
hashes: vec![
H256::from_str(
"0x3b9aca00f0671c9a2a1b817a0a78d3fe0c0f776cccb2a8c3c1b412a4f4e4d4e2",
)
.unwrap(),
H256::from_str(
"0x3b9aca00f0671c9a2a1b817a0a78d3fe0c0f776cccb2a8c3c1b412a4f4e4d4e2",
)
.unwrap(),
],
},
&hex!("f852821010ca84deadc0de84deadc0def842a03b9aca00f0671c9a2a1b817a0a78d3fe0c0f776cccb2a8c3c1b412a4f4e4d4e2a03b9aca00f0671c9a2a1b817a0a78d3fe0c0f776cccb2a8c3c1b412a4f4e4d4e2")[..],
),
(
NewPooledTransactionHashes68 {
types: vec![0x6f, 0x6f],
sizes: vec![0x7fffffff, 0x7fffffff],
hashes: vec![
H256::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000002",
)
.unwrap(),
H256::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000002",
)
.unwrap(),
],
},
&hex!("f852826f6fca847fffffff847ffffffff842a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000002")[..],
),
];
for vector in vectors {
test_encoding_vector(vector);
}
}
}