From 9e380b8ccd12151961f0ee00eede9ccded565917 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Sun, 11 Dec 2016 00:30:19 +0300 Subject: [PATCH] continue work on gettxout --- rpc/src/v1/helpers/errors.rs | 27 +++++++++ rpc/src/v1/impls/blockchain.rs | 75 +++++++++++++++++++++++-- rpc/src/v1/types/get_tx_out_response.rs | 38 +++++++++---- rpc/src/v1/types/mod.rs.in | 2 +- 4 files changed, 125 insertions(+), 17 deletions(-) diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index 8d15d0af..cddd2f77 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -3,6 +3,9 @@ mod codes { // NOTE [ToDr] Codes from [-32099, -32000] pub const EXECUTION_ERROR: i64 = -32015; + pub const TRANSACTION_NOT_FOUND: i64 = -32096; + pub const TRANSACTION_OUTPUT_NOT_FOUND: i64 = -32097; + pub const TRANSACTION_OF_SIDE_BRANCH: i64 = -32098; pub const BLOCK_NOT_FOUND: i64 = -32099; } @@ -52,3 +55,27 @@ pub fn block_at_height_not_found(data: T) -> Error { data: Some(Value::String(format!("{:?}", data))), } } + +pub fn transaction_not_found(data: T) -> Error { + Error { + code: ErrorCode::ServerError(codes::TRANSACTION_NOT_FOUND), + message: "Transaction with given hash is not found".into(), + data: Some(Value::String(format!("{:?}", data))), + } +} + +pub fn transaction_output_not_found(data: T) -> Error { + Error { + code: ErrorCode::ServerError(codes::TRANSACTION_OUTPUT_NOT_FOUND), + message: "Transaction output is not found".into(), + data: Some(Value::String(format!("{:?}", data))), + } +} + +pub fn transaction_of_side_branch(data: T) -> Error { + Error { + code: ErrorCode::ServerError(codes::TRANSACTION_OF_SIDE_BRANCH), + message: "Transaction is of side branch".into(), + data: Some(Value::String(format!("{:?}", data))), + } +} diff --git a/rpc/src/v1/impls/blockchain.rs b/rpc/src/v1/impls/blockchain.rs index 785eb628..29349dbb 100644 --- a/rpc/src/v1/impls/blockchain.rs +++ b/rpc/src/v1/impls/blockchain.rs @@ -1,13 +1,17 @@ use v1::traits::BlockChain; use v1::types::{GetBlockResponse, VerboseBlock, RawBlock}; -use v1::types::GetTxOutResponse; +use v1::types::{GetTxOutResponse, TxOutScriptPubKey}; use v1::types::GetTxOutSetInfoResponse; use v1::types::H256; use v1::types::U256; -use v1::helpers::errors::{block_not_found, block_at_height_not_found}; +use v1::types::ScriptType; +use v1::helpers::errors::{block_not_found, block_at_height_not_found, transaction_not_found, + transaction_output_not_found, transaction_of_side_branch}; use jsonrpc_macros::Trailing; use jsonrpc_core::Error; use db; +use script::Script; +use chain::OutPoint; use verification; use ser::serialize; use primitives::hash::H256 as GlobalH256; @@ -23,6 +27,7 @@ pub trait BlockChainClientCoreApi: Send + Sync + 'static { fn difficulty(&self) -> f64; fn raw_block(&self, hash: GlobalH256) -> Option; fn verbose_block(&self, hash: GlobalH256) -> Option; + fn verbose_transaction_out(&self, prev_out: OutPoint) -> Result; } pub struct BlockChainClientCore { @@ -92,6 +97,55 @@ impl BlockChainClientCoreApi for BlockChainClientCore { } }) } + + fn verbose_transaction_out(&self, prev_out: OutPoint) -> Result { + let transaction = match self.storage.transaction(&prev_out.hash) { + Some(transaction) => transaction, + // no transaction => no response + None => return Err(transaction_not_found(prev_out.hash)), + }; + + if prev_out.index >= transaction.outputs.len() as u32 { + return Err(transaction_output_not_found(prev_out)); + } + + let meta = match self.storage.transaction_meta(&prev_out.hash) { + Some(meta) => meta, + // not in the main branch => no response + None => return Err(transaction_of_side_branch(prev_out.hash)), + }; + + let block_header = match self.storage.block_header(meta.height().into()) { + Some(block_header) => block_header, + // this is possible during reorgs + None => return Err(transaction_not_found(prev_out.hash)), + }; + + let best_block = self.storage.best_block().expect("storage with genesis block is required"); + if best_block.number < meta.height() { + // this is possible during reorgs + return Err(transaction_not_found(prev_out.hash)); + } + + let ref script_bytes = transaction.outputs[prev_out.index as usize].script_pubkey; + let script: Script = script_bytes.clone().into(); + let script_asm = format!("{}", script); + + Ok(GetTxOutResponse { + 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 { + asm: script_asm, + hex: script_bytes.clone().into(), + req_sigs: 0, // TODO + script_type: ScriptType::NonStandard, // TODO + addresses: vec![], + }, + version: transaction.version, + coinbase: transaction.is_coinbase(), + }) + } } impl BlockChainClient where T: BlockChainClientCoreApi { @@ -138,8 +192,12 @@ impl BlockChain for BlockChainClient where T: BlockChainClientCoreApi { .ok_or(block_not_found(hash)) } - fn transaction_out(&self, _transaction_hash: H256, _out_index: u32, _include_mempool: Trailing) -> Result { - rpc_unimplemented!() + fn transaction_out(&self, transaction_hash: H256, out_index: u32, _include_mempool: Trailing) -> Result { + self.core.verbose_transaction_out(OutPoint { hash: transaction_hash.into(), index: out_index }) + .map(|mut response| { + response.bestblock = response.bestblock.reversed(); + response + }) } fn transaction_out_set_info(&self) -> Result { @@ -157,6 +215,7 @@ pub mod tests { use primitives::hash::H256 as GlobalH256; use v1::types::{VerboseBlock, RawBlock}; use v1::traits::BlockChain; + use v1::helpers::errors::block_not_found; use test_data; use super::*; @@ -208,6 +267,10 @@ pub mod tests { nextblockhash: None, }) } + + fn verbose_transaction_out(&self, _prev_out: OutPoint) -> Result { + Ok(GetTxOutResponse::default()) // TODO: non-default + } } impl BlockChainClientCoreApi for ErrorBlockChainClientCore { @@ -230,6 +293,10 @@ pub mod tests { fn verbose_block(&self, _hash: GlobalH256) -> Option { None } + + fn verbose_transaction_out(&self, prev_out: OutPoint) -> Result { + Err(block_not_found(prev_out.hash)) + } } #[test] diff --git a/rpc/src/v1/types/get_tx_out_response.rs b/rpc/src/v1/types/get_tx_out_response.rs index 7fa884c5..9f204e87 100644 --- a/rpc/src/v1/types/get_tx_out_response.rs +++ b/rpc/src/v1/types/get_tx_out_response.rs @@ -3,36 +3,50 @@ use super::hash::{H160, H256}; use super::script::ScriptType; /// gettxout response -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Default, Serialize, Deserialize)] pub struct GetTxOutResponse { /// Hash of the block this transaction output is included into. - bestblock: H256, + /// Why it's called 'best'? Who knows + pub bestblock: H256, /// Number of confirmations of this transaction - confirmations: u32, + pub confirmations: u32, /// Transaction value in BTC - value: f64, + pub value: f64, /// Script info #[serde(rename = "scriptPubKey")] - script_pub_key: TxOutScriptPubKey, + pub script_pub_key: TxOutScriptPubKey, /// This transaction version - version: i32, + pub version: i32, /// Is this transactio a coinbase transaction? - coinbase: bool, + pub coinbase: bool, } /// Script pub key information #[derive(Debug, Serialize, Deserialize)] pub struct TxOutScriptPubKey { /// Script code - asm: String, + pub asm: String, /// Script hex - hex: Bytes, + pub hex: Bytes, /// Number of required signatures #[serde(rename = "reqSigs")] - req_sigs: u32, + pub req_sigs: u32, /// Type of script #[serde(rename = "type")] - script_type: ScriptType, + pub script_type: ScriptType, /// Array of bitcoin addresses - addresses: Vec, + pub addresses: Vec, +} + +// TODO: remove me +impl Default for TxOutScriptPubKey { + fn default() -> Self { + TxOutScriptPubKey { + asm: String::default(), + hex: Bytes::default(), + req_sigs: u32::default(), + script_type: ScriptType::NonStandard, + addresses: vec![], + } + } } diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index 29fc5c2b..0dffeec5 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -14,7 +14,7 @@ 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; +pub use self::get_tx_out_response::{GetTxOutResponse, TxOutScriptPubKey}; pub use self::get_tx_out_set_info_response::GetTxOutSetInfoResponse; pub use self::hash::H256; pub use self::raw_block::RawBlock;