Skip to content

Commit

Permalink
Merge pull request #33 from SiaFoundation/chris/filecontract-deserialize
Browse files Browse the repository at this point in the history
Fix FileContract and FileContractRevision serialization
  • Loading branch information
n8maninger authored Jul 23, 2024
2 parents 41dabc9 + 8bef221 commit 56e10d3
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 293 deletions.
167 changes: 95 additions & 72 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use blake2b_simd::{Hash, Params};
use blake2b_simd::Params;
use core::fmt;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
Expand All @@ -14,92 +14,115 @@ impl fmt::Display for ChainIndex {
}
}

/// encapsulates the various errors that can occur when parsing a Sia object
/// from a string
#[derive(Debug, PartialEq)]
pub enum HexParseError {
MissingPrefix,
InvalidLength,
InvalidPrefix,
InvalidChecksum, // not every object has a checksum
HexError(hex::FromHexError),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Hash256([u8; 32]);
// Macro to implement types used as identifiers which are 32 byte hashes and are
// serialized with a prefix
#[macro_export]
macro_rules! ImplHashID {
($name:ident, $prefix:expr) => {
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct $name([u8; 32]);

impl serde::Serialize for $name {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
String::serialize(&self.to_string(), serializer)
} else {
<[u8; 32]>::serialize(&self.0, serializer)
}
}
}

impl Serialize for Hash256 {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
String::serialize(&self.to_string(), serializer)
} else {
<[u8; 32]>::serialize(&self.0, serializer)
impl<'de> serde::Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = String::deserialize(deserializer)?;
$name::parse_string(&s)
.map_err(|e| serde::de::Error::custom(format!("{:?}", e)))
} else {
let data = <[u8; 32]>::deserialize(deserializer)?;
Ok($name(data))
}
}
}
}
}

impl<'de> Deserialize<'de> for Hash256 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = String::deserialize(deserializer)?;
Hash256::parse_string(&s).map_err(|e| serde::de::Error::custom(format!("{:?}", e)))
} else {
let data = <[u8; 32]>::deserialize(deserializer)?;
Ok(Hash256(data))
impl $name {
// Example method that might be used in serialization/deserialization
pub fn parse_string(s: &str) -> Result<Self, HexParseError> {
let s = match s.split_once(':') {
Some((_prefix, suffix)) => suffix,
None => s,
};

if s.len() != 64 {
return Err(HexParseError::InvalidLength);
}

let mut data = [0u8; 32];
hex::decode_to_slice(s, &mut data).map_err(HexParseError::HexError)?;
Ok($name(data))
}
}
}
}

impl Hash256 {
pub fn new(data: [u8; 32]) -> Self {
Hash256(data)
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}:{}", $prefix, hex::encode(self.0))
}
}

pub fn as_bytes(&self) -> &[u8] {
self.0.as_ref()
}
impl From<blake2b_simd::Hash> for $name {
fn from(hash: blake2b_simd::Hash) -> Self {
let mut h = [0; 32];
h.copy_from_slice(&hash.as_bytes()[..32]);
Self(h)
}
}

pub fn as_array(&self) -> &[u8; 32] {
&self.0
}
impl From<[u8; 32]> for $name {
fn from(data: [u8; 32]) -> Self {
$name(data)
}
}

pub fn parse_string(s: &str) -> Result<Self, HexParseError> {
let s = match s.split_once(':') {
Some((_prefix, suffix)) => suffix,
None => s,
};
impl From<$name> for [u8; 32] {
fn from(hash: $name) -> [u8; 32] {
hash.0
}
}

if s.len() != 64 {
return Err(HexParseError::InvalidLength);
impl AsRef<[u8; 32]> for $name {
fn as_ref(&self) -> &[u8; 32] {
&self.0
}
}

let mut data = [0u8; 32];
hex::decode_to_slice(s, &mut data).map_err(HexParseError::HexError)?;
Ok(Hash256(data))
}
}
impl AsRef<[u8]> for $name {
fn as_ref(&self) -> &[u8] {
&self.0
}
}

impl fmt::Display for Hash256 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "h:{}", hex::encode(self.0))
}
impl Default for $name {
fn default() -> Self {
$name([0; 32])
}
}
};
}

impl From<Hash> for Hash256 {
fn from(hash: Hash) -> Self {
let mut h = [0; 32];
h.copy_from_slice(&hash.as_bytes()[..32]);
Self(h)
}
}
ImplHashID!(Hash256, "h");

