parity-zcash/rpc/src/v1/types/transaction.rs

487 lines
17 KiB
Rust

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<u32>,
}
/// 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<TransactionOutput>,
}
/// 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<Address>,
}
/// 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<SignedTransactionInput>,
/// Transaction outputs
pub vout: Vec<SignedTransactionOutput>,
/// 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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<D>(deserializer: D) -> Result<Self, D::Error>
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<V>(self, mut visitor: V) -> Result<TransactionOutputs, V::Error>
where
V: MapAccess<'b>,
{
let mut outputs: Vec<TransactionOutput> =
Vec::with_capacity(visitor.size_hint().unwrap_or(0));
while let Some(key) = try!(visitor.next_key::<String>()) {
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::<TransactionInput>(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::<TransactionOutputs>(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::<TransactionInputScript>(
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::<TransactionOutputScript>(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::<SignedTransactionInput>(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::<SignedTransactionOutput>(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::<Transaction>(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);
}
}