From 59aad9ec30adced2657a72d44294b934c9af0580 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 13 Dec 2016 13:27:45 +0300 Subject: [PATCH 1/6] continue work on 'raw' RPC methods --- rpc/src/v1/impls/blockchain.rs | 10 +- rpc/src/v1/impls/raw.rs | 23 +- rpc/src/v1/traits/raw.rs | 16 +- rpc/src/v1/types/{raw_block.rs => block.rs} | 1 + rpc/src/v1/types/block_template.rs | 2 +- rpc/src/v1/types/get_block_response.rs | 2 +- rpc/src/v1/types/get_tx_out_response.rs | 54 +--- rpc/src/v1/types/mod.rs.in | 12 +- rpc/src/v1/types/raw_transaction.rs | 3 - rpc/src/v1/types/transaction.rs | 338 ++++++++++++++++++++ 10 files changed, 390 insertions(+), 71 deletions(-) rename rpc/src/v1/types/{raw_block.rs => block.rs} (70%) delete mode 100644 rpc/src/v1/types/raw_transaction.rs create mode 100644 rpc/src/v1/types/transaction.rs diff --git a/rpc/src/v1/impls/blockchain.rs b/rpc/src/v1/impls/blockchain.rs index 755142eb..340fa4fd 100644 --- a/rpc/src/v1/impls/blockchain.rs +++ b/rpc/src/v1/impls/blockchain.rs @@ -1,6 +1,6 @@ use v1::traits::BlockChain; use v1::types::{GetBlockResponse, VerboseBlock, RawBlock}; -use v1::types::{GetTxOutResponse, TxOutScriptPubKey}; +use v1::types::{GetTxOutResponse, TransactionOutputScript}; use v1::types::GetTxOutSetInfoResponse; use v1::types::H256; use v1::types::U256; @@ -138,7 +138,7 @@ impl BlockChainClientCoreApi for BlockChainClientCore { bestblock: block_header.hash().into(), confirmations: best_block.number - meta.height() + 1, value: 0.00000001f64 * (transaction.outputs[prev_out.index as usize].value as f64), - script_pub_key: TxOutScriptPubKey { + script: TransactionOutputScript { asm: script_asm, hex: script_bytes.clone().into(), req_sigs: script.num_signatures_required() as u32, @@ -221,7 +221,7 @@ pub mod tests { use primitives::hash::H256 as GlobalH256; use v1::types::{VerboseBlock, RawBlock}; use v1::traits::BlockChain; - use v1::types::{GetTxOutResponse, TxOutScriptPubKey}; + use v1::types::{GetTxOutResponse, TransactionOutputScript}; use v1::helpers::errors::block_not_found; use v1::types::Bytes; use v1::types::H256; @@ -285,7 +285,7 @@ pub mod tests { bestblock: H256::from(0x56), confirmations: 777, value: 100000.56, - script_pub_key: TxOutScriptPubKey { + script: TransactionOutputScript { asm: "Hello, world!!!".to_owned(), hex: Bytes::new(vec![1, 2, 3, 4]), req_sigs: 777, @@ -553,7 +553,7 @@ pub mod tests { bestblock: "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000".into(), confirmations: 1, value: 50.0, - script_pub_key: TxOutScriptPubKey { + script: TransactionOutputScript { asm: "OP_PUSHBYTES_65 0x04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f\nOP_CHECKSIG\n".to_owned(), hex: Bytes::from("4104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"), req_sigs: 1, diff --git a/rpc/src/v1/impls/raw.rs b/rpc/src/v1/impls/raw.rs index 556e1001..08db3df4 100644 --- a/rpc/src/v1/impls/raw.rs +++ b/rpc/src/v1/impls/raw.rs @@ -1,9 +1,10 @@ use v1::traits::Raw; -use v1::types::RawTransaction; +use v1::types::{RawTransaction, TransactionInput, TransactionOutput, Transaction, GetRawTransactionResponse}; use v1::types::H256; use v1::helpers::errors::{execution, invalid_params}; use jsonrpc_core::Error; -use chain::Transaction; +use jsonrpc_macros::Trailing; +use chain::Transaction as GlobalTransaction; use sync; use ser::{Reader, deserialize}; use primitives::hash::H256 as GlobalH256; @@ -13,7 +14,7 @@ pub struct RawClient { } pub trait RawClientCoreApi: Send + Sync + 'static { - fn accept_transaction(&self, transaction: Transaction) -> Result; + fn accept_transaction(&self, transaction: GlobalTransaction) -> Result; } pub struct RawClientCore { @@ -29,10 +30,10 @@ impl RawClientCore { } impl RawClientCoreApi for RawClientCore { - fn accept_transaction(&self, transaction: Transaction) -> Result { + fn accept_transaction(&self, transaction: GlobalTransaction) -> Result { self.local_sync_node.accept_transaction(transaction) -} } +} impl RawClient where T: RawClientCoreApi { pub fn new(core: T) -> Self { @@ -50,6 +51,18 @@ impl Raw for RawClient where T: RawClientCoreApi { .map(|h| h.reversed().into()) .map_err(|e| execution(e)) } + + fn create_raw_transaction(&self, _inputs: Vec, _outputs: Vec, _lock_time: Trailing) -> Result { + rpc_unimplemented!() + } + + fn decode_raw_transaction(&self, _transaction: RawTransaction) -> Result { + rpc_unimplemented!() + } + + fn get_raw_transaction(&self, _hash: H256, _verbose: Trailing) -> Result { + rpc_unimplemented!() + } } #[cfg(test)] diff --git a/rpc/src/v1/traits/raw.rs b/rpc/src/v1/traits/raw.rs index 3406083c..55a7ce50 100644 --- a/rpc/src/v1/traits/raw.rs +++ b/rpc/src/v1/traits/raw.rs @@ -1,7 +1,12 @@ +use jsonrpc_macros::Trailing; use jsonrpc_core::Error; -use v1::types::RawTransaction; use v1::types::H256; +use v1::types::RawTransaction; +use v1::types::Transaction; +use v1::types::TransactionInput; +use v1::types::TransactionOutput; +use v1::types::GetRawTransactionResponse; build_rpc_trait! { /// Partiy-bitcoin raw data interface. @@ -9,5 +14,14 @@ build_rpc_trait! { /// Adds transaction to the memory pool && relays it to the peers. #[rpc(name = "sendrawtransaction")] fn send_raw_transaction(&self, RawTransaction) -> Result; + /// Create a transaction spending the given inputs and creating new outputs. + #[rpc(name = "createrawtransaction")] + fn create_raw_transaction(&self, Vec, Vec, Trailing) -> Result; + /// Return an object representing the serialized, hex-encoded transaction. + #[rpc(name = "decoderawtransaction")] + fn decode_raw_transaction(&self, RawTransaction) -> Result; + /// Return the raw transaction data. + #[rpc(name = "getrawtransaction")] + fn get_raw_transaction(&self, H256, Trailing) -> Result; } } diff --git a/rpc/src/v1/types/raw_block.rs b/rpc/src/v1/types/block.rs similarity index 70% rename from rpc/src/v1/types/raw_block.rs rename to rpc/src/v1/types/block.rs index 3ac34003..e79fc43d 100644 --- a/rpc/src/v1/types/raw_block.rs +++ b/rpc/src/v1/types/block.rs @@ -1,3 +1,4 @@ use super::bytes::Bytes; +/// Hex-encoded block pub type RawBlock = Bytes; diff --git a/rpc/src/v1/types/block_template.rs b/rpc/src/v1/types/block_template.rs index dac43b48..706e7516 100644 --- a/rpc/src/v1/types/block_template.rs +++ b/rpc/src/v1/types/block_template.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use super::hash::H256; -use super::raw_transaction::RawTransaction; +use super::transaction::RawTransaction; use db; use miner; diff --git a/rpc/src/v1/types/get_block_response.rs b/rpc/src/v1/types/get_block_response.rs index 864e9d0b..89b3b0e9 100644 --- a/rpc/src/v1/types/get_block_response.rs +++ b/rpc/src/v1/types/get_block_response.rs @@ -1,7 +1,7 @@ use serde::{Serialize, Serializer}; use super::hash::H256; use super::uint::U256; -use super::raw_block::RawBlock; +use super::block::RawBlock; /// Response to getblock RPC request #[derive(Debug)] diff --git a/rpc/src/v1/types/get_tx_out_response.rs b/rpc/src/v1/types/get_tx_out_response.rs index 5cfa4a81..be8b89e6 100644 --- a/rpc/src/v1/types/get_tx_out_response.rs +++ b/rpc/src/v1/types/get_tx_out_response.rs @@ -1,7 +1,5 @@ -use super::address::Address; -use super::bytes::Bytes; use super::hash::H256; -use super::script::ScriptType; +use super::transaction::TransactionOutputScript; /// gettxout response #[derive(Debug, Serialize, Deserialize, PartialEq)] @@ -15,30 +13,13 @@ pub struct GetTxOutResponse { pub value: f64, /// Script info #[serde(rename = "scriptPubKey")] - pub script_pub_key: TxOutScriptPubKey, + pub script: TransactionOutputScript, /// This transaction version pub version: i32, /// Is this transactio a coinbase transaction? pub coinbase: bool, } -/// Script pub key information -#[derive(Debug, Serialize, Deserialize, PartialEq)] -pub struct TxOutScriptPubKey { - /// 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 - pub addresses: Vec
, -} - #[cfg(test)] mod tests { use serde_json; @@ -53,7 +34,7 @@ mod tests { bestblock: H256::from(0x56), confirmations: 777, value: 100000.56, - script_pub_key: TxOutScriptPubKey { + script: TransactionOutputScript { asm: "Hello, world!!!".to_owned(), hex: Bytes::new(vec![1, 2, 3, 4]), req_sigs: 777, @@ -72,7 +53,7 @@ mod tests { bestblock: H256::from(0x56), confirmations: 777, value: 100000.56, - script_pub_key: TxOutScriptPubKey { + script: TransactionOutputScript { asm: "Hello, world!!!".to_owned(), hex: Bytes::new(vec![1, 2, 3, 4]), req_sigs: 777, @@ -86,31 +67,4 @@ mod tests { serde_json::from_str::(r#"{"bestblock":"5600000000000000000000000000000000000000000000000000000000000000","confirmations":777,"value":100000.56,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]},"version":33,"coinbase":false}"#).unwrap(), txout); } - - #[test] - fn tx_out_script_pubkey_serialize() { - let txout = TxOutScriptPubKey { - asm: "Hello, world!!!".to_owned(), - hex: Bytes::new(vec![1, 2, 3, 4]), - req_sigs: 777, - script_type: ScriptType::Multisig, - addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()], - }; - assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]}"#); - } - - #[test] - fn tx_out_script_pubkey_deserialize() { - let txout = TxOutScriptPubKey { - asm: "Hello, world!!!".to_owned(), - hex: Bytes::new(vec![1, 2, 3, 4]), - req_sigs: 777, - script_type: ScriptType::Multisig, - addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()], - }; - - assert_eq!( - serde_json::from_str::(r#"{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]}"#).unwrap(), - txout); - } } diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index 75f7d7fd..2ad8fb83 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -1,4 +1,5 @@ mod address; +mod block; mod block_template; mod block_template_request; mod bytes; @@ -6,20 +7,21 @@ mod get_block_response; mod get_tx_out_response; mod get_tx_out_set_info_response; mod hash; -mod raw_block; -mod raw_transaction; mod script; +mod transaction; mod uint; pub use self::address::Address; +pub use self::block::RawBlock; pub use self::block_template::{BlockTemplate, BlockTemplateTransaction}; pub use self::block_template_request::{BlockTemplateRequest, BlockTemplateRequestMode}; pub use self::bytes::Bytes; pub use self::get_block_response::{GetBlockResponse, VerboseBlock}; -pub use self::get_tx_out_response::{GetTxOutResponse, TxOutScriptPubKey}; +pub use self::get_tx_out_response::GetTxOutResponse; pub use self::get_tx_out_set_info_response::GetTxOutSetInfoResponse; pub use self::hash::{H160, H256}; -pub use self::raw_block::RawBlock; -pub use self::raw_transaction::RawTransaction; pub use self::script::ScriptType; +pub use self::transaction::{RawTransaction, Transaction, TransactionInput, TransactionOutput, + TransactionInputScript, TransactionOutputScript, SignedTransactionInput, GetRawTransactionResponse, + SignedTransactionOutput}; pub use self::uint::U256; diff --git a/rpc/src/v1/types/raw_transaction.rs b/rpc/src/v1/types/raw_transaction.rs deleted file mode 100644 index 2c36e782..00000000 --- a/rpc/src/v1/types/raw_transaction.rs +++ /dev/null @@ -1,3 +0,0 @@ -use super::bytes::Bytes; - -pub type RawTransaction = Bytes; diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs new file mode 100644 index 00000000..b0dae5dd --- /dev/null +++ b/rpc/src/v1/types/transaction.rs @@ -0,0 +1,338 @@ +use serde::{Serialize, Serializer}; + +use super::address::Address; +use super::bytes::Bytes; +use super::hash::H256; +use super::script::ScriptType; + +/// 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 +#[derive(Debug, Serialize, Deserialize, PartialEq)] +pub struct TransactionOutput { + /// Receiver' address + pub address: Address, + /// Amount in BTC + pub amount: f64, +} + +/// 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 + 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, + /// Hex-encoded witness data (if any) + pub txinwitness: Vec, +} + +/// 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 (differs from txid for witness transactions) + pub hash: H256, + /// The serialized transaction size + pub size: usize, + /// The virtual transaction size (differs from size for witness transactions) + pub vsize: 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: &mut S) -> Result<(), 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), + } + } +} + +#[cfg(test)] +mod tests { + use serde_json; + use super::super::bytes::Bytes; + use super::super::hash::H256; + use super::super::script::ScriptType; + use super::*; + + #[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_output_serialize() { + let txout = TransactionOutput { + address: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), + amount: 123.45, + }; + assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"address":"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","amount":123.45}"#); + } + + #[test] + fn transaction_output_deserialize() { + let txout = TransactionOutput { + address: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), + amount: 123.45, + }; + assert_eq!( + serde_json::from_str::(r#"{"address":"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","amount":123.45}"#).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!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()], + }; + assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]}"#); + } + + #[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!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()], + }; + + assert_eq!( + serde_json::from_str::(r#"{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]}"#).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, + txinwitness: vec![], + }; + assert_eq!(serde_json::to_string(&txin).unwrap(), r#"{"txid":"4d00000000000000000000000000000000000000000000000000000000000000","vout":13,"script_sig":{"asm":"Hello, world!!!","hex":"01020304"},"sequence":123,"txinwitness":[]}"#); + } + + #[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, + txinwitness: vec![], + }; + assert_eq!( + serde_json::from_str::(r#"{"txid":"4d00000000000000000000000000000000000000000000000000000000000000","vout":13,"script_sig":{"asm":"Hello, world!!!","hex":"01020304"},"sequence":123,"txinwitness":[]}"#).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!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".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":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]}}"#); + } + + #[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!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into()], + }, + }; + assert_eq!( + serde_json::from_str::(r#"{"value":777.79,"n":12,"scriptPubKey":{"asm":"Hello, world!!!","hex":"01020304","reqSigs":777,"type":"multisig","addresses":["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","1H5m1XzvHsjWX3wwU781ubctznEpNACrNC"]}}"#).unwrap(), + txout); + } + + #[test] + fn transaction_serialize() { + let tx = Transaction { + hex: "DEADBEEF".into(), + txid: H256::from(4), + hash: H256::from(5), + size: 33, + vsize: 44, + 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,"vsize":44,"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, + vsize: 44, + 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,"vsize":44,"version":55,"locktime":66,"vin":[],"vout":[],"blockhash":"0600000000000000000000000000000000000000000000000000000000000000","confirmations":77,"time":88,"blocktime":99}"#).unwrap(), + tx); + } +} From 89290a05a575d7f9400fa6aa07a7b9c0d8cf681b Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 13 Dec 2016 13:34:42 +0300 Subject: [PATCH 2/6] fix after merge --- rpc/src/v1/impls/blockchain.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rpc/src/v1/impls/blockchain.rs b/rpc/src/v1/impls/blockchain.rs index 2214c1ea..63ae0c16 100644 --- a/rpc/src/v1/impls/blockchain.rs +++ b/rpc/src/v1/impls/blockchain.rs @@ -16,7 +16,6 @@ use verification; use ser::serialize; use network::Magic; use primitives::hash::H256 as GlobalH256; -use network::Magic; pub struct BlockChainClient { core: T, From 3b31ef9216db68cc0df4741448b26b0d6fcf7eaa Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 13 Dec 2016 14:03:55 +0300 Subject: [PATCH 3/6] fixed compilation --- rpc/src/v1/types/get_tx_out_response.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rpc/src/v1/types/get_tx_out_response.rs b/rpc/src/v1/types/get_tx_out_response.rs index be8b89e6..76dcbded 100644 --- a/rpc/src/v1/types/get_tx_out_response.rs +++ b/rpc/src/v1/types/get_tx_out_response.rs @@ -26,6 +26,7 @@ mod tests { use super::super::bytes::Bytes; use super::super::hash::H256; use super::super::script::ScriptType; + use super::super::transaction::TransactionOutputScript; use super::*; #[test] From 9ff3879e2061a933d00991bf7397cf70c724abaf Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 13 Dec 2016 14:26:16 +0300 Subject: [PATCH 4/6] added curl examples for RPCs --- rpc/src/v1/traits/blockchain.rs | 6 ++++++ rpc/src/v1/traits/miner.rs | 1 + rpc/src/v1/traits/raw.rs | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/rpc/src/v1/traits/blockchain.rs b/rpc/src/v1/traits/blockchain.rs index 2b75ddd0..85330fd3 100644 --- a/rpc/src/v1/traits/blockchain.rs +++ b/rpc/src/v1/traits/blockchain.rs @@ -11,21 +11,27 @@ build_rpc_trait! { /// Parity-bitcoin blockchain data interface. pub trait BlockChain { /// Get hash of best block. + /// @curl-example: curl --data-binary '{"jsonrpc": "2.0", "method": "getbestblockhash", "params": [], "id":1 }' -H 'content-type: application/json;' http://127.0.0.1:8332/ #[rpc(name = "getbestblockhash")] fn best_block_hash(&self) -> Result; /// Get hash of block at given height. + /// @curl-example: curl --data-binary '{"jsonrpc": "2.0", "method": "getblockhash", "params": [0], "id":1 }' -H 'content-type: application/json;' http://127.0.0.1:8332/ #[rpc(name = "getblockhash")] fn block_hash(&self, u32) -> Result; /// Get proof-of-work difficulty as a multiple of the minimum difficulty + /// @curl-example: curl --data-binary '{"jsonrpc": "2.0", "method": "getdifficulty", "params": [], "id":1 }' -H 'content-type: application/json;' http://127.0.0.1:8332/ #[rpc(name = "getdifficulty")] fn difficulty(&self) -> Result; /// Get information on given block. + /// @curl-example: curl --data-binary '{"jsonrpc": "2.0", "method": "getblock", "params": ["000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"], "id":1 }' -H 'content-type: application/json;' http://127.0.0.1:8332/ #[rpc(name = "getblock")] fn block(&self, H256, Trailing) -> Result; /// Get details about an unspent transaction output. + /// @curl-example: curl --data-binary '{"jsonrpc": "2.0", "method": "gettxout", "params": ["4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", 0], "id":1 }' -H 'content-type: application/json;' http://127.0.0.1:8332/ #[rpc(name = "gettxout")] fn transaction_out(&self, H256, u32, Trailing) -> Result; /// Get statistics about the unspent transaction output set. + /// @curl-example: curl --data-binary '{"jsonrpc": "2.0", "method": "gettxoutsetinfo", "params": [], "id":1 }' -H 'content-type: application/json;' http://127.0.0.1:8332/ #[rpc(name = "gettxoutsetinfo")] fn transaction_out_set_info(&self) -> Result; } diff --git a/rpc/src/v1/traits/miner.rs b/rpc/src/v1/traits/miner.rs index c3c47194..1c67114f 100644 --- a/rpc/src/v1/traits/miner.rs +++ b/rpc/src/v1/traits/miner.rs @@ -6,6 +6,7 @@ build_rpc_trait! { /// Partiy-bitcoin miner data interface. pub trait Miner { /// Get block template for mining. + /// @curl-example: curl --data-binary '{"jsonrpc": "2.0", "method": "getblocktemplate", "params": [{"capabilities": ["coinbasetxn", "workid", "coinbase/append"]}], "id":1 }' -H 'content-type: application/json;' http://127.0.0.1:8332/ #[rpc(name = "getblocktemplate")] fn get_block_template(&self, BlockTemplateRequest) -> Result; } diff --git a/rpc/src/v1/traits/raw.rs b/rpc/src/v1/traits/raw.rs index 55a7ce50..4a093c30 100644 --- a/rpc/src/v1/traits/raw.rs +++ b/rpc/src/v1/traits/raw.rs @@ -12,15 +12,19 @@ build_rpc_trait! { /// Partiy-bitcoin raw data interface. pub trait Raw { /// Adds transaction to the memory pool && relays it to the peers. + /// @curl-example: curl --data-binary '{"jsonrpc": "2.0", "method": "sendrawtransaction", "params": ["01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"], "id":1 }' -H 'content-type: application/json;' http://127.0.0.1:8332/ #[rpc(name = "sendrawtransaction")] fn send_raw_transaction(&self, RawTransaction) -> Result; /// Create a transaction spending the given inputs and creating new outputs. + /// @curl-example: curl --data-binary '{"jsonrpc": "2.0", "method": "createrawtransaction", "params": [[{"txid":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","vout":0}],{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":0.01}], "id":1 }' -H 'content-type: application/json;' http://127.0.0.1:8332/ #[rpc(name = "createrawtransaction")] fn create_raw_transaction(&self, Vec, Vec, Trailing) -> Result; /// Return an object representing the serialized, hex-encoded transaction. + /// @curl-example: curl --data-binary '{"jsonrpc": "2.0", "method": "decoderawtransaction", "params": ["01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"], "id":1 }' -H 'content-type: application/json;' http://127.0.0.1:8332/ #[rpc(name = "decoderawtransaction")] fn decode_raw_transaction(&self, RawTransaction) -> Result; /// Return the raw transaction data. + /// @curl-example: curl --data-binary '{"jsonrpc": "2.0", "method": "getrawtransaction", "params": ["4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"], "id":1 }' -H 'content-type: application/json;' http://127.0.0.1:8332/ #[rpc(name = "getrawtransaction")] fn get_raw_transaction(&self, H256, Trailing) -> Result; } From d7f28285975c5bed78d4d3ed14cc6409e875491d Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 13 Dec 2016 15:36:35 +0300 Subject: [PATCH 5/6] TransactionOutputs struct --- rpc/src/v1/types/transaction.rs | 92 ++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index b0dae5dd..0305c9fe 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -1,4 +1,4 @@ -use serde::{Serialize, Serializer}; +use serde::{Serialize, Serializer, Deserialize, Deserializer}; use super::address::Address; use super::bytes::Bytes; @@ -28,6 +28,13 @@ pub struct TransactionOutput { pub amount: f64, } +/// 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 { @@ -130,6 +137,53 @@ impl Serialize for GetRawTransactionResponse { } } +impl TransactionOutputs { + pub fn len(&self) -> usize { + self.outputs.len() + } +} + +impl Serialize for TransactionOutputs { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { + let mut state = try!(serializer.serialize_map(Some(self.len()))); + for output in &self.outputs { + try!(serializer.serialize_map_key(&mut state, &output.address)); + try!(serializer.serialize_map_value(&mut state, &output.amount)); + } + serializer.serialize_map_end(state) + } +} + +impl Deserialize for TransactionOutputs { + fn deserialize(deserializer: &mut D) -> Result where D: Deserializer { + use serde::de::{Visitor, MapVisitor}; + + struct TransactionOutputsVisitor; + + impl Visitor for TransactionOutputsVisitor { + type Value = TransactionOutputs; + + fn visit_map(&mut self, mut visitor: V) -> Result where V: MapVisitor { + let mut outputs: Vec = Vec::with_capacity(visitor.size_hint().0); + + while let Some((address, amount)) = try!(visitor.visit()) { + outputs.push(TransactionOutput { + address: address, + amount: amount, + }); + } + + try!(visitor.end()); + Ok(TransactionOutputs { + outputs: outputs, + }) + } + } + + deserializer.deserialize(TransactionOutputsVisitor) + } +} + #[cfg(test)] mod tests { use serde_json; @@ -181,6 +235,42 @@ mod tests { txout); } + #[test] + fn transaction_outputs_serialize() { + let txout = TransactionOutputs { + outputs: vec![ + TransactionOutput { + address: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), + amount: 123.45, + }, + TransactionOutput { + address: "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into(), + amount: 67.89, + }, + ] + }; + assert_eq!(serde_json::to_string(&txout).unwrap(), r#"{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":123.45,"1H5m1XzvHsjWX3wwU781ubctznEpNACrNC":67.89}"#); + } + + #[test] + fn transaction_outputs_deserialize() { + let txout = TransactionOutputs { + outputs: vec![ + TransactionOutput { + address: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into(), + amount: 123.45, + }, + TransactionOutput { + address: "1H5m1XzvHsjWX3wwU781ubctznEpNACrNC".into(), + amount: 67.89, + }, + ] + }; + assert_eq!( + serde_json::from_str::(r#"{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":123.45,"1H5m1XzvHsjWX3wwU781ubctznEpNACrNC":67.89}"#).unwrap(), + txout); + } + #[test] fn transaction_input_script_serialize() { let txin = TransactionInputScript { From 6bccd1cb380a684367229d34cd756f2bb53bb758 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 13 Dec 2016 16:58:03 +0300 Subject: [PATCH 6/6] used TransactionOutputs in traits --- rpc/src/v1/impls/raw.rs | 4 ++-- rpc/src/v1/traits/raw.rs | 4 ++-- rpc/src/v1/types/mod.rs.in | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rpc/src/v1/impls/raw.rs b/rpc/src/v1/impls/raw.rs index 08db3df4..5c2eacfe 100644 --- a/rpc/src/v1/impls/raw.rs +++ b/rpc/src/v1/impls/raw.rs @@ -1,5 +1,5 @@ use v1::traits::Raw; -use v1::types::{RawTransaction, TransactionInput, TransactionOutput, Transaction, GetRawTransactionResponse}; +use v1::types::{RawTransaction, TransactionInput, TransactionOutputs, Transaction, GetRawTransactionResponse}; use v1::types::H256; use v1::helpers::errors::{execution, invalid_params}; use jsonrpc_core::Error; @@ -52,7 +52,7 @@ impl Raw for RawClient where T: RawClientCoreApi { .map_err(|e| execution(e)) } - fn create_raw_transaction(&self, _inputs: Vec, _outputs: Vec, _lock_time: Trailing) -> Result { + fn create_raw_transaction(&self, _inputs: Vec, _outputs: TransactionOutputs, _lock_time: Trailing) -> Result { rpc_unimplemented!() } diff --git a/rpc/src/v1/traits/raw.rs b/rpc/src/v1/traits/raw.rs index 4a093c30..5dac5e26 100644 --- a/rpc/src/v1/traits/raw.rs +++ b/rpc/src/v1/traits/raw.rs @@ -5,7 +5,7 @@ use v1::types::H256; use v1::types::RawTransaction; use v1::types::Transaction; use v1::types::TransactionInput; -use v1::types::TransactionOutput; +use v1::types::TransactionOutputs; use v1::types::GetRawTransactionResponse; build_rpc_trait! { @@ -18,7 +18,7 @@ build_rpc_trait! { /// Create a transaction spending the given inputs and creating new outputs. /// @curl-example: curl --data-binary '{"jsonrpc": "2.0", "method": "createrawtransaction", "params": [[{"txid":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","vout":0}],{"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa":0.01}], "id":1 }' -H 'content-type: application/json;' http://127.0.0.1:8332/ #[rpc(name = "createrawtransaction")] - fn create_raw_transaction(&self, Vec, Vec, Trailing) -> Result; + fn create_raw_transaction(&self, Vec, TransactionOutputs, Trailing) -> Result; /// Return an object representing the serialized, hex-encoded transaction. /// @curl-example: curl --data-binary '{"jsonrpc": "2.0", "method": "decoderawtransaction", "params": ["01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"], "id":1 }' -H 'content-type: application/json;' http://127.0.0.1:8332/ #[rpc(name = "decoderawtransaction")] diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index 0e648e59..3012cd1e 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -24,6 +24,6 @@ pub use self::hash::{H160, H256}; pub use self::script::ScriptType; pub use self::transaction::{RawTransaction, Transaction, TransactionInput, TransactionOutput, TransactionInputScript, TransactionOutputScript, SignedTransactionInput, GetRawTransactionResponse, - SignedTransactionOutput}; + SignedTransactionOutput, TransactionOutputs}; pub use self::uint::U256; pub use self::nodes::AddNodeOperation;