impl AsRef<[u8]> for Hash256 {
fn as_ref(&self) -> &[u8] {
&self.0
}
/// encapsulates the various errors that can occur when parsing a Sia object
/// from a string
#[derive(Debug, PartialEq)]
pub enum HexParseError {
MissingPrefix,
InvalidLength,
InvalidPrefix,
InvalidChecksum, // not every object has a checksum
HexError(hex::FromHexError),
}

/// An address that can be used to receive UTXOs
Expand Down
7 changes: 7 additions & 0 deletions src/currency.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use core::num::ParseIntError;
use core::ops::{Add, Deref, DerefMut, Div, Mul, Sub};
use std::iter::Sum;

use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -200,6 +201,12 @@ impl Div for Currency {
}
}

impl Sum for Currency {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Currency::new(0), Add::add)
}
}

#[derive(Debug, PartialEq)]
pub enum CurrencyParseError {
ParseIntErr(ParseIntError),
Expand Down
2 changes: 1 addition & 1 deletion src/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl PrivateKey {

impl From<Hash256> for PrivateKey {
fn from(hash: Hash256) -> Self {
PrivateKey::from_seed(hash.as_array())
PrivateKey::from_seed(hash.as_ref())
}
}

Expand Down
22 changes: 11 additions & 11 deletions src/spendpolicy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ impl SpendPolicy {
.next()
.ok_or(ValidationError::MissingSignature)
.and_then(|sig| {
pk.verify(hash.as_bytes(), sig)
pk.verify(hash.as_ref(), sig)
.then_some(())
.ok_or(ValidationError::InvalidSignature)
}),
Expand Down Expand Up @@ -215,7 +215,7 @@ impl SpendPolicy {
let mut remaining = uc.signatures_required;
for pk in uc.public_keys.iter() {
let sig = signatures.next().ok_or(ValidationError::MissingSignature)?;
if pk.public_key().verify(hash.as_bytes(), sig) {
if pk.public_key().verify(hash.as_ref(), sig) {
remaining -= 1;
if remaining == 0 {
break;
Expand Down Expand Up @@ -512,7 +512,7 @@ mod tests {
},
{
let pk = PrivateKey::from_seed(&random());
let sig_hash = Hash256::new(random());
let sig_hash = Hash256::from(random::<[u8; 32]>());

PolicyTest {
policy: SpendPolicy::PublicKey(pk.public_key()),
Expand All @@ -525,14 +525,14 @@ mod tests {
hardforks: NetworkHardforks::default(),
},
hash: sig_hash,
signatures: vec![pk.sign(sig_hash.as_bytes())],
signatures: vec![pk.sign(sig_hash.as_ref())],
preimages: vec![],
result: Ok(()),
}
},
{
let pk = PrivateKey::from_seed(&random());
let sig_hash = Hash256::new(random());
let sig_hash = Hash256::from(random::<[u8; 32]>());

PolicyTest {
policy: SpendPolicy::Threshold(
Expand All @@ -551,14 +551,14 @@ mod tests {
hardforks: NetworkHardforks::default(),
},
hash: sig_hash,
signatures: vec![pk.sign(sig_hash.as_bytes())],
signatures: vec![pk.sign(sig_hash.as_ref())],
preimages: vec![],
result: Err(ValidationError::ThresholdNotMet),
}
},
{
let pk = PrivateKey::from_seed(&random());
let sig_hash = Hash256::new(random());
let sig_hash = Hash256::from(random::<[u8; 32]>());

PolicyTest {
policy: SpendPolicy::Threshold(
Expand All @@ -584,7 +584,7 @@ mod tests {
},
{
let pk = PrivateKey::from_seed(&random());
let sig_hash = Hash256::new(random());
let sig_hash = Hash256::from(random::<[u8; 32]>());

PolicyTest {
policy: SpendPolicy::Threshold(
Expand All @@ -603,14 +603,14 @@ mod tests {
hardforks: NetworkHardforks::default(),
},
hash: sig_hash,
signatures: vec![pk.sign(sig_hash.as_bytes())],
signatures: vec![pk.sign(sig_hash.as_ref())],
preimages: vec![],
result: Ok(()),
}
},
{
let pk = PrivateKey::from_seed(&random());
let sig_hash = Hash256::new(random());
let sig_hash = Hash256::from(random::<[u8; 32]>());

PolicyTest {
policy: SpendPolicy::Threshold(
Expand All @@ -636,7 +636,7 @@ mod tests {
},
{
let pk = PrivateKey::from_seed(&random());
let sig_hash = Hash256::new(random());
let sig_hash = Hash256::from(random::<[u8; 32]>());

PolicyTest {
policy: SpendPolicy::Threshold(
Expand Down
Loading

0 comments on commit 56e10d3

Please sign in to comment.