use chia_bls::PublicKey;
use chia_protocol::{CoinSpend, SpendBundle};
use serde::{Deserialize, Serialize};
use super::value::TransactionSummary;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RequiredSignature {
pub public_key: PublicKey,
#[serde(with = "hex_bytes")]
pub message: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UnsignedSpend {
pub coin_spends: Vec<CoinSpend>,
pub required_signatures: Vec<RequiredSignature>,
pub summary: TransactionSummary,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SignedBundle {
pub bundle: SpendBundle,
}
mod hex_bytes {
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S: Serializer>(bytes: &[u8], s: S) -> Result<S::Ok, S::Error> {
s.serialize_str(&hex::encode(bytes))
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
let s = String::deserialize(d)?;
hex::decode(&s).map_err(serde::de::Error::custom)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::Amount;
#[test]
fn required_signature_round_trips_with_hex_message() {
let sig = RequiredSignature {
public_key: PublicKey::default(),
message: vec![0xde, 0xad, 0xbe, 0xef],
};
let json = serde_json::to_string(&sig).unwrap();
assert!(
json.contains("deadbeef"),
"message must be hex-encoded: {json}"
);
let back: RequiredSignature = serde_json::from_str(&json).unwrap();
assert_eq!(back, sig);
}
#[test]
fn unsigned_spend_round_trips() {
let unsigned = UnsignedSpend {
coin_spends: vec![],
required_signatures: vec![],
summary: TransactionSummary {
melted_singletons: Vec::new(),
nft_operations: Vec::new(),
received: vec![],
outputs: vec![],
fee: Amount(0),
},
};
let json = serde_json::to_string(&unsigned).unwrap();
let back: UnsignedSpend = serde_json::from_str(&json).unwrap();
assert_eq!(back, unsigned);
}
#[test]
fn signed_bundle_round_trips() {
let signed = SignedBundle {
bundle: SpendBundle::new(vec![], chia_bls::Signature::default()),
};
let json = serde_json::to_string(&signed).unwrap();
let back: SignedBundle = serde_json::from_str(&json).unwrap();
assert_eq!(back, signed);
}
#[test]
fn bad_hex_message_is_rejected() {
let json = r#"{"public_key":"","message":"zz"}"#;
assert!(serde_json::from_str::<RequiredSignature>(json).is_err());
}
}