From bb06cef4b5c45864c5c91d43c0d4018e91915d46 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Mon, 22 Apr 2024 15:43:06 +0200 Subject: [PATCH 01/11] signing: implement serialize for unlockkey --- src/signing.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/signing.rs b/src/signing.rs index 496b7c8..7eeeaa5 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -8,11 +8,18 @@ use crate::transactions::{CoveredFields, Transaction}; use crate::{Algorithm, HexParseError, SiaEncodable}; use blake2b_simd::Params; use ed25519_dalek::{Signature as ED25519Signature, Signer, SigningKey, Verifier, VerifyingKey}; +use serde::Serialize; /// An ed25519 public key that can be used to verify a signature #[derive(Debug, PartialEq, Clone, Copy)] pub struct PublicKey([u8; 32]); +impl Serialize for PublicKey { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_bytes(&self.0) + } +} + impl PublicKey { pub fn new(buf: [u8; 32]) -> Self { PublicKey(buf) @@ -75,7 +82,7 @@ impl From for UnlockKey { /// contract /// /// Currently only supports ed25519 keys -#[derive(Debug, PartialEq, Clone, Copy)] +#[derive(Debug, PartialEq, Clone, Copy, Serialize)] pub struct UnlockKey { algorithm: Algorithm, public_key: PublicKey, @@ -351,6 +358,24 @@ mod tests { use super::*; use crate::*; + #[test] + fn test_public_key() { + let key: [u8; 32] = [ + 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, + ]; + let pk = UnlockKey::new(Algorithm::ED25519, PublicKey::new(key)); + assert_eq!( + &encoding::to_bytes(&pk).unwrap(), + &[ + 101, 100, 50, 53, 53, 49, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, + 154, 172, 31, 251, 28, 253, 16, 121, 168, 198, 200, 123, 71, 218, 29, 86, 126, 53, + 185, 114, 52, 153, 60, 40, 140, 26, 208, 219, 29, 28, 225, 182 + ] + ); + } + #[test] fn test_whole_sig_hash() { let state = SigningState { From 1de019675e5bd2cf85ea2970364248d93b22f0df Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Mon, 22 Apr 2024 16:06:47 +0200 Subject: [PATCH 02/11] signing/address: implement serialize for UnlockConditions and UnlockKey --- src/address.rs | 44 +++++++++++++++++++++++++++++--------------- src/signing.rs | 11 +---------- src/spendpolicy.rs | 6 +++++- src/transactions.rs | 7 ++++--- 4 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/address.rs b/src/address.rs index 615f05d..56dd4f0 100644 --- a/src/address.rs +++ b/src/address.rs @@ -1,8 +1,8 @@ use core::fmt; -use std::io::{Error, Write}; use crate::blake2b::{Accumulator, LEAF_HASH_PREFIX}; -use crate::{specifier::Specifier, HexParseError, PublicKey, SiaEncodable, UnlockKey}; +use crate::encoding::to_writer; +use crate::{specifier::Specifier, HexParseError, PublicKey, UnlockKey}; use blake2b_simd::Params; use serde::Serialize; @@ -116,24 +116,13 @@ impl Serialize for Algorithm { } // specifies the conditions for spending an output or revising a file contract. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Serialize)] pub struct UnlockConditions { pub timelock: u64, pub public_keys: Vec, pub required_signatures: u64, } -impl SiaEncodable for UnlockConditions { - fn encode(&self, w: &mut W) -> Result<(), Error> { - w.write_all(&self.timelock.to_le_bytes())?; - w.write_all(&(self.public_keys.len() as u64).to_le_bytes())?; - for key in &self.public_keys { - key.encode(w)?; - } - w.write_all(&self.required_signatures.to_le_bytes()) - } -} - impl UnlockConditions { pub fn new( timelock: u64, @@ -172,7 +161,7 @@ impl UnlockConditions { for key in &self.public_keys { let mut state = Params::new().hash_length(32).to_state(); state.update(LEAF_HASH_PREFIX); - key.encode(&mut state).unwrap(); + to_writer(&mut state, key).unwrap(); let h = state.finalize(); let mut leaf = [0u8; 32]; @@ -247,6 +236,31 @@ mod tests { ) } + #[test] + fn test_serialize_unlock_conditions() { + let uc = UnlockConditions::new( + 123, + vec![UnlockKey::new( + Algorithm::ED25519, + 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, + ]), + )], + 1, + ); + assert_eq!( + to_bytes(&uc).unwrap(), + [ + 123, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 101, 100, 50, 53, 53, 49, 57, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 154, 172, 31, 251, 28, 253, 16, + 121, 168, 198, 200, 123, 71, 218, 29, 86, 126, 53, 185, 114, 52, 153, 60, 40, 140, + 26, 208, 219, 29, 28, 225, 182, 1, 0, 0, 0, 0, 0, 0, 0 + ] + ) + } + #[test] fn test_standard_unlockhash() { let test_cases = vec![ diff --git a/src/signing.rs b/src/signing.rs index 7eeeaa5..796a002 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -3,7 +3,6 @@ use std::io::{Error, Write}; use std::time::SystemTime; use crate::consensus::ChainIndex; -use crate::encoding::to_writer; use crate::transactions::{CoveredFields, Transaction}; use crate::{Algorithm, HexParseError, SiaEncodable}; use blake2b_simd::Params; @@ -131,14 +130,6 @@ impl fmt::Display for UnlockKey { } } -impl SiaEncodable for UnlockKey { - fn encode(&self, w: &mut W) -> Result<(), Error> { - to_writer(w, &self.algorithm).unwrap(); // TODO: handle error - w.write_all(&32_u64.to_le_bytes())?; - w.write_all(self.public_key.as_ref()) - } -} - impl Drop for PrivateKey { fn drop(&mut self) { // Zero out the private key @@ -359,7 +350,7 @@ mod tests { use crate::*; #[test] - fn test_public_key() { + fn test_serialize_public_key() { let key: [u8; 32] = [ 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, diff --git a/src/spendpolicy.rs b/src/spendpolicy.rs index a975981..6488096 100644 --- a/src/spendpolicy.rs +++ b/src/spendpolicy.rs @@ -1,5 +1,6 @@ use blake2b_simd::Params; use core::{fmt, slice::Iter}; +use serde_json::to_writer; use sha2::{Digest, Sha256}; use std::{ io::{Error as IOError, Write}, @@ -259,7 +260,10 @@ impl SpendPolicy { } SpendPolicy::Opaque(addr) => w.write_all(addr.as_ref()), #[allow(deprecated)] - SpendPolicy::UnlockConditions(uc) => uc.encode(w), + SpendPolicy::UnlockConditions(uc) => { + to_writer(w, uc).unwrap(); + Ok(()) + } } } } diff --git a/src/transactions.rs b/src/transactions.rs index ef17075..8ac39f3 100644 --- a/src/transactions.rs +++ b/src/transactions.rs @@ -64,7 +64,8 @@ pub struct SiacoinInput { impl SiaEncodable for SiacoinInput { fn encode(&self, w: &mut W) -> Result<(), Error> { self.parent_id.encode(w)?; - self.unlock_conditions.encode(w) + to_writer(w, &self.unlock_conditions).unwrap(); + Ok(()) } } @@ -128,7 +129,7 @@ pub struct SiafundInput { impl SiaEncodable for SiafundInput { fn encode(&self, w: &mut W) -> Result<(), Error> { self.parent_id.encode(w)?; - self.unlock_conditions.encode(w)?; + to_writer(w, &self.unlock_conditions).unwrap(); to_writer(w, &self.claim_address).unwrap(); // TODO: handle error Ok(()) } @@ -235,7 +236,7 @@ pub struct FileContractRevision { impl SiaEncodable for FileContractRevision { fn encode(&self, w: &mut W) -> Result<(), Error> { w.write_all(self.parent_id.as_ref())?; - self.unlock_conditions.encode(w)?; + to_writer(w, &self.unlock_conditions).unwrap(); w.write_all(&self.revision_number.to_le_bytes())?; w.write_all(&self.file_size.to_le_bytes())?; w.write_all(&self.file_merkle_root.0)?; From 3993d9559eddcc8980993ef556e4e2a1044e114d Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Mon, 22 Apr 2024 16:55:54 +0200 Subject: [PATCH 03/11] signing: implement Serialize for more types --- src/lib.rs | 9 ++---- src/signing.rs | 19 ++++++------- src/transactions.rs | 67 +++------------------------------------------ 3 files changed, 15 insertions(+), 80 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8a2eb60..dba0750 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ pub use address::*; pub use consensus::*; pub use currency::*; pub use seed::*; +use serde::Serialize; pub use signing::*; pub use spendpolicy::*; pub use transactions::*; @@ -36,7 +37,7 @@ pub enum HexParseError { HexError(hex::FromHexError), } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)] pub struct Hash256([u8; 32]); impl Hash256 { @@ -70,12 +71,6 @@ impl fmt::Display for Hash256 { } } -impl SiaEncodable for Hash256 { - fn encode(&self, w: &mut W) -> Result<(), Error> { - w.write_all(&self.0) - } -} - impl AsRef<[u8]> for Hash256 { fn as_ref(&self) -> &[u8] { &self.0 diff --git a/src/signing.rs b/src/signing.rs index 796a002..737f1ac 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -1,5 +1,4 @@ use core::fmt; -use std::io::{Error, Write}; use std::time::SystemTime; use crate::consensus::ChainIndex; @@ -8,6 +7,7 @@ use crate::{Algorithm, HexParseError, SiaEncodable}; use blake2b_simd::Params; use ed25519_dalek::{Signature as ED25519Signature, Signer, SigningKey, Verifier, VerifyingKey}; use serde::Serialize; +use serde_json::to_writer; /// An ed25519 public key that can be used to verify a signature #[derive(Debug, PartialEq, Clone, Copy)] @@ -152,6 +152,12 @@ pub struct NetworkHardforks { #[derive(Debug, Clone, PartialEq, Eq)] pub struct Signature([u8; 64]); +impl Serialize for Signature { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_bytes(&self.0) // prefixed with length + } +} + impl Signature { pub fn new(sig: [u8; 64]) -> Self { Signature(sig) @@ -184,13 +190,6 @@ impl AsRef<[u8; 64]> for Signature { } } -impl SiaEncodable for Signature { - fn encode(&self, w: &mut W) -> Result<(), Error> { - w.write_all(&(self.0.len() as u64).to_le_bytes())?; - w.write_all(&self.0) - } -} - impl fmt::Display for Signature { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "sig:{}", hex::encode(self.0)) @@ -288,8 +287,8 @@ impl SigningState { state.update(&public_key_index.to_le_bytes()); state.update(&timelock.to_le_bytes()); - for i in covered_sigs.into_iter() { - txn.signatures[i as usize].encode(&mut state).unwrap(); + for i in covered_sigs.iter() { + to_writer(&mut state, i).unwrap(); } state.finalize().as_bytes().try_into().unwrap() diff --git a/src/transactions.rs b/src/transactions.rs index 8ac39f3..94d8346 100644 --- a/src/transactions.rs +++ b/src/transactions.rs @@ -7,6 +7,7 @@ use crate::Signature; use crate::{Address, UnlockConditions}; use crate::{Hash256, HexParseError, SiaEncodable}; use blake2b_simd::{Params, State}; +use serde::Serialize; const SIACOIN_OUTPUT_ID_PREFIX: [u8; 16] = [ b's', b'i', b'a', b'c', b'o', b'i', b'n', b' ', b'o', b'u', b't', b'p', b'u', b't', 0, 0, @@ -274,7 +275,7 @@ impl SiaEncodable for StorageProof { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] pub struct CoveredFields { pub whole_transaction: bool, pub siacoin_inputs: Vec, @@ -289,54 +290,7 @@ pub struct CoveredFields { pub signatures: Vec, } -impl SiaEncodable for CoveredFields { - fn encode(&self, w: &mut W) -> Result<(), Error> { - w.write_all(&[self.whole_transaction as u8])?; - w.write_all(&(self.siacoin_inputs.len() as u64).to_le_bytes())?; - for input in &self.siacoin_inputs { - w.write_all(&input.to_le_bytes())?; - } - w.write_all(&(self.siacoin_outputs.len() as u64).to_le_bytes())?; - for output in &self.siacoin_outputs { - w.write_all(&output.to_le_bytes())?; - } - w.write_all(&(self.siafund_inputs.len() as u64).to_le_bytes())?; - for input in &self.siafund_inputs { - w.write_all(&input.to_le_bytes())?; - } - w.write_all(&(self.siafund_outputs.len() as u64).to_le_bytes())?; - for output in &self.siafund_outputs { - w.write_all(&output.to_le_bytes())?; - } - w.write_all(&(self.file_contracts.len() as u64).to_le_bytes())?; - for file_contract in &self.file_contracts { - w.write_all(&file_contract.to_le_bytes())?; - } - w.write_all(&(self.file_contract_revisions.len() as u64).to_le_bytes())?; - for file_contract_revision in &self.file_contract_revisions { - w.write_all(&file_contract_revision.to_le_bytes())?; - } - w.write_all(&(self.storage_proofs.len() as u64).to_le_bytes())?; - for storage_proof in &self.storage_proofs { - w.write_all(&storage_proof.to_le_bytes())?; - } - w.write_all(&(self.miner_fees.len() as u64).to_le_bytes())?; - for miner_fee in &self.miner_fees { - w.write_all(&miner_fee.to_le_bytes())?; - } - w.write_all(&(self.arbitrary_data.len() as u64).to_le_bytes())?; - for arbitrary_data in &self.arbitrary_data { - w.write_all(&arbitrary_data.to_le_bytes())?; - } - w.write_all(&(self.signatures.len() as u64).to_le_bytes())?; - for signature in &self.signatures { - w.write_all(&signature.to_le_bytes())?; - } - Ok(()) - } -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] pub struct TransactionSignature { pub parent_id: Hash256, pub public_key_index: u64, @@ -345,16 +299,6 @@ pub struct TransactionSignature { pub signature: Signature, } -impl SiaEncodable for TransactionSignature { - fn encode(&self, w: &mut W) -> Result<(), Error> { - w.write_all(self.parent_id.as_ref())?; - w.write_all(&self.public_key_index.to_le_bytes())?; - w.write_all(&self.timelock.to_le_bytes())?; - self.covered_fields.encode(w)?; - self.signature.encode(w) - } -} - #[derive(Debug, Clone, PartialEq)] pub struct TransactionID([u8; 32]); @@ -576,10 +520,7 @@ impl SiaEncodable for Transaction { w.write_all(&(data.len() as u64).to_le_bytes())?; w.write_all(data)?; } - w.write_all(&(self.signatures.len() as u64).to_le_bytes())?; - for signature in &self.signatures { - signature.encode(w)?; - } + to_writer(w, &self.signatures).unwrap(); Ok(()) } } From 123a9080192d5b32c2b4064d2c16372809429db7 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Tue, 23 Apr 2024 10:06:29 +0200 Subject: [PATCH 04/11] signing: add test_json_serialize_unlockkey --- src/signing.rs | 53 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/src/signing.rs b/src/signing.rs index 737f1ac..a7c9b58 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -6,6 +6,7 @@ use crate::transactions::{CoveredFields, Transaction}; use crate::{Algorithm, HexParseError, SiaEncodable}; use blake2b_simd::Params; use ed25519_dalek::{Signature as ED25519Signature, Signer, SigningKey, Verifier, VerifyingKey}; +use serde::ser::SerializeStruct; use serde::Serialize; use serde_json::to_writer; @@ -19,6 +20,12 @@ impl Serialize for PublicKey { } } +impl fmt::Display for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + hex::encode(self.0).fmt(f) + } +} + impl PublicKey { pub fn new(buf: [u8; 32]) -> Self { PublicKey(buf) @@ -81,12 +88,31 @@ impl From for UnlockKey { /// contract /// /// Currently only supports ed25519 keys -#[derive(Debug, PartialEq, Clone, Copy, Serialize)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct UnlockKey { algorithm: Algorithm, public_key: PublicKey, } +impl Serialize for UnlockKey { + fn serialize(&self, serializer: S) -> Result { + if serializer.is_human_readable() { + serializer.serialize_str(&self.to_string()) + } else { + let mut s = serializer.serialize_struct("UnlockKey", 2)?; + s.serialize_field("algorithm", &self.algorithm)?; + s.serialize_field("public_key", &self.public_key)?; + s.end() + } + } +} + +impl fmt::Display for UnlockKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}:{}", self.algorithm, self.public_key) + } +} + impl UnlockKey { /// Creates a new UnlockKey pub fn new(algorithm: Algorithm, public_key: PublicKey) -> UnlockKey { @@ -119,17 +145,6 @@ impl UnlockKey { } } -impl fmt::Display for UnlockKey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{}:{}", - self.algorithm, - hex::encode(self.public_key.as_ref()) - ) - } -} - impl Drop for PrivateKey { fn drop(&mut self) { // Zero out the private key @@ -348,6 +363,20 @@ mod tests { use super::*; use crate::*; + #[test] + fn test_json_serialize_unlockkey() { + assert_eq!( + serde_json::to_string( + &UnlockKey::parse_string( + "ed25519:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6" + ) + .unwrap() + ) + .unwrap(), + "\"ed25519:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6\"" + ); + } + #[test] fn test_serialize_public_key() { let key: [u8; 32] = [ From 3a8f23cabe36622704808706511a1ac99cd17fa5 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Tue, 23 Apr 2024 10:31:39 +0200 Subject: [PATCH 05/11] signing: add test_json_serialize_public_key --- src/signing.rs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/signing.rs b/src/signing.rs index a7c9b58..6037faa 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -16,7 +16,11 @@ pub struct PublicKey([u8; 32]); impl Serialize for PublicKey { fn serialize(&self, serializer: S) -> Result { - serializer.serialize_bytes(&self.0) + if serializer.is_human_readable() { + return serializer.serialize_str(&self.to_string()); + } else { + serializer.serialize_bytes(&self.0) + } } } @@ -364,7 +368,20 @@ mod tests { use crate::*; #[test] - fn test_json_serialize_unlockkey() { + fn test_json_serialize_public_key() { + 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\"" + ); + } + + #[test] + fn test_json_serialize_unlock_key() { assert_eq!( serde_json::to_string( &UnlockKey::parse_string( @@ -388,9 +405,10 @@ mod tests { assert_eq!( &encoding::to_bytes(&pk).unwrap(), &[ - 101, 100, 50, 53, 53, 49, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, - 154, 172, 31, 251, 28, 253, 16, 121, 168, 198, 200, 123, 71, 218, 29, 86, 126, 53, - 185, 114, 52, 153, 60, 40, 140, 26, 208, 219, 29, 28, 225, 182 + 0x65, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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 ] ); } From f4ea294a46158ffe054786df7d1ca28e5e52c15f Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Tue, 23 Apr 2024 10:36:06 +0200 Subject: [PATCH 06/11] lib: add test_json_serialize_hash256 --- src/lib.rs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index dba0750..14b6de6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,9 +37,19 @@ pub enum HexParseError { HexError(hex::FromHexError), } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct Hash256([u8; 32]); +impl Serialize for Hash256 { + fn serialize(&self, serializer: S) -> Result { + if serializer.is_human_readable() { + serializer.serialize_str(&self.to_string()) + } else { + self.0.serialize(serializer) + } + } +} + impl Hash256 { pub fn from_slice(data: [u8; 32]) -> Self { Hash256(data) @@ -76,3 +86,20 @@ impl AsRef<[u8]> for Hash256 { &self.0 } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_json_serialize_hash256() { + let hash = Hash256::parse_string( + "h:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6", + ) + .unwrap(); + assert_eq!( + serde_json::to_string(&hash).unwrap(), + "\"h:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6\"" + ); + } +} From cfd7456895b341367c8331d042f3b87ab7b473e5 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Tue, 23 Apr 2024 10:40:28 +0200 Subject: [PATCH 07/11] signing: add test_json_serialize_signature --- src/signing.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/signing.rs b/src/signing.rs index 6037faa..76f2aab 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -173,7 +173,11 @@ pub struct Signature([u8; 64]); impl Serialize for Signature { fn serialize(&self, serializer: S) -> Result { - serializer.serialize_bytes(&self.0) // prefixed with length + if serializer.is_human_readable() { + serializer.serialize_str(&self.to_string()) + } else { + serializer.serialize_bytes(&self.0) // prefixed with length + } } } @@ -394,6 +398,20 @@ mod tests { ); } + #[test] + fn test_json_serialize_signature() { + assert_eq!( + serde_json::to_string( + &Signature::parse_string( + "sig:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b69aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6" + ) + .unwrap() + ) + .unwrap(), + "\"sig:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b69aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6\"" + ); + } + #[test] fn test_serialize_public_key() { let key: [u8; 32] = [ From 14229cad6340b78f28f4624f3b957d90b32a9aa7 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Tue, 23 Apr 2024 10:56:01 +0200 Subject: [PATCH 08/11] signing: add test_json_serialize_transaction_signature --- src/transactions.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/transactions.rs b/src/transactions.rs index 94d8346..8d9e60f 100644 --- a/src/transactions.rs +++ b/src/transactions.rs @@ -276,6 +276,7 @@ impl SiaEncodable for StorageProof { } #[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] pub struct CoveredFields { pub whole_transaction: bool, pub siacoin_inputs: Vec, @@ -290,8 +291,28 @@ pub struct CoveredFields { pub signatures: Vec, } +impl Default for CoveredFields { + fn default() -> Self { + CoveredFields { + whole_transaction: false, + siacoin_inputs: Vec::new(), + siacoin_outputs: Vec::new(), + siafund_inputs: Vec::new(), + siafund_outputs: Vec::new(), + file_contracts: Vec::new(), + file_contract_revisions: Vec::new(), + storage_proofs: Vec::new(), + miner_fees: Vec::new(), + arbitrary_data: Vec::new(), + signatures: Vec::new(), + } + } +} + #[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] pub struct TransactionSignature { + #[serde(rename = "parentID")] pub parent_id: Hash256, pub public_key_index: u64, pub timelock: u64, @@ -529,6 +550,18 @@ impl SiaEncodable for Transaction { mod tests { use super::*; + #[test] + fn test_json_serialize_transaction_signature() { + let txn_sig = TransactionSignature { + parent_id: Hash256([0u8; 32]), + public_key_index: 1, + timelock: 2, + covered_fields: CoveredFields::default(), + signature: Signature::new([0u8; 64]), + }; + assert_eq!(serde_json::to_string(&txn_sig).unwrap(), "{\"parentID\":\"h:0000000000000000000000000000000000000000000000000000000000000000\",\"publicKeyIndex\":1,\"timelock\":2,\"coveredFields\":{\"wholeTransaction\":false,\"siacoinInputs\":[],\"siacoinOutputs\":[],\"siafundInputs\":[],\"siafundOutputs\":[],\"fileContracts\":[],\"fileContractRevisions\":[],\"storageProofs\":[],\"minerFees\":[],\"arbitraryData\":[],\"signatures\":[]},\"signature\":\"sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"}") + } + #[test] fn test_transaction_id() { let txn = Transaction::default(); From 5fd6596899d3cae1ff2c2c7ff21ebcf9071be1e8 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Tue, 23 Apr 2024 10:59:22 +0200 Subject: [PATCH 09/11] address: add test_json_serialize_unlock_conditions --- src/address.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/address.rs b/src/address.rs index 56dd4f0..b9c449c 100644 --- a/src/address.rs +++ b/src/address.rs @@ -117,6 +117,7 @@ impl Serialize for Algorithm { // specifies the conditions for spending an output or revising a file contract. #[derive(Debug, PartialEq, Clone, Serialize)] +#[serde(rename_all = "camelCase")] pub struct UnlockConditions { pub timelock: u64, pub public_keys: Vec, @@ -236,6 +237,23 @@ mod tests { ) } + #[test] + fn test_json_serialize_unlock_conditions() { + let uc = UnlockConditions::new( + 123, + vec![UnlockKey::new( + Algorithm::ED25519, + 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, + ]), + )], + 1, + ); + assert_eq!(serde_json::to_string(&uc).unwrap(), "{\"timelock\":123,\"publicKeys\":[\"ed25519:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6\"],\"requiredSignatures\":1}") + } + #[test] fn test_serialize_unlock_conditions() { let uc = UnlockConditions::new( From 950e07632d041dbde33e9d640437613418994e27 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Tue, 23 Apr 2024 11:02:03 +0200 Subject: [PATCH 10/11] transasctions: add test_json_serialize_covered_fields --- src/transactions.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/transactions.rs b/src/transactions.rs index 8d9e60f..bc67a12 100644 --- a/src/transactions.rs +++ b/src/transactions.rs @@ -550,6 +550,15 @@ impl SiaEncodable for Transaction { mod tests { use super::*; + #[test] + fn test_json_serialize_covered_fields() { + let mut cf = CoveredFields::default(); + cf.siacoin_inputs.push(1); + cf.siacoin_outputs.push(2); + cf.siacoin_outputs.push(3); + assert_eq!(serde_json::to_string(&cf).unwrap(), "{\"wholeTransaction\":false,\"siacoinInputs\":[1],\"siacoinOutputs\":[2,3],\"siafundInputs\":[],\"siafundOutputs\":[],\"fileContracts\":[],\"fileContractRevisions\":[],\"storageProofs\":[],\"minerFees\":[],\"arbitraryData\":[],\"signatures\":[]}") + } + #[test] fn test_json_serialize_transaction_signature() { let txn_sig = TransactionSignature { From ed3edd9b655dc70bc71aab6df3987b31d7c6c987 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Tue, 23 Apr 2024 11:06:15 +0200 Subject: [PATCH 11/11] clippy: fix --- src/signing.rs | 2 +- src/transactions.rs | 20 +------------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/signing.rs b/src/signing.rs index 76f2aab..db2609c 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -17,7 +17,7 @@ pub struct PublicKey([u8; 32]); impl Serialize for PublicKey { fn serialize(&self, serializer: S) -> Result { if serializer.is_human_readable() { - return serializer.serialize_str(&self.to_string()); + serializer.serialize_str(&self.to_string()) } else { serializer.serialize_bytes(&self.0) } diff --git a/src/transactions.rs b/src/transactions.rs index bc67a12..8a4ec8a 100644 --- a/src/transactions.rs +++ b/src/transactions.rs @@ -275,7 +275,7 @@ impl SiaEncodable for StorageProof { } } -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Default, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct CoveredFields { pub whole_transaction: bool, @@ -291,24 +291,6 @@ pub struct CoveredFields { pub signatures: Vec, } -impl Default for CoveredFields { - fn default() -> Self { - CoveredFields { - whole_transaction: false, - siacoin_inputs: Vec::new(), - siacoin_outputs: Vec::new(), - siafund_inputs: Vec::new(), - siafund_outputs: Vec::new(), - file_contracts: Vec::new(), - file_contract_revisions: Vec::new(), - storage_proofs: Vec::new(), - miner_fees: Vec::new(), - arbitrary_data: Vec::new(), - signatures: Vec::new(), - } - } -} - #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct TransactionSignature {