continue work on gettxout

This commit is contained in:
Svyatoslav Nikolsky 2016-12-11 00:30:19 +03:00
parent 8185a2f7c7
commit 9e380b8ccd
4 changed files with 125 additions and 17 deletions

View File

@ -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<T: fmt::Debug>(data: T) -> Error {
data: Some(Value::String(format!("{:?}", data))),
}
}
pub fn transaction_not_found<T: fmt::Debug>(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<T: fmt::Debug>(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<T: fmt::Debug>(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))),
}
}

View File

@ -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<RawBlock>;
fn verbose_block(&self, hash: GlobalH256) -> Option<VerboseBlock>;
fn verbose_transaction_out(&self, prev_out: OutPoint) -> Result<GetTxOutResponse, Error>;
}
pub struct BlockChainClientCore {
@ -92,6 +97,55 @@ impl BlockChainClientCoreApi for BlockChainClientCore {
}
})
}
fn verbose_transaction_out(&self, prev_out: OutPoint) -> Result<GetTxOutResponse, Error> {
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<T> BlockChainClient<T> where T: BlockChainClientCoreApi {
@ -138,8 +192,12 @@ impl<T> BlockChain for BlockChainClient<T> where T: BlockChainClientCoreApi {
.ok_or(block_not_found(hash))
}
fn transaction_out(&self, _transaction_hash: H256, _out_index: u32, _include_mempool: Trailing<bool>) -> Result<GetTxOutResponse, Error> {
rpc_unimplemented!()
fn transaction_out(&self, transaction_hash: H256, out_index: u32, _include_mempool: Trailing<bool>) -> Result<GetTxOutResponse, Error> {
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<GetTxOutSetInfoResponse, Error> {
@ -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<GetTxOutResponse, Error> {
Ok(GetTxOutResponse::default()) // TODO: non-default
}
}
impl BlockChainClientCoreApi for ErrorBlockChainClientCore {
@ -230,6 +293,10 @@ pub mod tests {
fn verbose_block(&self, _hash: GlobalH256) -> Option<VerboseBlock> {
None
}
fn verbose_transaction_out(&self, prev_out: OutPoint) -> Result<GetTxOutResponse, Error> {
Err(block_not_found(prev_out.hash))
}
}
#[test]

View File

@ -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<H160>,
pub addresses: Vec<H160>,
}
// 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![],
}
}
}

View File

@ -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;