Skip to content

Commit

Permalink
Merge pull request #32 from SiaFoundation/chris/deserialize-common
Browse files Browse the repository at this point in the history
Implement Deserialize for `PublicKey`, `Algorithm`, `UnlockConditions`, `SiacoinOutputID`, `SiacoinInputID`, `SiacoinOutput`, `SiacoinInput`, `SiafundOutput` and `SiafundInput`
  • Loading branch information
n8maninger authored Jul 15, 2024
2 parents ee447f6 + 3ca744a commit d1c4a4e
Show file tree
Hide file tree
Showing 7 changed files with 396 additions and 106 deletions.
4 changes: 2 additions & 2 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ pub struct Hash256([u8; 32]);
impl Serialize for Hash256 {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
serializer.serialize_str(&self.to_string())
String::serialize(&self.to_string(), serializer)
} else {
self.0.serialize(serializer)
<[u8; 32]>::serialize(&self.0, serializer)
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/currency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub struct Currency(u128);
impl Serialize for Currency {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
serializer.serialize_str(&self.to_string())
String::serialize(&self.to_string(), serializer)
} else {
let currency_buf = self.to_be_bytes();
let i = currency_buf
Expand All @@ -41,7 +41,7 @@ impl<'de> Deserialize<'de> for Currency {
return Err(serde::de::Error::custom("invalid currency length"));
}
let mut buf = [0; 16];
buf[..data.len()].copy_from_slice(&data);
buf[16 - data.len()..].copy_from_slice(&data);
Ok(Currency(u128::from_be_bytes(buf)))
}
}
Expand Down
65 changes: 52 additions & 13 deletions src/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,45 @@ use std::time::SystemTime;

use crate::{ChainIndex, Hash256, HexParseError};
use ed25519_dalek::{Signature as ED25519Signature, Signer, SigningKey, Verifier, VerifyingKey};
use serde::Serialize;
use serde::{de::Error, Deserialize, Serialize};

/// An ed25519 public key that can be used to verify a signature
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct PublicKey([u8; 32]);

impl PublicKey {
const PREFIX: &'static str = "ed25519:";
}

impl Serialize for PublicKey {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
serializer.serialize_str(&self.to_string())
String::serialize(
&format!("{}{}", Self::PREFIX, &self.to_string()),
serializer,
)
} else {
serializer.serialize_bytes(&self.0)
<[u8; 32]>::serialize(&self.0, serializer)
}
}
}

impl<'de> Deserialize<'de> for PublicKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = String::deserialize(deserializer)?;
let s = s.strip_prefix(Self::PREFIX).ok_or(Error::custom(format!(
"key must have prefix '{}'",
Self::PREFIX
)))?;
let mut pk = [0; 32];
hex::decode_to_slice(s, &mut pk).map_err(|e| Error::custom(format!("{:?}", e)))?;
Ok(Self::new(pk))
} else {
Ok(PublicKey(<[u8; 32]>::deserialize(deserializer)?))
}
}
}
Expand Down Expand Up @@ -90,9 +117,9 @@ pub struct Signature([u8; 64]);
impl Serialize for Signature {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
serializer.serialize_str(&self.to_string())
String::serialize(&self.to_string(), serializer)
} else {
serializer.serialize_bytes(&self.0) // prefixed with length
<[u8]>::serialize(&self.0, serializer) // prefixed with length
}
}
}
Expand Down Expand Up @@ -183,19 +210,31 @@ impl SigningState {

#[cfg(test)]
mod tests {
use crate::encoding::{from_reader, to_bytes};

use super::*;

#[test]
fn test_json_serialize_public_key() {
fn test_serialize_publickey() {
let public_key_str = "9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6";
let public_key = PublicKey::new(hex::decode(public_key_str).unwrap().try_into().unwrap());

// binary
let public_key_serialized = to_bytes(&public_key).unwrap();
let public_key_deserialized: PublicKey =
from_reader(&mut &public_key_serialized[..]).unwrap();
assert_eq!(public_key_serialized, hex::decode(public_key_str).unwrap());
assert_eq!(public_key_deserialized, public_key);

// json
let public_key_serialized = serde_json::to_string(&public_key).unwrap();
let public_key_deserialized: PublicKey =
serde_json::from_str(&public_key_serialized).unwrap();
assert_eq!(
serde_json::to_string(&PublicKey::new([
0x9a, 0xac, 0x1f, 0xfb, 0x1c, 0xfd, 0x10, 0x79, 0xa8, 0xc6, 0xc8, 0x7b, 0x47, 0xda,
0x1d, 0x56, 0x7e, 0x35, 0xb9, 0x72, 0x34, 0x99, 0x3c, 0x28, 0x8c, 0x1a, 0xd0, 0xdb,
0x1d, 0x1c, 0xe1, 0xb6,
]))
.unwrap(),
"\"9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6\""
public_key_serialized,
format!("\"ed25519:{0}\"", public_key_str)
);
assert_eq!(public_key_deserialized, public_key);
}

#[test]
Expand Down
4 changes: 2 additions & 2 deletions src/specifier.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use core::{fmt, str};

use serde::Serialize;
use serde::{Deserialize, Serialize};

pub const SPECIFIER_SIZE: usize = 16;

#[derive(Debug, PartialEq, Serialize)]
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Specifier([u8; SPECIFIER_SIZE]);

impl Specifier {
Expand Down
8 changes: 4 additions & 4 deletions src/spendpolicy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,11 @@ impl SpendPolicy {
SpendPolicy::UnlockConditions(uc) => {
if uc.timelock > signing_state.index.height {
return Err(ValidationError::InvalidHeight);
} else if uc.required_signatures > 255 {
} else if uc.signatures_required > 255 {
return Err(ValidationError::InvalidPolicy);
}

let mut remaining = uc.required_signatures;
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) {
Expand Down Expand Up @@ -268,7 +268,7 @@ impl SpendPolicy {
impl Serialize for SpendPolicy {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
serializer.serialize_str(&self.to_string())
String::serialize(&self.to_string(), serializer)
} else {
// unknown length since policie are recursive and need custom
// serialize/deserialize implementations anyway.
Expand Down Expand Up @@ -305,7 +305,7 @@ impl fmt::Display for SpendPolicy {
SpendPolicy::Opaque(addr) => write!(f, "opaque(0x{})", hex::encode(addr)),
#[allow(deprecated)]
SpendPolicy::UnlockConditions(uc) => {
write!(f, "uc({},{},[", uc.timelock, uc.required_signatures)?;
write!(f, "uc({},{},[", uc.timelock, uc.signatures_required)?;
for (i, pk) in uc.public_keys.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
Expand Down
Loading

0 comments on commit d1c4a4e

Please sign in to comment.