Fix Uint256 deserialization (#2786)
* Change base_fee_per_gas to Uint256 * Add custom (de)serialization to ExecutionPayload * Fix errors * Add a quoted_u256 module * Remove unused function * lint * Add test * Remove extra line Co-authored-by: Paul Hauner <paul@paulhauner.com>
This commit is contained in:
parent
de49c7ddaa
commit
24966c059d
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2709,7 +2709,6 @@ dependencies = [
|
|||||||
"eth2_ssz 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"eth2_ssz 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"eth2_wallet",
|
"eth2_wallet",
|
||||||
"genesis",
|
"genesis",
|
||||||
"int_to_bytes",
|
|
||||||
"lighthouse_network",
|
"lighthouse_network",
|
||||||
"lighthouse_version",
|
"lighthouse_version",
|
||||||
"log",
|
"log",
|
||||||
|
@ -326,7 +326,7 @@ impl<T: EthSpec> From<ExecutionPayload<T>> for JsonExecutionPayload<T> {
|
|||||||
gas_used: e.gas_used,
|
gas_used: e.gas_used,
|
||||||
timestamp: e.timestamp,
|
timestamp: e.timestamp,
|
||||||
extra_data: e.extra_data,
|
extra_data: e.extra_data,
|
||||||
base_fee_per_gas: Uint256::from_little_endian(e.base_fee_per_gas.as_bytes()),
|
base_fee_per_gas: e.base_fee_per_gas,
|
||||||
block_hash: e.block_hash,
|
block_hash: e.block_hash,
|
||||||
transactions: e.transactions,
|
transactions: e.transactions,
|
||||||
}
|
}
|
||||||
@ -347,19 +347,13 @@ impl<T: EthSpec> From<JsonExecutionPayload<T>> for ExecutionPayload<T> {
|
|||||||
gas_used: e.gas_used,
|
gas_used: e.gas_used,
|
||||||
timestamp: e.timestamp,
|
timestamp: e.timestamp,
|
||||||
extra_data: e.extra_data,
|
extra_data: e.extra_data,
|
||||||
base_fee_per_gas: uint256_to_hash256(e.base_fee_per_gas),
|
base_fee_per_gas: e.base_fee_per_gas,
|
||||||
block_hash: e.block_hash,
|
block_hash: e.block_hash,
|
||||||
transactions: e.transactions,
|
transactions: e.transactions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uint256_to_hash256(u: Uint256) -> Hash256 {
|
|
||||||
let mut bytes = [0; 32];
|
|
||||||
u.to_little_endian(&mut bytes);
|
|
||||||
Hash256::from_slice(&bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct JsonConsensusValidatedRequest {
|
pub struct JsonConsensusValidatedRequest {
|
||||||
@ -797,7 +791,7 @@ mod test {
|
|||||||
gas_used: 2,
|
gas_used: 2,
|
||||||
timestamp: 42,
|
timestamp: 42,
|
||||||
extra_data: vec![].into(),
|
extra_data: vec![].into(),
|
||||||
base_fee_per_gas: uint256_to_hash256(Uint256::from(1)),
|
base_fee_per_gas: Uint256::from(1),
|
||||||
block_hash: Hash256::repeat_byte(1),
|
block_hash: Hash256::repeat_byte(1),
|
||||||
transactions: vec![].into(),
|
transactions: vec![].into(),
|
||||||
})
|
})
|
||||||
@ -960,7 +954,7 @@ mod test {
|
|||||||
gas_used: 0,
|
gas_used: 0,
|
||||||
timestamp: 5,
|
timestamp: 5,
|
||||||
extra_data: vec![].into(),
|
extra_data: vec![].into(),
|
||||||
base_fee_per_gas: uint256_to_hash256(Uint256::from(0)),
|
base_fee_per_gas: Uint256::from(0),
|
||||||
block_hash: Hash256::from_str("0xb084c10440f05f5a23a55d1d7ebcb1b3892935fb56f23cdc9a7f42c348eed174").unwrap(),
|
block_hash: Hash256::from_str("0xb084c10440f05f5a23a55d1d7ebcb1b3892935fb56f23cdc9a7f42c348eed174").unwrap(),
|
||||||
transactions: vec![].into(),
|
transactions: vec![].into(),
|
||||||
};
|
};
|
||||||
@ -984,7 +978,7 @@ mod test {
|
|||||||
gas_used: 0,
|
gas_used: 0,
|
||||||
timestamp: 5,
|
timestamp: 5,
|
||||||
extra_data: vec![].into(),
|
extra_data: vec![].into(),
|
||||||
base_fee_per_gas: uint256_to_hash256(Uint256::from(0)),
|
base_fee_per_gas: Uint256::from(0),
|
||||||
block_hash: Hash256::from_str("0xb084c10440f05f5a23a55d1d7ebcb1b3892935fb56f23cdc9a7f42c348eed174").unwrap(),
|
block_hash: Hash256::from_str("0xb084c10440f05f5a23a55d1d7ebcb1b3892935fb56f23cdc9a7f42c348eed174").unwrap(),
|
||||||
transactions: vec![].into(),
|
transactions: vec![].into(),
|
||||||
})
|
})
|
||||||
|
@ -250,7 +250,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
|||||||
gas_used: GAS_USED,
|
gas_used: GAS_USED,
|
||||||
timestamp: payload.timestamp,
|
timestamp: payload.timestamp,
|
||||||
extra_data: "block gen was here".as_bytes().to_vec().into(),
|
extra_data: "block gen was here".as_bytes().to_vec().into(),
|
||||||
base_fee_per_gas: Hash256::from_low_u64_le(1),
|
base_fee_per_gas: Uint256::one(),
|
||||||
block_hash: Hash256::zero(),
|
block_hash: Hash256::zero(),
|
||||||
transactions: vec![].into(),
|
transactions: vec![].into(),
|
||||||
};
|
};
|
||||||
|
@ -10,6 +10,7 @@ license = "Apache-2.0"
|
|||||||
serde = { version = "1.0.116", features = ["derive"] }
|
serde = { version = "1.0.116", features = ["derive"] }
|
||||||
serde_derive = "1.0.116"
|
serde_derive = "1.0.116"
|
||||||
hex = "0.4.2"
|
hex = "0.4.2"
|
||||||
|
ethereum-types = "0.12.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_json = "1.0.58"
|
serde_json = "1.0.58"
|
||||||
|
@ -9,4 +9,4 @@ pub mod u32_hex;
|
|||||||
pub mod u64_hex_be;
|
pub mod u64_hex_be;
|
||||||
pub mod u8_hex;
|
pub mod u8_hex;
|
||||||
|
|
||||||
pub use quoted_int::{quoted_u32, quoted_u64, quoted_u8};
|
pub use quoted_int::{quoted_u256, quoted_u32, quoted_u64, quoted_u8};
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
//!
|
//!
|
||||||
//! Quotes can be optional during decoding.
|
//! Quotes can be optional during decoding.
|
||||||
|
|
||||||
|
use ethereum_types::U256;
|
||||||
use serde::{Deserializer, Serializer};
|
use serde::{Deserializer, Serializer};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
@ -56,6 +57,17 @@ macro_rules! define_mod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compositional wrapper type that allows quotes or no quotes.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct MaybeQuoted<T>
|
||||||
|
where
|
||||||
|
T: From<$int> + Into<$int> + Copy + TryFrom<u64>,
|
||||||
|
{
|
||||||
|
#[serde(with = "self")]
|
||||||
|
pub value: T,
|
||||||
|
}
|
||||||
|
|
||||||
/// Wrapper type for requiring quotes on a `$int`-like type.
|
/// Wrapper type for requiring quotes on a `$int`-like type.
|
||||||
///
|
///
|
||||||
/// Unlike using `serde(with = "quoted_$int::require_quotes")` this is composable, and can be nested
|
/// Unlike using `serde(with = "quoted_$int::require_quotes")` this is composable, and can be nested
|
||||||
@ -142,3 +154,66 @@ pub mod quoted_u64 {
|
|||||||
|
|
||||||
define_mod!(u64, visit_u64);
|
define_mod!(u64, visit_u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod quoted_u256 {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
struct U256Visitor;
|
||||||
|
|
||||||
|
impl<'de> serde::de::Visitor<'de> for U256Visitor {
|
||||||
|
type Value = U256;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
formatter.write_str("a quoted U256 integer")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
U256::from_dec_str(v).map_err(serde::de::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize with quotes.
|
||||||
|
pub fn serialize<S>(value: &U256, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(&format!("{}", value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialize with quotes.
|
||||||
|
pub fn deserialize<'de, D>(deserializer: D) -> Result<U256, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_str(U256Visitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
struct WrappedU256(#[serde(with = "quoted_u256")] U256);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn u256_with_quotes() {
|
||||||
|
assert_eq!(
|
||||||
|
&serde_json::to_string(&WrappedU256(U256::one())).unwrap(),
|
||||||
|
"\"1\""
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
serde_json::from_str::<WrappedU256>("\"1\"").unwrap(),
|
||||||
|
WrappedU256(U256::one())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn u256_without_quotes() {
|
||||||
|
serde_json::from_str::<WrappedU256>("1").unwrap_err();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -38,7 +38,7 @@ tempfile = "3.1.0"
|
|||||||
derivative = "2.1.1"
|
derivative = "2.1.1"
|
||||||
rusqlite = { version = "0.25.3", features = ["bundled"], optional = true }
|
rusqlite = { version = "0.25.3", features = ["bundled"], optional = true }
|
||||||
arbitrary = { version = "1.0", features = ["derive"], optional = true }
|
arbitrary = { version = "1.0", features = ["derive"], optional = true }
|
||||||
eth2_serde_utils = "0.1.0"
|
eth2_serde_utils = { path = "../serde_utils" }
|
||||||
regex = "1.3.9"
|
regex = "1.3.9"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
parking_lot = "0.11.1"
|
parking_lot = "0.11.1"
|
||||||
|
@ -29,7 +29,8 @@ pub struct ExecutionPayload<T: EthSpec> {
|
|||||||
pub timestamp: u64,
|
pub timestamp: u64,
|
||||||
#[serde(with = "ssz_types::serde_utils::hex_var_list")]
|
#[serde(with = "ssz_types::serde_utils::hex_var_list")]
|
||||||
pub extra_data: VariableList<u8, T::MaxExtraDataBytes>,
|
pub extra_data: VariableList<u8, T::MaxExtraDataBytes>,
|
||||||
pub base_fee_per_gas: Hash256,
|
#[serde(with = "eth2_serde_utils::quoted_u256")]
|
||||||
|
pub base_fee_per_gas: Uint256,
|
||||||
pub block_hash: Hash256,
|
pub block_hash: Hash256,
|
||||||
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
|
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
|
||||||
pub transactions:
|
pub transactions:
|
||||||
@ -51,7 +52,7 @@ impl<T: EthSpec> ExecutionPayload<T> {
|
|||||||
gas_used: 0,
|
gas_used: 0,
|
||||||
timestamp: 0,
|
timestamp: 0,
|
||||||
extra_data: VariableList::empty(),
|
extra_data: VariableList::empty(),
|
||||||
base_fee_per_gas: Hash256::zero(),
|
base_fee_per_gas: Uint256::zero(),
|
||||||
block_hash: Hash256::zero(),
|
block_hash: Hash256::zero(),
|
||||||
transactions: VariableList::empty(),
|
transactions: VariableList::empty(),
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,8 @@ pub struct ExecutionPayloadHeader<T: EthSpec> {
|
|||||||
pub timestamp: u64,
|
pub timestamp: u64,
|
||||||
#[serde(with = "ssz_types::serde_utils::hex_var_list")]
|
#[serde(with = "ssz_types::serde_utils::hex_var_list")]
|
||||||
pub extra_data: VariableList<u8, T::MaxExtraDataBytes>,
|
pub extra_data: VariableList<u8, T::MaxExtraDataBytes>,
|
||||||
pub base_fee_per_gas: Hash256,
|
#[serde(with = "eth2_serde_utils::quoted_u256")]
|
||||||
|
pub base_fee_per_gas: Uint256,
|
||||||
pub block_hash: Hash256,
|
pub block_hash: Hash256,
|
||||||
pub transactions_root: Hash256,
|
pub transactions_root: Hash256,
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
use bls::Hash256;
|
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use clap_utils::{parse_optional, parse_required};
|
use clap_utils::{parse_optional, parse_required};
|
||||||
use int_to_bytes::int_to_bytes32;
|
|
||||||
use ssz::Encode;
|
use ssz::Encode;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
@ -16,10 +14,7 @@ pub fn run<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
|
|||||||
.map_err(|e| format!("Unable to get time: {:?}", e))?
|
.map_err(|e| format!("Unable to get time: {:?}", e))?
|
||||||
.as_secs(),
|
.as_secs(),
|
||||||
);
|
);
|
||||||
let base_fee_per_gas = Hash256::from_slice(&int_to_bytes32(parse_required(
|
let base_fee_per_gas = parse_required(matches, "base-fee-per-gas")?;
|
||||||
matches,
|
|
||||||
"base-fee-per-gas",
|
|
||||||
)?));
|
|
||||||
let gas_limit = parse_required(matches, "gas-limit")?;
|
let gas_limit = parse_required(matches, "gas-limit")?;
|
||||||
let file_name = matches.value_of("file").ok_or("No file supplied")?;
|
let file_name = matches.value_of("file").ok_or("No file supplied")?;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user