Skip to content

Commit

Permalink
transactions: deserialize transaction signatures
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisSchinnerl committed Jul 30, 2024
1 parent 84202cd commit b05bdc8
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 32 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ name = "sia_core"
path = "src/lib.rs"

[dependencies]
base64 = "0.22.1"
bip39 = "2.0.0"
blake2b_simd = "1.0.2"
ed25519-dalek = "2.1.1"
Expand Down
41 changes: 26 additions & 15 deletions src/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use core::fmt;
use std::time::SystemTime;

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

Expand Down Expand Up @@ -117,13 +118,37 @@ 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() {
String::serialize(&self.to_string(), serializer)
String::serialize(&BASE64_STANDARD.encode(&self.0[..]), serializer)
} else {
<[u8]>::serialize(&self.0, serializer) // prefixed with length
}
}
}

impl<'de> Deserialize<'de> for Signature {
fn deserialize<D>(deserializer: D) -> Result<Signature, D::Error>
where
D: serde::Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = String::deserialize(deserializer)?;
let sig = BASE64_STANDARD
.decode(s)
.map_err(|e| D::Error::custom(format!("{:?}", e)))?;
if sig.len() != 64 {
return Err(D::Error::custom("Invalid signature length"));
}
Ok(Signature(sig.try_into().unwrap()))
} else {
let data = <Vec<u8>>::deserialize(deserializer)?;
if data.len() != 64 {
return Err(D::Error::custom("Invalid signature length"));
}
Ok(Signature(data.try_into().unwrap()))
}
}
}

impl Signature {
pub fn new(sig: [u8; 64]) -> Self {
Signature(sig)
Expand Down Expand Up @@ -236,18 +261,4 @@ mod tests {
);
assert_eq!(public_key_deserialized, public_key);
}

#[test]
fn test_json_serialize_signature() {
assert_eq!(
serde_json::to_string(
&Signature::parse_string(
"sig:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b69aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6"
)
.unwrap()
)
.unwrap(),
"\"sig:9aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b69aac1ffb1cfd1079a8c6c87b47da1d567e35b97234993c288c1ad0db1d1ce1b6\""
);
}
}
68 changes: 51 additions & 17 deletions src/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,17 @@ pub struct StorageProof {
pub proof: Vec<Hash256>,
}

#[derive(Debug, Default, Clone, Serialize)]
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CoveredFields {
pub whole_transaction: bool,
pub siacoin_inputs: Vec<usize>,
pub siacoin_outputs: Vec<usize>,
pub siafund_inputs: Vec<usize>,
pub siafund_outputs: Vec<usize>,
pub file_contracts: Vec<usize>,
pub file_contract_revisions: Vec<usize>,
pub storage_proofs: Vec<usize>,
pub siafund_inputs: Vec<usize>,
pub siafund_outputs: Vec<usize>,
pub miner_fees: Vec<usize>,
pub arbitrary_data: Vec<usize>,
pub signatures: Vec<usize>,
Expand All @@ -113,7 +113,7 @@ impl CoveredFields {
}
}

#[derive(Debug, Clone, Serialize)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionSignature {
#[serde(rename = "parentID")]
Expand Down Expand Up @@ -439,7 +439,7 @@ mod tests {
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\":[]}")
assert_eq!(serde_json::to_string(&cf).unwrap(), "{\"wholeTransaction\":false,\"siacoinInputs\":[1],\"siacoinOutputs\":[2,3],\"fileContracts\":[],\"fileContractRevisions\":[],\"storageProofs\":[],\"siafundInputs\":[],\"siafundOutputs\":[],\"minerFees\":[],\"arbitraryData\":[],\"signatures\":[]}")
}

#[test]
Expand Down Expand Up @@ -471,18 +471,6 @@ mod tests {
}
}

#[test]
fn test_json_serialize_transaction_signature() {
let txn_sig = TransactionSignature {
parent_id: Hash256::default(),
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_serialize_siacoin_input() {
let siacoin_input = SiacoinInput {
Expand Down Expand Up @@ -617,6 +605,52 @@ mod tests {
assert_eq!(output_deserialized, output);
}

#[test]
fn test_serialize_transaction_signature() {
let signature = TransactionSignature {
parent_id: Hash256::parse_string(
"b3633a1370a72002ae2a956d21e8d481c3a69e146633470cf625ecd83fdeaa24",
)
.unwrap(),
public_key_index: 1,
timelock: 2,
covered_fields: CoveredFields {
whole_transaction: true,
..Default::default()
},
signature: Signature::new([3u8; 64]),
};

// binary
let signature_serialized = to_bytes(&signature).unwrap();
let signature_deserialized: TransactionSignature =
from_reader(&mut &signature_serialized[..]).unwrap();
assert_eq!(
signature_serialized,
[
179, 99, 58, 19, 112, 167, 32, 2, 174, 42, 149, 109, 33, 232, 212, 129, 195, 166,
158, 20, 102, 51, 71, 12, 246, 37, 236, 216, 63, 222, 170, 36, 1, 0, 0, 0, 0, 0, 0,
0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
]
);
assert_eq!(signature_deserialized, signature);

// json
let signature_serialized = serde_json::to_string(&signature).unwrap();
let signature_deserialized: TransactionSignature =
serde_json::from_str(&signature_serialized).unwrap();
assert_eq!(
signature_serialized,
"{\"parentID\":\"h:b3633a1370a72002ae2a956d21e8d481c3a69e146633470cf625ecd83fdeaa24\",\"publicKeyIndex\":1,\"timelock\":2,\"coveredFields\":{\"wholeTransaction\":true,\"siacoinInputs\":[],\"siacoinOutputs\":[],\"fileContracts\":[],\"fileContractRevisions\":[],\"storageProofs\":[],\"siafundInputs\":[],\"siafundOutputs\":[],\"minerFees\":[],\"arbitraryData\":[],\"signatures\":[]},\"signature\":\"AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw==\"}",
);
assert_eq!(signature_deserialized, signature);
}

#[test]
fn test_serialize_siafund_output() {
let addr_str =
Expand Down

0 comments on commit b05bdc8

Please sign in to comment.