use super::bytes::Bytes; use super::hash::H256; use super::script::ScriptType; use serde::ser::SerializeMap; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use v1::types; use zebra_keys::Address; /// Hex-encoded transaction pub type RawTransaction = Bytes; /// Transaction input #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct TransactionInput { /// Previous transaction id pub txid: H256, /// Previous transaction output index pub vout: u32, /// Sequence number pub sequence: Option, } /// Transaction output of form "address": amount #[derive(Debug, PartialEq)] pub struct TransactionOutputWithAddress { /// Receiver' address pub address: Address, /// Amount in BTC pub amount: f64, } /// Transaction output of form "data": serialized(output script data) #[derive(Debug, PartialEq)] pub struct TransactionOutputWithScriptData { /// Serialized script data pub script_data: Bytes, } /// Transaction output #[derive(Debug, PartialEq)] pub enum TransactionOutput { /// Of form address: amount Address(TransactionOutputWithAddress), /// Of form data: script_data_bytes ScriptData(TransactionOutputWithScriptData), } /// Transaction outputs, which serializes/deserializes as KV-map #[derive(Debug, PartialEq)] pub struct TransactionOutputs { /// Transaction outputs pub outputs: Vec, } /// Transaction input script #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct TransactionInputScript { /// Script code pub asm: String, /// Script hex pub hex: Bytes, } /// Transaction output script #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct TransactionOutputScript { /// Script code pub asm: String, /// Script hex pub hex: Bytes, /// Number of required signatures #[serde(rename = "reqSigs")] pub req_sigs: u32, /// Type of script #[serde(rename = "type")] pub script_type: ScriptType, /// Array of bitcoin addresses #[serde(with = "types::address::vec")] pub addresses: Vec
, } /// Signed transaction input #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct SignedTransactionInput { /// Previous transaction id pub txid: H256, /// Previous transaction output index pub vout: u32, /// Input script pub script_sig: TransactionInputScript, /// Sequence number pub sequence: u32, } /// Signed transaction output #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct SignedTransactionOutput { /// Output value in BTC pub value: f64, /// Output index pub n: u32, /// Output script #[serde(rename = "scriptPubKey")] pub script: TransactionOutputScript, } /// Transaction #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct Transaction { /// Raw transaction pub hex: RawTransaction, /// The transaction id (same as provided) pub txid: H256, /// The transaction hash pub hash: H256, /// The serialized transaction size pub size: usize, /// The version pub version: i32, /// The lock time pub locktime: i32, /// Transaction inputs pub vin: Vec, /// Transaction outputs pub vout: Vec, /// Hash of the block this transaction is included in pub blockhash: H256, /// Number of confirmations of this transaction pub confirmations: u32, /// The transaction time in seconds since epoch (Jan 1 1970 GMT) pub time: u32, /// The block time in seconds since epoch (Jan 1 1970 GMT) pub blocktime: u32, } /// Return value of `getrawtransaction` method #[derive(Debug, PartialEq)] pub enum GetRawTransactionResponse { /// Return value when asking for raw transaction Raw(RawTransaction), /// Return value when asking for verbose transaction Verbose(Transaction), } impl Serialize for GetRawTransactionResponse { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { GetRawTransactionResponse::Raw(ref raw_transaction) => { raw_transaction.serialize(serializer) } GetRawTransactionResponse::Verbose(ref verbose_transaction) => { verbose_transaction.serialize(serializer) } } } } impl TransactionOutputs { pub fn len(&self) -> usize { self.outputs.len() } } impl Serialize for TransactionOutputs { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let mut state = serializer.serialize_map(Some(self.len()))?; for output in &self.outputs { match output { &TransactionOutput::Address(ref address_output) => { state.serialize_entry( &address_output.address.to_string(), &address_output.amount, )?; } &TransactionOutput::ScriptData(ref script_output) => { state.serialize_entry("data", &script_output.script_data)?; } } } state.end() } } impl<'a> Deserialize<'a> for TransactionOutputs { fn deserialize(deserializer: D) -> Result where D: Deserializer<'a>, { use serde::de::{MapAccess, Visitor}; struct TransactionOutputsVisitor; impl<'b> Visitor<'b> for TransactionOutputsVisitor { type Value = TransactionOutputs; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a transaction output object") } fn visit_map(self, mut visitor: V) -> Result where V: MapAccess<'b>, { let mut outputs: Vec = Vec::with_capacity(visitor.size_hint().unwrap_or(0)); while let Some(key) = try!(visitor.next_key::()) { if &key == "data" { let value: Bytes = try!(visitor.next_value()); outputs.push(TransactionOutput::ScriptData( TransactionOutputWithScriptData { script_data: value }, )); } else { let address = types::address::AddressVisitor::default().visit_str(&key)?; let amount: f64 = try!(visitor.next_value()); outputs.push(TransactionOutput::Address(TransactionOutputWithAddress { address: address, amount: amount, })); } } Ok(TransactionOutputs { outputs: outputs }) } } deserializer.deserialize_any(TransactionOutputsVisitor) } } #[cfg(test)] mod tests { use super::super::bytes::Bytes; use super::super::hash::H256; use super::super::script::ScriptType; use super::*; use serde_json; #[test] fn transaction_input_serialize() { let txinput = TransactionInput { txid: H256::from(7), vout: 33, sequence: Some(88), }; assert_eq!(serde_json::to_string(&txinput).unwrap(), r#"{"txid":"0700000000000000000000000000000000000000000000000000000000000000","vout":33,"sequence":88}"#); } #[test] fn transaction_input_deserialize() { let txinput = TransactionInput { txid: H256::from(7), vout: 33, sequence: Some(88), }; assert_eq!( serde_json::from_str::(r#"{"txid":"0700000000000000000000000000000000000000000000000000000000000000","vout":33,"sequence":88}"#).unwrap(), txinput); } #[test] fn transaction_outputs_serialize() { let txout = TransactionOutputs { outputs: vec![ TransactionOutput::Address(TransactionOutputWithAddress { address: "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), amount: 123.45, }), TransactionOutput::Address(TransactionOutputWithAddress { address: "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into(), amount: 67.89, }), TransactionOutput::ScriptData(TransactionOutputWithScriptData { script_data: Bytes::new(vec![1, 2, 3, 4]), }), TransactionOutput::ScriptData(TransactionOutputWithScriptData { script_data: Bytes::new(vec![5, 6, 7, 8]), }), ], }; assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi":123.45,"t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543":67.89,"data":"01020304","data":"05060708"}"#); } #[test] fn transaction_outputs_deserialize() { let txout = TransactionOutputs { outputs: vec![ TransactionOutput::Address(TransactionOutputWithAddress { address: "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), amount: 123.45, }), TransactionOutput::Address(TransactionOutputWithAddress { address: "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into(), amount: 67.89, }), TransactionOutput::ScriptData(TransactionOutputWithScriptData { script_data: Bytes::new(vec![1, 2, 3, 4]), }), TransactionOutput::ScriptData(TransactionOutputWithScriptData { script_data: Bytes::new(vec![5, 6, 7, 8]), }), ], }; assert_eq!( serde_json::from_str::(r#"{"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi":123.45,"t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543":67.89,"data":"01020304","data":"05060708"}"#).unwrap(), txout); } #[test] fn transaction_input_script_serialize() { let txin = TransactionInputScript { asm: "Hello, world!!!".to_owned(), hex: Bytes::new(vec![1, 2, 3, 4]), }; assert_eq!( serde_json::to_string(&txin).unwrap(), r#"{"asm":"Hello, world!!!","hex":"01020304"}"# ); } #[test] fn transaction_input_script_deserialize() { let txin = TransactionInputScript { asm: "Hello, world!!!".to_owned(), hex: Bytes::new(vec![1, 2, 3, 4]), }; assert_eq!( serde_json::from_str::( r#"{"asm":"Hello, world!!!","hex":"01020304"}"# ) .unwrap(), txin ); } #[test] fn transaction_output_script_serialize() { let txout = TransactionOutputScript { asm: "Hello, world!!!".to_owned(), hex: Bytes::new(vec![1, 2, 3, 4]), req_sigs: 777, script_type: ScriptType::Multisig, addresses: vec![ "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into(), ], }; assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"]}"#); } #[test] fn transaction_output_script_deserialize() { let txout = TransactionOutputScript { asm: "Hello, world!!!".to_owned(), hex: Bytes::new(vec![1, 2, 3, 4]), req_sigs: 777, script_type: ScriptType::Multisig, addresses: vec![ "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into(), ], }; assert_eq!( serde_json::from_str::(r#"{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"]}"#).unwrap(), txout); } #[test] fn signed_transaction_input_serialize() { let txin = SignedTransactionInput { txid: H256::from(77), vout: 13, script_sig: TransactionInputScript { asm: "Hello, world!!!".to_owned(), hex: Bytes::new(vec![1, 2, 3, 4]), }, sequence: 123, }; assert_eq!(serde_json::to_string(&txin).unwrap(), r#"{"txid":"4d00000000000000000000000000000000000000000000000000000000000000","vout":13,"script_sig":{"asm":"Hello, world!!!","hex":"01020304"},"sequence":123}"#); } #[test] fn signed_transaction_input_deserialize() { let txin = SignedTransactionInput { txid: H256::from(77), vout: 13, script_sig: TransactionInputScript { asm: "Hello, world!!!".to_owned(), hex: Bytes::new(vec![1, 2, 3, 4]), }, sequence: 123, }; assert_eq!( serde_json::from_str::(r#"{"txid":"4d00000000000000000000000000000000000000000000000000000000000000","vout":13,"script_sig":{"asm":"Hello, world!!!","hex":"01020304"},"sequence":123}"#).unwrap(), txin); } #[test] fn signed_transaction_output_serialize() { let txout = SignedTransactionOutput { value: 777.79, n: 12, script: TransactionOutputScript { asm: "Hello, world!!!".to_owned(), hex: Bytes::new(vec![1, 2, 3, 4]), req_sigs: 777, script_type: ScriptType::Multisig, addresses: vec![ "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into(), ], }, }; assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"value":777.79,"n":12,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"]}}"#); } #[test] fn signed_transaction_output_deserialize() { let txout = SignedTransactionOutput { value: 777.79, n: 12, script: TransactionOutputScript { asm: "Hello, world!!!".to_owned(), hex: Bytes::new(vec![1, 2, 3, 4]), req_sigs: 777, script_type: ScriptType::Multisig, addresses: vec![ "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".into(), "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543".into(), ], }, }; assert_eq!( serde_json::from_str::(r#"{"value":777.79,"n":12,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi","t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543"]}}"#).unwrap(), txout); } #[test] fn transaction_serialize() { let tx = Transaction { hex: "DEADBEEF".into(), txid: H256::from(4), hash: H256::from(5), size: 33, version: 55, locktime: 66, vin: vec![], vout: vec![], blockhash: H256::from(6), confirmations: 77, time: 88, blocktime: 99, }; assert_eq!(serde_json::to_string(&tx).unwrap(), r#"{"hex":"deadbeef","txid":"0400000000000000000000000000000000000000000000000000000000000000","hash":"0500000000000000000000000000000000000000000000000000000000000000","size":33,"version":55,"locktime":66,"vin":[],"vout":[],"blockhash":"0600000000000000000000000000000000000000000000000000000000000000","confirmations":77,"time":88,"blocktime":99}"#); } #[test] fn transaction_deserialize() { let tx = Transaction { hex: "DEADBEEF".into(), txid: H256::from(4), hash: H256::from(5), size: 33, version: 55, locktime: 66, vin: vec![], vout: vec![], blockhash: H256::from(6), confirmations: 77, time: 88, blocktime: 99, }; assert_eq!( serde_json::from_str::(r#"{"hex":"deadbeef","txid":"0400000000000000000000000000000000000000000000000000000000000000","hash":"0500000000000000000000000000000000000000000000000000000000000000","size":33,"version":55,"locktime":66,"vin":[],"vout":[],"blockhash":"0600000000000000000000000000000000000000000000000000000000000000","confirmations":77,"time":88,"blocktime":99}"#).unwrap(), tx); } }