Skip to content

Commit

Permalink
Add a json-conversion feature for converting JSON to valid Heights
Browse files Browse the repository at this point in the history
  • Loading branch information
teor2345 committed Apr 14, 2023
1 parent e97aa29 commit b9ec68b
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5755,6 +5755,7 @@ dependencies = [
"secp256k1",
"serde",
"serde-big-array",
"serde_json",
"serde_with 2.3.2",
"sha2 0.9.9",
"spandoc",
Expand Down
8 changes: 8 additions & 0 deletions zebra-chain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ default = []

# Production features that activate extra functionality

# Consensus-critical conversion from JSON to Zcash types
json-conversion = [
"serde_json",
]

# Experimental mining RPC support
getblocktemplate-rpcs = [
"zcash_address",
Expand Down Expand Up @@ -88,6 +93,9 @@ ed25519-zebra = "3.1.0"
redjubjub = "0.5.0"
reddsa = "0.5.0"

# Production feature json-conversion
serde_json = { version = "1.0.95", optional = true }

# Experimental feature getblocktemplate-rpcs
zcash_address = { version = "0.2.0", optional = true }

Expand Down
2 changes: 1 addition & 1 deletion zebra-chain/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub use commitment::{
};
pub use hash::Hash;
pub use header::{BlockTimeError, CountedHeader, Header, ZCASH_BLOCK_VERSION};
pub use height::{Height, HeightDiff};
pub use height::{Height, HeightDiff, TryIntoHeight};
pub use serialize::{SerializedBlock, MAX_BLOCK_BYTES};

#[cfg(any(test, feature = "proptest-impl"))]
Expand Down
49 changes: 48 additions & 1 deletion zebra-chain/src/block/height.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
use std::ops::{Add, Sub};

use crate::serialization::SerializationError;
use crate::{serialization::SerializationError, BoxError};

#[cfg(feature = "json-conversion")]
pub mod json_conversion;

/// The length of the chain back to the genesis block.
///
Expand Down Expand Up @@ -70,6 +73,9 @@ impl Height {
/// even if they are outside the valid height range (for example, in buggy RPC code).
pub type HeightDiff = i64;

// We don't implement TryFrom<u64>, because it causes type inference issues for integer constants.
// Instead, use 1u64.try_into_height().

impl TryFrom<u32> for Height {
type Error = &'static str;

Expand All @@ -84,6 +90,47 @@ impl TryFrom<u32> for Height {
}
}

/// Convenience trait for converting a type into a valid Zcash [`Height`].
pub trait TryIntoHeight {
/// The error type returned by [`Height`] conversion failures.
type Error;

/// Convert `self` to a `Height`, if possible.
fn try_into_height(&self) -> Result<Height, Self::Error>;
}

impl TryIntoHeight for u64 {
type Error = BoxError;

fn try_into_height(&self) -> Result<Height, Self::Error> {
u32::try_from(*self)?.try_into().map_err(Into::into)
}
}

impl TryIntoHeight for usize {
type Error = BoxError;

fn try_into_height(&self) -> Result<Height, Self::Error> {
u32::try_from(*self)?.try_into().map_err(Into::into)
}
}

impl TryIntoHeight for str {
type Error = BoxError;

fn try_into_height(&self) -> Result<Height, Self::Error> {
self.parse().map_err(Into::into)
}
}

impl TryIntoHeight for String {
type Error = BoxError;

fn try_into_height(&self) -> Result<Height, Self::Error> {
self.as_str().try_into_height()
}
}

// We don't implement Add<u32> or Sub<u32>, because they cause type inference issues for integer constants.

impl Sub<Height> for Height {
Expand Down
24 changes: 24 additions & 0 deletions zebra-chain/src/block/height/json_conversion.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! Consensus-critical conversion from JSON [`Value`] to [`Height`].
use serde_json::Value;

use crate::BoxError;

use super::{Height, TryIntoHeight};

impl TryIntoHeight for Value {
type Error = BoxError;

fn try_into_height(&self) -> Result<Height, Self::Error> {
if self.is_number() {
let height = self.as_u64().ok_or("JSON value outside u64 range")?;
return height.try_into_height();
}

if let Some(height) = self.as_str() {
return height.try_into_height();
}

Err("JSON value must be a number or string".into())
}
}
2 changes: 1 addition & 1 deletion zebra-rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ zcash_address = { version = "0.2.0", optional = true }
# Test-only feature proptest-impl
proptest = { version = "1.1.0", optional = true }

zebra-chain = { path = "../zebra-chain" }
zebra-chain = { path = "../zebra-chain", features = ["json-conversion"] }
zebra-consensus = { path = "../zebra-consensus" }
zebra-network = { path = "../zebra-network" }
zebra-node-services = { path = "../zebra-node-services" }
Expand Down

0 comments on commit b9ec68b

Please sign in to comment.