Skip to content

Commit

Permalink
Merge pull request #285 from ralexstokes/tidy-exports
Browse files Browse the repository at this point in the history
updates to support _using_ polymorphic types
  • Loading branch information
ralexstokes authored Oct 16, 2023
2 parents 79f65c2 + a53538c commit e810cec
Show file tree
Hide file tree
Showing 20 changed files with 2,649 additions and 1,205 deletions.
2 changes: 1 addition & 1 deletion beacon-api-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ license = "MIT OR Apache-2.0"
default = ["cli", "native-tls"]
cli = ["clap"]
rustls = ["reqwest/rustls-tls"]
native-tls = ["reqwest/default-tls"]
native-tls = ["reqwest/default-tls"]

[dependencies]
tokio = { version = "1.0", features = ["full"] }
Expand Down
121 changes: 6 additions & 115 deletions beacon-api-client/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use ethereum_consensus::{
Fork,
};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fmt, marker::PhantomData, str::FromStr};
use std::{collections::HashMap, fmt, str::FromStr};

#[derive(Serialize, Deserialize)]
pub struct VersionData {
Expand Down Expand Up @@ -465,125 +465,16 @@ pub struct Value<T> {
pub meta: HashMap<String, serde_json::Value>,
}

#[derive(Debug, Serialize)]
pub struct VersionedValue<T: serde::Serialize + serde::de::DeserializeOwned> {
#[derive(Debug, Serialize, Deserialize)]
#[serde(bound = "T: serde::Serialize + serde::de::DeserializeOwned")]
pub struct VersionedValue<T> {
pub version: Fork,
pub data: T,
#[serde(flatten)]
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub meta: HashMap<String, serde_json::Value>,
}

impl<'de, T: serde::Serialize + serde::de::DeserializeOwned> serde::Deserialize<'de>
for VersionedValue<T>
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Debug)]
enum Field<'de> {
Version,
Data,
Meta(&'de str),
}

impl<'de> serde::Deserialize<'de> for Field<'de> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct FieldVisitor;

impl<'de> serde::de::Visitor<'de> for FieldVisitor {
type Value = Field<'de>;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("some field name")
}

fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match v {
"version" => Ok(Field::Version),
"data" => Ok(Field::Data),
s => Ok(Field::Meta(s)),
}
}
}
deserializer.deserialize_identifier(FieldVisitor)
}
}

struct Visitor<T>(PhantomData<T>);

impl<'de, T: serde::Serialize + serde::de::DeserializeOwned> serde::de::Visitor<'de>
for Visitor<T>
{
type Value = VersionedValue<T>;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct VersionedValue")
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut version = None;
let mut version_str = None;
let mut data: Option<serde_json::Value> = None;
let mut meta = HashMap::default();
while let Some(key) = map.next_key()? {
match key {
Field::Version => {
if version.is_some() {
return Err(serde::de::Error::duplicate_field("version"))
}
let version_value: serde_json::Value = map.next_value()?;
let fork: Fork = serde_json::from_value(version_value.clone())
.map_err(serde::de::Error::custom)?;
version = Some(fork);
match version_value {
serde_json::Value::String(inner) => {
version_str = Some(inner);
}
other => {
return Err(serde::de::Error::custom(format!(
"expected JSON string, but found value {other}"
)))
}
};
}
Field::Data => {
if data.is_some() {
return Err(serde::de::Error::duplicate_field("data"))
}
data = Some(map.next_value()?);
}
Field::Meta(name) => {
let next_value: serde_json::Value = map.next_value()?;
meta.insert(name.to_string(), next_value);
}
}
}
let version = version.ok_or_else(|| serde::de::Error::missing_field("version"))?;
let data = data.ok_or_else(|| serde::de::Error::missing_field("data"))?;
let data_with_version = serde_json::json!({
"version": version_str,
"data": data,
});
let data: T =
serde_json::from_value(data_with_version).map_err(serde::de::Error::custom)?;
Ok(VersionedValue { version, data, meta })
}
}

const FIELDS: &[&str] = &["version", "data", "meta"];
deserializer.deserialize_struct("VersionedValue", FIELDS, Visitor(PhantomData))
}
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(bound = "T: Serialize + serde::de::DeserializeOwned")]
#[serde(untagged)]
Expand Down
2 changes: 1 addition & 1 deletion ethereum-consensus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ ec = [
]

[dependencies]
ssz_rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "51f3932d1578a62f856c19175482056912de5f3e" }
ssz_rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "c00a4659b9d1980d410c487a88e983cf2506c928" }
blst = "0.3.11"
rand = "0.8.4"
thiserror = "1.0.30"
Expand Down
7 changes: 7 additions & 0 deletions ethereum-consensus/examples/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use ethereum_consensus::{
Attestation, AttesterSlashing, BeaconState, Deposit, PendingAttestation, ProposerSlashing,
SignedBeaconBlock, SignedVoluntaryExit,
},
types::mainnet as types,
};

