Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Serialize for multiple types #13

Merged
merged 11 commits into from
Apr 29, 2024
44 changes: 29 additions & 15 deletions src/address.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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<UnlockKey>,
pub required_signatures: u64,
}

impl SiaEncodable for UnlockConditions {
fn encode<W: Write>(&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,
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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![
Expand Down
9 changes: 2 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand All @@ -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 {
Expand Down Expand Up @@ -70,12 +71,6 @@ impl fmt::Display for Hash256 {
}
}

impl SiaEncodable for Hash256 {
fn encode<W: Write>(&self, w: &mut W) -> Result<(), Error> {
w.write_all(&self.0)
}
}

impl AsRef<[u8]> for Hash256 {
fn as_ref(&self) -> &[u8] {
&self.0
Expand Down
55 changes: 35 additions & 20 deletions src/signing.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
use core::fmt;
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;
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)]
pub struct PublicKey([u8; 32]);

impl Serialize for PublicKey {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_bytes(&self.0)
}
}

impl PublicKey {
pub fn new(buf: [u8; 32]) -> Self {
PublicKey(buf)
Expand Down Expand Up @@ -75,7 +81,7 @@ impl From<PrivateKey> 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,
Expand Down Expand Up @@ -124,14 +130,6 @@ impl fmt::Display for UnlockKey {
}
}

impl SiaEncodable for UnlockKey {
fn encode<W: Write>(&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
Expand All @@ -154,6 +152,12 @@ pub struct NetworkHardforks {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Signature([u8; 64]);

impl Serialize for Signature {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_bytes(&self.0) // prefixed with length
}
}

impl Signature {
pub fn new(sig: [u8; 64]) -> Self {
Signature(sig)
Expand Down Expand Up @@ -186,13 +190,6 @@ impl AsRef<[u8; 64]> for Signature {
}
}

impl SiaEncodable for Signature {
fn encode<W: Write>(&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))
Expand Down Expand Up @@ -290,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()
Expand Down Expand Up @@ -351,6 +348,24 @@ mod tests {
use super::*;
use crate::*;

#[test]
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,
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 {
Expand Down
6 changes: 5 additions & 1 deletion src/spendpolicy.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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(())
}
}
}
}
Expand Down
74 changes: 8 additions & 66 deletions src/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -64,7 +65,8 @@ pub struct SiacoinInput {
impl SiaEncodable for SiacoinInput {
fn encode<W: Write>(&self, w: &mut W) -> Result<(), Error> {
self.parent_id.encode(w)?;
self.unlock_conditions.encode(w)
to_writer(w, &self.unlock_conditions).unwrap();
Ok(())
}
}

Expand Down Expand Up @@ -128,7 +130,7 @@ pub struct SiafundInput {
impl SiaEncodable for SiafundInput {
fn encode<W: Write>(&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(())
}
Expand Down Expand Up @@ -235,7 +237,7 @@ pub struct FileContractRevision {
impl SiaEncodable for FileContractRevision {
fn encode<W: Write>(&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)?;
Expand Down Expand Up @@ -273,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<u64>,
Expand All @@ -288,54 +290,7 @@ pub struct CoveredFields {
pub signatures: Vec<u64>,
}

impl SiaEncodable for CoveredFields {
fn encode<W: Write>(&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,
Expand All @@ -344,16 +299,6 @@ pub struct TransactionSignature {
pub signature: Signature,
}

impl SiaEncodable for TransactionSignature {
fn encode<W: Write>(&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]);

Expand Down Expand Up @@ -575,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(())
}
}
Expand Down