fn main() {
Expand Down Expand Up @@ -37,4 +38,10 @@ fn main() {
let header_json = serde_json::to_string(&header).unwrap();
println!("{}", &header_json);
let _: ExecutionPayloadHeader = serde_json::from_str(&header_json).unwrap();

let header = types::ExecutionPayloadHeader::Bellatrix(header);
let header_json = serde_json::to_string_pretty(&header).unwrap();
println!("{header_json}");
let recovered_header = serde_json::from_str(&header_json).unwrap();
assert_eq!(header, recovered_header);
}
2 changes: 1 addition & 1 deletion ethereum-consensus/src/altair/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub use crate::{
state_transition::{Context, Result, Validation},
};
use crate::{
crypto::{fast_aggregate_verify, hash, verify_signature},
crypto::{fast_aggregate_verify, hash},
ssz::prelude::*,
};
use std::{cmp, collections::HashSet};
Expand Down
5 changes: 1 addition & 4 deletions ethereum-consensus/src/bellatrix/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,7 @@ pub use crate::{
state_transition::{Context, Result, Validation},
};
use crate::{
crypto::{
eth_aggregate_public_keys, eth_fast_aggregate_verify, fast_aggregate_verify, hash,
verify_signature,
},
crypto::{eth_aggregate_public_keys, eth_fast_aggregate_verify, fast_aggregate_verify, hash},
ssz::prelude::*,
};
use integer_sqrt::IntegerSquareRoot;
Expand Down
5 changes: 1 addition & 4 deletions ethereum-consensus/src/capella/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,7 @@ pub use crate::{
state_transition::{Context, Result, Validation},
};
use crate::{
crypto::{
eth_aggregate_public_keys, eth_fast_aggregate_verify, fast_aggregate_verify, hash,
verify_signature,
},
crypto::{eth_aggregate_public_keys, eth_fast_aggregate_verify, fast_aggregate_verify, hash},
ssz::prelude::*,
};
use integer_sqrt::IntegerSquareRoot;
Expand Down
5 changes: 1 addition & 4 deletions ethereum-consensus/src/deneb/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,7 @@ pub use crate::{
state_transition::{Context, Result, Validation},
};
use crate::{
crypto::{
eth_aggregate_public_keys, eth_fast_aggregate_verify, fast_aggregate_verify, hash,
verify_signature,
},
crypto::{eth_aggregate_public_keys, eth_fast_aggregate_verify, fast_aggregate_verify, hash},
ssz::prelude::*,
};
use integer_sqrt::IntegerSquareRoot;
Expand Down
23 changes: 18 additions & 5 deletions ethereum-consensus/src/signing.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
crypto::{verify_signature, SecretKey},
crypto::{self, SecretKey},
primitives::{BlsPublicKey, BlsSignature, Domain, Root},
ssz::prelude::*,
Error,
Expand All @@ -11,7 +11,7 @@ pub struct SigningData {
pub domain: Domain,
}

pub fn compute_signing_root<T: SimpleSerialize>(
pub fn compute_signing_root<T: Merkleized>(
ssz_object: &mut T,
domain: Domain,
) -> Result<Root, Error> {
Expand All @@ -21,7 +21,7 @@ pub fn compute_signing_root<T: SimpleSerialize>(
s.hash_tree_root().map_err(Error::Merkleization)
}

pub fn sign_with_domain<T: SimpleSerialize>(
pub fn sign_with_domain<T: Merkleized>(
data: &mut T,
signing_key: &SecretKey,
domain: Domain,
Expand All @@ -30,12 +30,25 @@ pub fn sign_with_domain<T: SimpleSerialize>(
Ok(signing_key.sign(signing_root.as_ref()))
}

pub fn verify_signed_data<T: SimpleSerialize>(
pub fn verify_signed_data<T: Merkleized>(
data: &mut T,
signature: &BlsSignature,
public_key: &BlsPublicKey,
domain: Domain,
) -> Result<(), Error> {
let signing_root = compute_signing_root(data, domain)?;
verify_signature(public_key, signing_root.as_ref(), signature).map_err(Into::into)
crypto::verify_signature(public_key, signing_root.as_ref(), signature).map_err(Into::into)
}

// This function wraps the inner implementation defined in `crate::crypto` but presents a bit nicer
// interface to users external to this crate.
// NOTE: `verify_signed_data` serves a similar purpose but asking for a `&mut T` there
// means that any message containing its public key (a common pattern in ethereum types)
// needs to pass in a (ref to a) `clone` of the public key inside the message type.
pub fn verify_signature(
public_key: &BlsPublicKey,
signing_root: &[u8],
signature: &BlsSignature,
) -> Result<(), Error> {
crypto::verify_signature(public_key, signing_root, signature).map_err(Into::into)
}
Loading

0 comments on commit e810cec

Please sign in to comment.