From 6304a40ab778ad45e7d88575853a8ec90643712d Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 9 Dec 2016 20:14:26 +0300 Subject: [PATCH 01/13] rpc BlockChain trait --- rpc/src/v1/traits/blockchain.rs | 35 +++++++++++++++++++ rpc/src/v1/traits/mod.rs | 2 ++ rpc/src/v1/types/get_block_response.rs | 3 ++ rpc/src/v1/types/get_transaction_response.rs | 3 ++ rpc/src/v1/types/get_tx_out_response.rs | 3 ++ .../v1/types/get_tx_out_set_info_response.rs | 3 ++ rpc/src/v1/types/mod.rs.in | 8 +++++ 7 files changed, 57 insertions(+) create mode 100644 rpc/src/v1/traits/blockchain.rs create mode 100644 rpc/src/v1/types/get_block_response.rs create mode 100644 rpc/src/v1/types/get_transaction_response.rs create mode 100644 rpc/src/v1/types/get_tx_out_response.rs create mode 100644 rpc/src/v1/types/get_tx_out_set_info_response.rs diff --git a/rpc/src/v1/traits/blockchain.rs b/rpc/src/v1/traits/blockchain.rs new file mode 100644 index 00000000..f6b0a45f --- /dev/null +++ b/rpc/src/v1/traits/blockchain.rs @@ -0,0 +1,35 @@ +use jsonrpc_core::Error; + +use v1::helpers::auto_args::Wrap; +use v1::types::H256; +use v1::types::GetBlockResponse; +use v1::types::GetTransactionResponse; +use v1::types::GetTxOutResponse; +use v1::types::GetTxOutSetInfoResponse; + +build_rpc_trait! { + /// Parity-bitcoin blockchain data interface. + pub trait BlockChain { + /// Get hash of best block. + #[rpc(name = "getbestblockhash")] + fn best_block_hash(&self) -> Result; + /// Get hash of block at given height. + #[rpc(name = "getblockhash")] + fn block_hash(&self, u32) -> Result; + /// Get proof-of-work difficulty as a multiple of the minimum difficulty + #[rpc(name = "getdifficulty")] + fn difficulty(&self) -> Result; + /// Get information on given block. + #[rpc(name = "getblock")] + fn block(&self, H256, Option) -> Result; + /// Get information on given transaction. + #[rpc(name = "gettransaction")] + fn transaction(&self, H256, Option) -> Result; + /// Get details about an unspent transaction output. + #[rpc(name = "gettxout")] + fn transaction_out(&self, H256, u32, Option) -> Result; + /// Get statistics about the unspent transaction output set. + #[rpc(name = "gettxoutsetinfo")] + fn transaction_out_set_info(&self) -> Result; + } +} diff --git a/rpc/src/v1/traits/mod.rs b/rpc/src/v1/traits/mod.rs index e1f17297..16bb2fc5 100644 --- a/rpc/src/v1/traits/mod.rs +++ b/rpc/src/v1/traits/mod.rs @@ -1,3 +1,5 @@ +mod blockchain; mod raw; +pub use self::blockchain::BlockChain; pub use self::raw::Raw; \ No newline at end of file diff --git a/rpc/src/v1/types/get_block_response.rs b/rpc/src/v1/types/get_block_response.rs new file mode 100644 index 00000000..e0cdc9d9 --- /dev/null +++ b/rpc/src/v1/types/get_block_response.rs @@ -0,0 +1,3 @@ +#[derive(Debug, Serialize, Deserialize)] +pub enum GetBlockResponse { +} \ No newline at end of file diff --git a/rpc/src/v1/types/get_transaction_response.rs b/rpc/src/v1/types/get_transaction_response.rs new file mode 100644 index 00000000..d2f511ff --- /dev/null +++ b/rpc/src/v1/types/get_transaction_response.rs @@ -0,0 +1,3 @@ +#[derive(Debug, Serialize, Deserialize)] +pub struct GetTransactionResponse { +} \ No newline at end of file diff --git a/rpc/src/v1/types/get_tx_out_response.rs b/rpc/src/v1/types/get_tx_out_response.rs new file mode 100644 index 00000000..e87edf9b --- /dev/null +++ b/rpc/src/v1/types/get_tx_out_response.rs @@ -0,0 +1,3 @@ +#[derive(Debug, Serialize, Deserialize)] +pub struct GetTxOutResponse { +} \ No newline at end of file diff --git a/rpc/src/v1/types/get_tx_out_set_info_response.rs b/rpc/src/v1/types/get_tx_out_set_info_response.rs new file mode 100644 index 00000000..a71ffc4f --- /dev/null +++ b/rpc/src/v1/types/get_tx_out_set_info_response.rs @@ -0,0 +1,3 @@ +#[derive(Debug, Serialize, Deserialize)] +pub struct GetTxOutSetInfoResponse { +} \ No newline at end of file diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index f0e0f6a1..5599425b 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -1,11 +1,19 @@ mod block_template; mod block_template_request; mod bytes; +mod get_block_response; +mod get_transaction_response; +mod get_tx_out_response; +mod get_tx_out_set_info_response; mod hash; mod raw_transaction; 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; +pub use self::get_transaction_response::GetTransactionResponse; +pub use self::get_tx_out_response::GetTxOutResponse; +pub use self::get_tx_out_set_info_response::GetTxOutSetInfoResponse; pub use self::hash::H256; pub use self::raw_transaction::RawTransaction; From e4fc56f119fffda4eef5676aa9f87ef03a7ae0ae Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 9 Dec 2016 21:21:05 +0300 Subject: [PATCH 02/13] GetBlockResponse struct --- rpc/src/v1/traits/blockchain.rs | 2 +- rpc/src/v1/types/get_block_response.rs | 59 +++++++++++++++++++ rpc/src/v1/types/get_transaction_response.rs | 2 +- rpc/src/v1/types/get_tx_out_response.rs | 2 +- .../v1/types/get_tx_out_set_info_response.rs | 2 +- rpc/src/v1/types/mod.rs.in | 2 + rpc/src/v1/types/raw_block.rs | 3 + 7 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 rpc/src/v1/types/raw_block.rs diff --git a/rpc/src/v1/traits/blockchain.rs b/rpc/src/v1/traits/blockchain.rs index f6b0a45f..34f74362 100644 --- a/rpc/src/v1/traits/blockchain.rs +++ b/rpc/src/v1/traits/blockchain.rs @@ -18,7 +18,7 @@ build_rpc_trait! { fn block_hash(&self, u32) -> Result; /// Get proof-of-work difficulty as a multiple of the minimum difficulty #[rpc(name = "getdifficulty")] - fn difficulty(&self) -> Result; + fn difficulty(&self) -> Result; /// Get information on given block. #[rpc(name = "getblock")] fn block(&self, H256, Option) -> Result; diff --git a/rpc/src/v1/types/get_block_response.rs b/rpc/src/v1/types/get_block_response.rs index e0cdc9d9..35ab58c0 100644 --- a/rpc/src/v1/types/get_block_response.rs +++ b/rpc/src/v1/types/get_block_response.rs @@ -1,3 +1,62 @@ +use super::bytes::Bytes; +use super::hash::H256; +use super::raw_block::RawBlock; + +/// Response to getblock RPC request #[derive(Debug, Serialize, Deserialize)] pub enum GetBlockResponse { + /// When asking for short response + Short(RawBlock), + /// When asking for verbose response + Verbose(Block), +} + +/// Verbose block information +#[derive(Debug, Serialize, Deserialize)] +pub struct Block { + /// Block hash + pub hash: H256, + /// Number of confirmations. -1 if block is on the side chain + pub confirmations: i64, + /// Block size + pub size: u32, + /// Block size, excluding witness data + pub strippedsize: u32, + /// Block weight + pub weight: u32, + /// Block height + pub height: u32, + /// Block version + pub version: u32, + /// Block version as hex + #[serde(rename = "versionHex")] + pub version_hex: Bytes, + /// Merkle root of this block + pub merkleroot: H256, + /// Transactions ids + pub tx: Vec, + /// Block time in seconds since epoch (Jan 1 1970 GMT) + pub time: u32, + /// Median block time in seconds since epoch (Jan 1 1970 GMT) + pub mediantime: u32, + /// Block nonce + pub nonce: u32, + /// Block nbits + pub bits: u32, + /// Block difficulty + pub difficulty: f64, + /// Expected number of hashes required to produce the chain up to this block (in hex) + pub chainwork: H256, + /// Hash of previous block + pub previousblockhash: Option, + /// Hash of next block + pub nextblockhash: Option, +} + +#[cfg(test)] +mod tests { + #[test] + fn block_serialize() { + + } } \ No newline at end of file diff --git a/rpc/src/v1/types/get_transaction_response.rs b/rpc/src/v1/types/get_transaction_response.rs index d2f511ff..3a926574 100644 --- a/rpc/src/v1/types/get_transaction_response.rs +++ b/rpc/src/v1/types/get_transaction_response.rs @@ -1,3 +1,3 @@ #[derive(Debug, Serialize, Deserialize)] pub struct GetTransactionResponse { -} \ No newline at end of file +} diff --git a/rpc/src/v1/types/get_tx_out_response.rs b/rpc/src/v1/types/get_tx_out_response.rs index e87edf9b..e7bfc620 100644 --- a/rpc/src/v1/types/get_tx_out_response.rs +++ b/rpc/src/v1/types/get_tx_out_response.rs @@ -1,3 +1,3 @@ #[derive(Debug, Serialize, Deserialize)] pub struct GetTxOutResponse { -} \ No newline at end of file +} diff --git a/rpc/src/v1/types/get_tx_out_set_info_response.rs b/rpc/src/v1/types/get_tx_out_set_info_response.rs index a71ffc4f..b7afb50d 100644 --- a/rpc/src/v1/types/get_tx_out_set_info_response.rs +++ b/rpc/src/v1/types/get_tx_out_set_info_response.rs @@ -1,3 +1,3 @@ #[derive(Debug, Serialize, Deserialize)] pub struct GetTxOutSetInfoResponse { -} \ No newline at end of file +} diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index 5599425b..d1b2ffe5 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -6,6 +6,7 @@ mod get_transaction_response; mod get_tx_out_response; mod get_tx_out_set_info_response; mod hash; +mod raw_block; mod raw_transaction; pub use self::block_template::{BlockTemplate, BlockTemplateTransaction}; @@ -16,4 +17,5 @@ pub use self::get_transaction_response::GetTransactionResponse; pub use self::get_tx_out_response::GetTxOutResponse; pub use self::get_tx_out_set_info_response::GetTxOutSetInfoResponse; pub use self::hash::H256; +pub use self::raw_block::RawBlock; pub use self::raw_transaction::RawTransaction; diff --git a/rpc/src/v1/types/raw_block.rs b/rpc/src/v1/types/raw_block.rs new file mode 100644 index 00000000..3ac34003 --- /dev/null +++ b/rpc/src/v1/types/raw_block.rs @@ -0,0 +1,3 @@ +use super::bytes::Bytes; + +pub type RawBlock = Bytes; From 1b9afd4e0125d0c67e4bb9eb8ee6a26ad223db4f Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 9 Dec 2016 22:18:44 +0300 Subject: [PATCH 03/13] test for RPC block serialize && deserialize --- rpc/src/v1/types/get_block_response.rs | 70 +++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/rpc/src/v1/types/get_block_response.rs b/rpc/src/v1/types/get_block_response.rs index 35ab58c0..d47f946d 100644 --- a/rpc/src/v1/types/get_block_response.rs +++ b/rpc/src/v1/types/get_block_response.rs @@ -12,7 +12,7 @@ pub enum GetBlockResponse { } /// Verbose block information -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Default, Serialize, Deserialize, PartialEq)] pub struct Block { /// Block hash pub hash: H256, @@ -55,8 +55,74 @@ pub struct Block { #[cfg(test)] mod tests { + use super::super::bytes::Bytes; + use super::super::hash::H256; + use serde_json; + use super::*; + #[test] fn block_serialize() { + let block = Block { + version_hex: Bytes::new(vec![0]), + ..Default::default() + }; + assert_eq!(serde_json::to_string(&block).unwrap(), r#"{"hash":"0000000000000000000000000000000000000000000000000000000000000000","confirmations":0,"size":0,"strippedsize":0,"weight":0,"height":0,"version":0,"versionHex":"00","merkleroot":"0000000000000000000000000000000000000000000000000000000000000000","tx":[],"time":0,"mediantime":0,"nonce":0,"bits":0,"difficulty":0.0,"chainwork":"0000000000000000000000000000000000000000000000000000000000000000","previousblockhash":null,"nextblockhash":null}"#); + let block = Block { + hash: H256::from(1), + confirmations: -1, + size: 500000, + strippedsize: 444444, + weight: 5236235, + height: 3513513, + version: 1, + version_hex: Bytes::new(vec![1]), + merkleroot: H256::from(2), + tx: vec![H256::from(3), H256::from(4)], + time: 111, + mediantime: 100, + nonce: 124, + bits: 13513, + difficulty: 555.555, + chainwork: H256::from(3), + previousblockhash: Some(H256::from(4)), + nextblockhash: Some(H256::from(5)), + }; + assert_eq!(serde_json::to_string(&block).unwrap(), r#"{"hash":"0100000000000000000000000000000000000000000000000000000000000000","confirmations":-1,"size":500000,"strippedsize":444444,"weight":5236235,"height":3513513,"version":1,"versionHex":"01","merkleroot":"0200000000000000000000000000000000000000000000000000000000000000","tx":["0300000000000000000000000000000000000000000000000000000000000000","0400000000000000000000000000000000000000000000000000000000000000"],"time":111,"mediantime":100,"nonce":124,"bits":13513,"difficulty":555.555,"chainwork":"0300000000000000000000000000000000000000000000000000000000000000","previousblockhash":"0400000000000000000000000000000000000000000000000000000000000000","nextblockhash":"0500000000000000000000000000000000000000000000000000000000000000"}"#); } -} \ No newline at end of file + + #[test] + fn block_deserialize() { + let block = Block { + version_hex: Bytes::new(vec![0]), + ..Default::default() + }; + assert_eq!( + serde_json::from_str::(r#"{"hash":"0000000000000000000000000000000000000000000000000000000000000000","confirmations":0,"size":0,"strippedsize":0,"weight":0,"height":0,"version":0,"versionHex":"00","merkleroot":"0000000000000000000000000000000000000000000000000000000000000000","tx":[],"time":0,"mediantime":0,"nonce":0,"bits":0,"difficulty":0.0,"chainwork":"0000000000000000000000000000000000000000000000000000000000000000","previousblockhash":null,"nextblockhash":null}"#).unwrap(), + block); + + let block = Block { + hash: H256::from(1), + confirmations: -1, + size: 500000, + strippedsize: 444444, + weight: 5236235, + height: 3513513, + version: 1, + version_hex: Bytes::new(vec![1]), + merkleroot: H256::from(2), + tx: vec![H256::from(3), H256::from(4)], + time: 111, + mediantime: 100, + nonce: 124, + bits: 13513, + difficulty: 555.555, + chainwork: H256::from(3), + previousblockhash: Some(H256::from(4)), + nextblockhash: Some(H256::from(5)), + }; + assert_eq!( + serde_json::from_str::(r#"{"hash":"0100000000000000000000000000000000000000000000000000000000000000","confirmations":-1,"size":500000,"strippedsize":444444,"weight":5236235,"height":3513513,"version":1,"versionHex":"01","merkleroot":"0200000000000000000000000000000000000000000000000000000000000000","tx":["0300000000000000000000000000000000000000000000000000000000000000","0400000000000000000000000000000000000000000000000000000000000000"],"time":111,"mediantime":100,"nonce":124,"bits":13513,"difficulty":555.555,"chainwork":"0300000000000000000000000000000000000000000000000000000000000000","previousblockhash":"0400000000000000000000000000000000000000000000000000000000000000","nextblockhash":"0500000000000000000000000000000000000000000000000000000000000000"}"#).unwrap(), + block); + } +} From 516151743c532f1aab6fef36a7cb2e106c9dcb58 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 9 Dec 2016 23:25:06 +0300 Subject: [PATCH 04/13] added tests for ser/deser GetBlockResponse --- rpc/src/v1/types/get_block_response.rs | 30 ++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/rpc/src/v1/types/get_block_response.rs b/rpc/src/v1/types/get_block_response.rs index d47f946d..dc7cae79 100644 --- a/rpc/src/v1/types/get_block_response.rs +++ b/rpc/src/v1/types/get_block_response.rs @@ -1,12 +1,13 @@ +use serde::{Serialize, Serializer}; use super::bytes::Bytes; use super::hash::H256; use super::raw_block::RawBlock; /// Response to getblock RPC request -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug)] pub enum GetBlockResponse { /// When asking for short response - Short(RawBlock), + Raw(RawBlock), /// When asking for verbose response Verbose(Block), } @@ -53,6 +54,15 @@ pub struct Block { pub nextblockhash: Option, } +impl Serialize for GetBlockResponse { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { + match *self { + GetBlockResponse::Raw(ref raw_block) => raw_block.serialize(serializer), + GetBlockResponse::Verbose(ref block) => block.serialize(serializer), + } + } +} + #[cfg(test)] mod tests { use super::super::bytes::Bytes; @@ -125,4 +135,20 @@ mod tests { serde_json::from_str::(r#"{"hash":"0100000000000000000000000000000000000000000000000000000000000000","confirmations":-1,"size":500000,"strippedsize":444444,"weight":5236235,"height":3513513,"version":1,"versionHex":"01","merkleroot":"0200000000000000000000000000000000000000000000000000000000000000","tx":["0300000000000000000000000000000000000000000000000000000000000000","0400000000000000000000000000000000000000000000000000000000000000"],"time":111,"mediantime":100,"nonce":124,"bits":13513,"difficulty":555.555,"chainwork":"0300000000000000000000000000000000000000000000000000000000000000","previousblockhash":"0400000000000000000000000000000000000000000000000000000000000000","nextblockhash":"0500000000000000000000000000000000000000000000000000000000000000"}"#).unwrap(), block); } + + #[test] + fn get_block_response_raw_serialize() { + let raw_response = GetBlockResponse::Raw(Bytes::new(vec![0])); + assert_eq!(serde_json::to_string(&raw_response).unwrap(), r#""00""#); + } + + #[test] + fn get_block_response_verbose_serialize() { + let block = Block { + version_hex: Bytes::new(vec![0]), + ..Default::default() + }; + let verbose_response = GetBlockResponse::Verbose(block); + assert_eq!(serde_json::to_string(&verbose_response).unwrap(), r#"{"hash":"0000000000000000000000000000000000000000000000000000000000000000","confirmations":0,"size":0,"strippedsize":0,"weight":0,"height":0,"version":0,"versionHex":"00","merkleroot":"0000000000000000000000000000000000000000000000000000000000000000","tx":[],"time":0,"mediantime":0,"nonce":0,"bits":0,"difficulty":0.0,"chainwork":"0000000000000000000000000000000000000000000000000000000000000000","previousblockhash":null,"nextblockhash":null}"#); + } } From 416ac097b54df40fe9f43093dd1db7fa8927112d Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Sat, 10 Dec 2016 13:50:07 +0300 Subject: [PATCH 05/13] started getblock iplementation --- Cargo.lock | 1 + primitives/src/bytes.rs | 4 + primitives/src/uint.rs | 1 - rpc/Cargo.toml | 1 + rpc/src/lib.rs | 1 + rpc/src/v1/impls/blockchain.rs | 119 +++++++++++++++++++++++++ rpc/src/v1/impls/mod.rs | 2 + rpc/src/v1/traits/blockchain.rs | 34 +++++++ rpc/src/v1/types/bytes.rs | 7 ++ rpc/src/v1/types/get_block_response.rs | 67 +++++++------- rpc/src/v1/types/mod.rs.in | 4 +- rpc/src/v1/types/uint.rs | 87 ++++++++++++++++++ sync/src/lib.rs | 2 +- verification/src/chain_verifier.rs | 4 +- 14 files changed, 292 insertions(+), 42 deletions(-) create mode 100644 rpc/src/v1/impls/blockchain.rs create mode 100644 rpc/src/v1/types/uint.rs diff --git a/Cargo.lock b/Cargo.lock index 3cdc1dcb..3b9bcf77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -804,6 +804,7 @@ dependencies = [ "sync 0.1.0", "test-data 0.1.0", "tokio-core 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "verification 0.1.0", ] [[package]] diff --git a/primitives/src/bytes.rs b/primitives/src/bytes.rs index 37958e75..cccfb837 100644 --- a/primitives/src/bytes.rs +++ b/primitives/src/bytes.rs @@ -12,6 +12,10 @@ impl Bytes { pub fn new_with_len(len: usize) -> Self { Bytes(vec![0; len]) } + + pub fn take(self) -> Vec { + self.0 + } } impl<'a> From<&'a [u8]> for Bytes { diff --git a/primitives/src/uint.rs b/primitives/src/uint.rs index 9ae77bc8..1e541629 100644 --- a/primitives/src/uint.rs +++ b/primitives/src/uint.rs @@ -884,7 +884,6 @@ macro_rules! construct_uint { impl fmt::LowerHex for $name { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let &$name(ref data) = self; - try!(write!(f, "0x")); let mut latch = false; for ch in data.iter().rev() { for x in 0..16 { diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 3d677e74..3176c3ba 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -24,6 +24,7 @@ network = { path = "../network" } db = { path = "../db" } test-data = { path = "../test-data" } miner = { path = "../miner" } +verification = { path = "../verification" } [build-dependencies] serde_codegen = { version = "0.8.0", optional = true } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index e061b965..b312a7ee 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -18,6 +18,7 @@ extern crate db; #[cfg(test)] extern crate test_data; extern crate miner; +extern crate verification; pub mod v1; pub mod rpc_server; diff --git a/rpc/src/v1/impls/blockchain.rs b/rpc/src/v1/impls/blockchain.rs new file mode 100644 index 00000000..c5aa2f70 --- /dev/null +++ b/rpc/src/v1/impls/blockchain.rs @@ -0,0 +1,119 @@ +use v1::traits::BlockChain; +use v1::types::{GetBlockResponse, VerboseBlock}; +use v1::types::GetTransactionResponse; +use v1::types::GetTxOutResponse; +use v1::types::GetTxOutSetInfoResponse; +use v1::types::RawBlock; +use v1::types::H256; +use v1::types::U256; +use jsonrpc_core::Error; +use db; +use verification; +use ser::serialize; +use primitives::hash::H256 as GlobalH256; + +pub struct BlockChainClient { + _core: T, +} + +pub trait BlockChainClientCoreApi: Send + Sync + 'static { + fn get_raw_block(&self, hash: GlobalH256) -> Option; + fn get_verbose_block(&self, hash: GlobalH256) -> Option; +} + +pub struct BlockChainClientCore { + storage: db::SharedStore, +} + +impl BlockChainClientCore { + pub fn new(storage: db::SharedStore) -> Self { + BlockChainClientCore { + storage: storage, + } + } +} + +impl BlockChainClientCoreApi for BlockChainClientCore { + fn get_raw_block(&self, hash: GlobalH256) -> Option { + self.storage.block(hash.into()) + .map(|block| { + serialize(&block).into() + }) + } + + fn get_verbose_block(&self, hash: GlobalH256) -> Option { + self.storage.block(hash.into()) + .map(|block| { + let block: db::IndexedBlock = block.into(); + let height = self.storage.block_number(block.hash()); + let confirmations = match height { + Some(block_number) => (self.storage.best_block().expect("genesis block is required").number - block_number + 1) as i64, + None => -1, + }; + let block_size = block.size(); + let median_time = verification::ChainVerifier::median_timestamp(self.storage.as_block_header_provider(), block.header()); + VerboseBlock { + confirmations: confirmations, + size: block_size as u32, + strippedsize: block_size as u32, // TODO: segwit + weight: block_size as u32, // TODO: segwit + height: height, + mediantime: median_time, + difficulty: 0f64, // TODO: https://en.bitcoin.it/wiki/Difficulty + https://www.bitcoinmining.com/what-is-bitcoin-mining-difficulty/ + chainwork: U256::default(), // TODO: read from storage + previousblockhash: Some(block.header().previous_header_hash.clone().into()), + nextblockhash: height.and_then(|h| self.storage.block_hash(h + 1).map(|h| h.into())), + bits: block.header().bits.into(), + hash: block.hash().clone().reversed().into(), + merkleroot: block.header().merkle_root_hash.clone().into(), + nonce: block.header().nonce, + time: block.header().time, + tx: vec![], // TODO + version: block.header().version, + version_hex: format!("{:x}", block.header().version), + } + }) + } +} + +impl BlockChainClient where T: BlockChainClientCoreApi { + pub fn new(core: T) -> Self { + BlockChainClient { + _core: core, + } + } +} + +impl BlockChain for BlockChainClient where T: BlockChainClientCoreApi { + fn best_block_hash(&self) -> Result { + rpc_unimplemented!() + } + + fn block_hash(&self, _height: u32) -> Result { + rpc_unimplemented!() + } + + fn difficulty(&self) -> Result { + rpc_unimplemented!() + } + + fn block(&self, _hash: H256, _verbose: Option) -> Result { + rpc_unimplemented!() + } + + fn transaction(&self, _hash: H256, _watch_only: Option) -> Result { + rpc_unimplemented!() + } + + fn transaction_out(&self, _transaction_hash: H256, _out_index: u32, _include_mempool: Option) -> Result { + rpc_unimplemented!() + } + + fn transaction_out_set_info(&self) -> Result { + rpc_unimplemented!() + } +} + +#[cfg(test)] +pub mod tests { +} diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index d079c545..724d4722 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -1,5 +1,7 @@ +mod blockchain; mod miner; mod raw; +pub use self::blockchain::{BlockChainClient, BlockChainClientCore}; pub use self::miner::{MinerClient, MinerClientCore}; pub use self::raw::{RawClient, RawClientCore}; diff --git a/rpc/src/v1/traits/blockchain.rs b/rpc/src/v1/traits/blockchain.rs index 34f74362..50d06d6d 100644 --- a/rpc/src/v1/traits/blockchain.rs +++ b/rpc/src/v1/traits/blockchain.rs @@ -33,3 +33,37 @@ build_rpc_trait! { fn transaction_out_set_info(&self) -> Result; } } +/* +use db::IndexedBlock; +use primitives::hash::H256; +use primitives::uint::U256; + +/// Verbose block information +#[derive(Debug)] +pub struct VerboseBlock { + /// Block + pub block: IndexedBlock, + /// Number of confirmations. -1 if block is on the side chain + pub confirmations: i64, + /// Block size + pub size: u32, + /// Block size, excluding witness data + pub stripped_size: u32, + /// Block weight + pub weight: u32, + /// Block height. We only provide this for main chain blocks + pub height: Option, + /// Median block time in seconds since epoch (Jan 1 1970 GMT) + /// We only provide this when there are > 2 parent blocks + pub median_time: Option, + /// Block difficulty + pub difficulty: f64, + /// Expected number of hashes required to produce the chain up to this block + pub chain_work: Option, + /// Hash of previous block + pub previous_block_hash: Option, + /// Hash of next block + pub next_block_hash: Option, +} + +*/ \ No newline at end of file diff --git a/rpc/src/v1/types/bytes.rs b/rpc/src/v1/types/bytes.rs index 2cb845ed..b7bd65d8 100644 --- a/rpc/src/v1/types/bytes.rs +++ b/rpc/src/v1/types/bytes.rs @@ -2,6 +2,7 @@ use rustc_serialize::hex::{ToHex, FromHex}; use serde::{Serialize, Serializer, Deserialize, Deserializer, Error}; use serde::de::Visitor; +use primitives::bytes::Bytes as GlobalBytes; /// Wrapper structure around vector of bytes. #[derive(Debug, PartialEq, Eq, Default, Hash, Clone)] @@ -19,6 +20,12 @@ impl Bytes { } } +impl From for Bytes { + fn from(v: GlobalBytes) -> Self { + Bytes(v.take()) + } +} + impl From> for Bytes { fn from(bytes: Vec) -> Bytes { Bytes(bytes) diff --git a/rpc/src/v1/types/get_block_response.rs b/rpc/src/v1/types/get_block_response.rs index dc7cae79..6c68935c 100644 --- a/rpc/src/v1/types/get_block_response.rs +++ b/rpc/src/v1/types/get_block_response.rs @@ -1,6 +1,6 @@ use serde::{Serialize, Serializer}; -use super::bytes::Bytes; use super::hash::H256; +use super::uint::U256; use super::raw_block::RawBlock; /// Response to getblock RPC request @@ -9,12 +9,12 @@ pub enum GetBlockResponse { /// When asking for short response Raw(RawBlock), /// When asking for verbose response - Verbose(Block), + Verbose(VerboseBlock), } /// Verbose block information #[derive(Debug, Default, Serialize, Deserialize, PartialEq)] -pub struct Block { +pub struct VerboseBlock { /// Block hash pub hash: H256, /// Number of confirmations. -1 if block is on the side chain @@ -26,12 +26,13 @@ pub struct Block { /// Block weight pub weight: u32, /// Block height - pub height: u32, + /// TODO: bitcoind always returns value, but we hold this value for main chain blocks only + pub height: Option, /// Block version pub version: u32, /// Block version as hex #[serde(rename = "versionHex")] - pub version_hex: Bytes, + pub version_hex: String, /// Merkle root of this block pub merkleroot: H256, /// Transactions ids @@ -39,7 +40,8 @@ pub struct Block { /// Block time in seconds since epoch (Jan 1 1970 GMT) pub time: u32, /// Median block time in seconds since epoch (Jan 1 1970 GMT) - pub mediantime: u32, + /// TODO: bitcoind always returns value, but we can calculate this only if height(block) > 2 + pub mediantime: Option, /// Block nonce pub nonce: u32, /// Block nbits @@ -47,7 +49,7 @@ pub struct Block { /// Block difficulty pub difficulty: f64, /// Expected number of hashes required to produce the chain up to this block (in hex) - pub chainwork: H256, + pub chainwork: U256, /// Hash of previous block pub previousblockhash: Option, /// Hash of next block @@ -58,7 +60,7 @@ impl Serialize for GetBlockResponse { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { match *self { GetBlockResponse::Raw(ref raw_block) => raw_block.serialize(serializer), - GetBlockResponse::Verbose(ref block) => block.serialize(serializer), + GetBlockResponse::Verbose(ref verbose_block) => verbose_block.serialize(serializer), } } } @@ -71,68 +73,62 @@ mod tests { use super::*; #[test] - fn block_serialize() { - let block = Block { - version_hex: Bytes::new(vec![0]), - ..Default::default() - }; - assert_eq!(serde_json::to_string(&block).unwrap(), r#"{"hash":"0000000000000000000000000000000000000000000000000000000000000000","confirmations":0,"size":0,"strippedsize":0,"weight":0,"height":0,"version":0,"versionHex":"00","merkleroot":"0000000000000000000000000000000000000000000000000000000000000000","tx":[],"time":0,"mediantime":0,"nonce":0,"bits":0,"difficulty":0.0,"chainwork":"0000000000000000000000000000000000000000000000000000000000000000","previousblockhash":null,"nextblockhash":null}"#); + fn verbose_block_serialize() { + let block = VerboseBlock::default(); + assert_eq!(serde_json::to_string(&block).unwrap(), r#"{"hash":"0000000000000000000000000000000000000000000000000000000000000000","confirmations":0,"size":0,"strippedsize":0,"weight":0,"height":null,"version":0,"versionHex":"","merkleroot":"0000000000000000000000000000000000000000000000000000000000000000","tx":[],"time":0,"mediantime":null,"nonce":0,"bits":0,"difficulty":0.0,"chainwork":"0","previousblockhash":null,"nextblockhash":null}"#); - let block = Block { + let block = VerboseBlock { hash: H256::from(1), confirmations: -1, size: 500000, strippedsize: 444444, weight: 5236235, - height: 3513513, + height: Some(3513513), version: 1, - version_hex: Bytes::new(vec![1]), + version_hex: "01".to_owned(), merkleroot: H256::from(2), tx: vec![H256::from(3), H256::from(4)], time: 111, - mediantime: 100, + mediantime: Some(100), nonce: 124, bits: 13513, difficulty: 555.555, - chainwork: H256::from(3), + chainwork: U256::from(3), previousblockhash: Some(H256::from(4)), nextblockhash: Some(H256::from(5)), }; - assert_eq!(serde_json::to_string(&block).unwrap(), r#"{"hash":"0100000000000000000000000000000000000000000000000000000000000000","confirmations":-1,"size":500000,"strippedsize":444444,"weight":5236235,"height":3513513,"version":1,"versionHex":"01","merkleroot":"0200000000000000000000000000000000000000000000000000000000000000","tx":["0300000000000000000000000000000000000000000000000000000000000000","0400000000000000000000000000000000000000000000000000000000000000"],"time":111,"mediantime":100,"nonce":124,"bits":13513,"difficulty":555.555,"chainwork":"0300000000000000000000000000000000000000000000000000000000000000","previousblockhash":"0400000000000000000000000000000000000000000000000000000000000000","nextblockhash":"0500000000000000000000000000000000000000000000000000000000000000"}"#); + assert_eq!(serde_json::to_string(&block).unwrap(), r#"{"hash":"0100000000000000000000000000000000000000000000000000000000000000","confirmations":-1,"size":500000,"strippedsize":444444,"weight":5236235,"height":3513513,"version":1,"versionHex":"01","merkleroot":"0200000000000000000000000000000000000000000000000000000000000000","tx":["0300000000000000000000000000000000000000000000000000000000000000","0400000000000000000000000000000000000000000000000000000000000000"],"time":111,"mediantime":100,"nonce":124,"bits":13513,"difficulty":555.555,"chainwork":"3","previousblockhash":"0400000000000000000000000000000000000000000000000000000000000000","nextblockhash":"0500000000000000000000000000000000000000000000000000000000000000"}"#); } #[test] - fn block_deserialize() { - let block = Block { - version_hex: Bytes::new(vec![0]), - ..Default::default() - }; + fn verbose_block_deserialize() { + let block = VerboseBlock::default(); assert_eq!( - serde_json::from_str::(r#"{"hash":"0000000000000000000000000000000000000000000000000000000000000000","confirmations":0,"size":0,"strippedsize":0,"weight":0,"height":0,"version":0,"versionHex":"00","merkleroot":"0000000000000000000000000000000000000000000000000000000000000000","tx":[],"time":0,"mediantime":0,"nonce":0,"bits":0,"difficulty":0.0,"chainwork":"0000000000000000000000000000000000000000000000000000000000000000","previousblockhash":null,"nextblockhash":null}"#).unwrap(), + serde_json::from_str::(r#"{"hash":"0000000000000000000000000000000000000000000000000000000000000000","confirmations":0,"size":0,"strippedsize":0,"weight":0,"height":null,"version":0,"versionHex":"","merkleroot":"0000000000000000000000000000000000000000000000000000000000000000","tx":[],"time":0,"mediantime":null,"nonce":0,"bits":0,"difficulty":0.0,"chainwork":"0","previousblockhash":null,"nextblockhash":null}"#).unwrap(), block); - let block = Block { + let block = VerboseBlock { hash: H256::from(1), confirmations: -1, size: 500000, strippedsize: 444444, weight: 5236235, - height: 3513513, + height: Some(3513513), version: 1, - version_hex: Bytes::new(vec![1]), + version_hex: "01".to_owned(), merkleroot: H256::from(2), tx: vec![H256::from(3), H256::from(4)], time: 111, - mediantime: 100, + mediantime: Some(100), nonce: 124, bits: 13513, difficulty: 555.555, - chainwork: H256::from(3), + chainwork: U256::from(3), previousblockhash: Some(H256::from(4)), nextblockhash: Some(H256::from(5)), }; assert_eq!( - serde_json::from_str::(r#"{"hash":"0100000000000000000000000000000000000000000000000000000000000000","confirmations":-1,"size":500000,"strippedsize":444444,"weight":5236235,"height":3513513,"version":1,"versionHex":"01","merkleroot":"0200000000000000000000000000000000000000000000000000000000000000","tx":["0300000000000000000000000000000000000000000000000000000000000000","0400000000000000000000000000000000000000000000000000000000000000"],"time":111,"mediantime":100,"nonce":124,"bits":13513,"difficulty":555.555,"chainwork":"0300000000000000000000000000000000000000000000000000000000000000","previousblockhash":"0400000000000000000000000000000000000000000000000000000000000000","nextblockhash":"0500000000000000000000000000000000000000000000000000000000000000"}"#).unwrap(), + serde_json::from_str::(r#"{"hash":"0100000000000000000000000000000000000000000000000000000000000000","confirmations":-1,"size":500000,"strippedsize":444444,"weight":5236235,"height":3513513,"version":1,"versionHex":"01","merkleroot":"0200000000000000000000000000000000000000000000000000000000000000","tx":["0300000000000000000000000000000000000000000000000000000000000000","0400000000000000000000000000000000000000000000000000000000000000"],"time":111,"mediantime":100,"nonce":124,"bits":13513,"difficulty":555.555,"chainwork":"3","previousblockhash":"0400000000000000000000000000000000000000000000000000000000000000","nextblockhash":"0500000000000000000000000000000000000000000000000000000000000000"}"#).unwrap(), block); } @@ -144,11 +140,8 @@ mod tests { #[test] fn get_block_response_verbose_serialize() { - let block = Block { - version_hex: Bytes::new(vec![0]), - ..Default::default() - }; + let block = VerboseBlock::default(); let verbose_response = GetBlockResponse::Verbose(block); - assert_eq!(serde_json::to_string(&verbose_response).unwrap(), r#"{"hash":"0000000000000000000000000000000000000000000000000000000000000000","confirmations":0,"size":0,"strippedsize":0,"weight":0,"height":0,"version":0,"versionHex":"00","merkleroot":"0000000000000000000000000000000000000000000000000000000000000000","tx":[],"time":0,"mediantime":0,"nonce":0,"bits":0,"difficulty":0.0,"chainwork":"0000000000000000000000000000000000000000000000000000000000000000","previousblockhash":null,"nextblockhash":null}"#); + assert_eq!(serde_json::to_string(&verbose_response).unwrap(), r#"{"hash":"0000000000000000000000000000000000000000000000000000000000000000","confirmations":0,"size":0,"strippedsize":0,"weight":0,"height":null,"version":0,"versionHex":"","merkleroot":"0000000000000000000000000000000000000000000000000000000000000000","tx":[],"time":0,"mediantime":null,"nonce":0,"bits":0,"difficulty":0.0,"chainwork":"0","previousblockhash":null,"nextblockhash":null}"#); } } diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index d1b2ffe5..dbd4f77e 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -8,14 +8,16 @@ mod get_tx_out_set_info_response; mod hash; mod raw_block; mod raw_transaction; +mod uint; 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; +pub use self::get_block_response::{GetBlockResponse, VerboseBlock}; pub use self::get_transaction_response::GetTransactionResponse; pub use self::get_tx_out_response::GetTxOutResponse; pub use self::get_tx_out_set_info_response::GetTxOutSetInfoResponse; pub use self::hash::H256; pub use self::raw_block::RawBlock; pub use self::raw_transaction::RawTransaction; +pub use self::uint::U256; diff --git a/rpc/src/v1/types/uint.rs b/rpc/src/v1/types/uint.rs new file mode 100644 index 00000000..9b38d0c9 --- /dev/null +++ b/rpc/src/v1/types/uint.rs @@ -0,0 +1,87 @@ +use std::str::FromStr; +use serde; +use primitives::uint::U256 as GlobalU256; + +macro_rules! impl_uint { + ($name: ident, $other: ident, $size: expr) => { + /// Uint serialization. + #[derive(Debug, Default, Clone, Copy, PartialEq, Hash)] + pub struct $name($other); + + impl Eq for $name { } + + impl From for $name where $other: From { + fn from(o: T) -> Self { + $name($other::from(o)) + } + } + + impl FromStr for $name { + type Err = <$other as FromStr>::Err; + + fn from_str(s: &str) -> Result { + $other::from_str(s).map($name) + } + } + + impl Into<$other> for $name { + fn into(self) -> $other { + self.0 + } + } + + impl serde::Serialize for $name { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer { + let as_hex = format!("{:x}", self.0); + serializer.serialize_str(&as_hex) + } + } + + impl serde::Deserialize for $name { + fn deserialize(deserializer: &mut D) -> Result<$name, D::Error> where D: serde::Deserializer { + struct UintVisitor; + + impl serde::de::Visitor for UintVisitor { + type Value = $name; + + fn visit_str(&mut self, value: &str) -> Result where E: serde::Error { + if value.len() > $size * 16 { + return Err(serde::Error::custom("Invalid length.")); + } + + $other::from_str(value).map($name).map_err(|_| serde::Error::custom("Invalid hex value.")) + } + + fn visit_string(&mut self, value: String) -> Result where E: serde::Error { + self.visit_str(&value) + } + } + + deserializer.deserialize(UintVisitor) + } + } + } +} + +impl_uint!(U256, GlobalU256, 4); + + +#[cfg(test)] +mod tests { + use super::U256; + use serde_json; + + #[test] + fn u256_serialize() { + let u256 = U256::from(256); + let serialized = serde_json::to_string(&u256).unwrap(); + assert_eq!(serialized, r#""100""#); + } + + #[test] + fn u256_deserialize() { + let u256 = U256::from(256); + let deserialized = serde_json::from_str::(r#""100""#).unwrap(); + assert_eq!(deserialized, u256); + } +} diff --git a/sync/src/lib.rs b/sync/src/lib.rs index edc9dd2d..0e912be6 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -85,7 +85,7 @@ pub fn create_local_sync_node(handle: &Handle, network: Magic, db: db::SharedSto }; let sync_chain = Arc::new(RwLock::new(SyncChain::new(db.clone()))); - let chain_verifier = Arc::new(ChainVerifier::new(db, network)); + let chain_verifier = Arc::new(ChainVerifier::new(db.clone(), network)); let sync_executor = SyncExecutor::new(sync_chain.clone()); let sync_server = Arc::new(SynchronizationServer::new(sync_chain.clone(), sync_executor.clone())); let sync_client_core = SynchronizationClientCore::new(sync_client_config, handle, sync_executor.clone(), sync_chain.clone(), chain_verifier.clone()); diff --git a/verification/src/chain_verifier.rs b/verification/src/chain_verifier.rs index 27597119..bbc04314 100644 --- a/verification/src/chain_verifier.rs +++ b/verification/src/chain_verifier.rs @@ -244,7 +244,7 @@ impl ChainVerifier { return Err(Error::FuturisticTimestamp); } - if let Some(median_timestamp) = self.median_timestamp(block_header_provider, header) { + if let Some(median_timestamp) = ChainVerifier::median_timestamp(block_header_provider, header) { // TODO: make timestamp validation on testnet work... if self.network != Magic::Testnet && median_timestamp >= header.time { trace!( @@ -345,7 +345,7 @@ impl ChainVerifier { } } - fn median_timestamp(&self, block_header_provider: &BlockHeaderProvider, header: &chain::BlockHeader) -> Option { + pub fn median_timestamp(block_header_provider: &BlockHeaderProvider, header: &chain::BlockHeader) -> Option { let mut timestamps = BTreeSet::new(); let mut block_ref = header.previous_header_hash.clone().into(); // TODO: optimize it, so it does not make 11 redundant queries each time From 8cd9b2dbdc273a845c128940fd90a18272912fc9 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Sat, 10 Dec 2016 14:38:51 +0300 Subject: [PATCH 06/13] fixed tests --- rpc/src/v1/helpers/errors.rs | 9 +++++++++ rpc/src/v1/impls/blockchain.rs | 17 +++++++++++++---- rpc/src/v1/types/get_block_response.rs | 4 ++-- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index 90ab238f..42603189 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -3,6 +3,7 @@ mod codes { // NOTE [ToDr] Codes from [-32099, -32000] pub const EXECUTION_ERROR: i64 = -32015; + pub const BLOCK_NOT_FOUND: i64 = -32099; } @@ -36,3 +37,11 @@ pub fn execution(data: T) -> Error { data: Some(Value::String(format!("{:?}", data))), } } + +pub fn block_not_found(data: T) -> Error { + Error { + code: ErrorCode::ServerError(codes::BLOCK_NOT_FOUND), + message: "Block not found".into(), + data: Some(Value::String(format!("{:?}", data))), + } +} diff --git a/rpc/src/v1/impls/blockchain.rs b/rpc/src/v1/impls/blockchain.rs index c5aa2f70..2af30e23 100644 --- a/rpc/src/v1/impls/blockchain.rs +++ b/rpc/src/v1/impls/blockchain.rs @@ -6,6 +6,7 @@ use v1::types::GetTxOutSetInfoResponse; use v1::types::RawBlock; use v1::types::H256; use v1::types::U256; +use v1::helpers::errors::block_not_found; use jsonrpc_core::Error; use db; use verification; @@ -13,7 +14,7 @@ use ser::serialize; use primitives::hash::H256 as GlobalH256; pub struct BlockChainClient { - _core: T, + core: T, } pub trait BlockChainClientCoreApi: Send + Sync + 'static { @@ -79,7 +80,7 @@ impl BlockChainClientCoreApi for BlockChainClientCore { impl BlockChainClient where T: BlockChainClientCoreApi { pub fn new(core: T) -> Self { BlockChainClient { - _core: core, + core: core, } } } @@ -97,8 +98,16 @@ impl BlockChain for BlockChainClient where T: BlockChainClientCoreApi { rpc_unimplemented!() } - fn block(&self, _hash: H256, _verbose: Option) -> Result { - rpc_unimplemented!() + fn block(&self, hash: H256, verbose: Option) -> Result { + let global_hash: GlobalH256 = hash.clone().into(); + if verbose.unwrap_or_default() { + self.core.get_verbose_block(global_hash.reversed()) + .map(|block| GetBlockResponse::Verbose(block)) + } else { + self.core.get_raw_block(global_hash.reversed()) + .map(|block| GetBlockResponse::Raw(block)) + } + .ok_or(block_not_found(hash)) } fn transaction(&self, _hash: H256, _watch_only: Option) -> Result { diff --git a/rpc/src/v1/types/get_block_response.rs b/rpc/src/v1/types/get_block_response.rs index 6c68935c..c48b9c98 100644 --- a/rpc/src/v1/types/get_block_response.rs +++ b/rpc/src/v1/types/get_block_response.rs @@ -75,7 +75,7 @@ mod tests { #[test] fn verbose_block_serialize() { let block = VerboseBlock::default(); - assert_eq!(serde_json::to_string(&block).unwrap(), r#"{"hash":"0000000000000000000000000000000000000000000000000000000000000000","confirmations":0,"size":0,"strippedsize":0,"weight":0,"height":null,"version":0,"versionHex":"","merkleroot":"0000000000000000000000000000000000000000000000000000000000000000","tx":[],"time":0,"mediantime":null,"nonce":0,"bits":0,"difficulty":0.0,"chainwork":"0","previousblockhash":null,"nextblockhash":null}"#); + assert_eq!(serde_json::to_string(&block).unwrap(), r#"{"hash":"0000000000000000000000000000000000000000000000000000000000000000","confirmations":0,"size":0,"strippedsize":0,"weight":0,"height":null,"version":0,"versionHex":"","merkleroot":"0000000000000000000000000000000000000000000000000000000000000000","tx":[],"time":0,"mediantime":null,"nonce":0,"bits":0,"difficulty":0.0,"chainwork":"","previousblockhash":null,"nextblockhash":null}"#); let block = VerboseBlock { hash: H256::from(1), @@ -142,6 +142,6 @@ mod tests { fn get_block_response_verbose_serialize() { let block = VerboseBlock::default(); let verbose_response = GetBlockResponse::Verbose(block); - assert_eq!(serde_json::to_string(&verbose_response).unwrap(), r#"{"hash":"0000000000000000000000000000000000000000000000000000000000000000","confirmations":0,"size":0,"strippedsize":0,"weight":0,"height":null,"version":0,"versionHex":"","merkleroot":"0000000000000000000000000000000000000000000000000000000000000000","tx":[],"time":0,"mediantime":null,"nonce":0,"bits":0,"difficulty":0.0,"chainwork":"0","previousblockhash":null,"nextblockhash":null}"#); + assert_eq!(serde_json::to_string(&verbose_response).unwrap(), r#"{"hash":"0000000000000000000000000000000000000000000000000000000000000000","confirmations":0,"size":0,"strippedsize":0,"weight":0,"height":null,"version":0,"versionHex":"","merkleroot":"0000000000000000000000000000000000000000000000000000000000000000","tx":[],"time":0,"mediantime":null,"nonce":0,"bits":0,"difficulty":0.0,"chainwork":"","previousblockhash":null,"nextblockhash":null}"#); } } From 03413b1501f511a7d56dff6b466a093e88e80041 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Sat, 10 Dec 2016 15:50:44 +0300 Subject: [PATCH 07/13] continue with blockchain RPCs --- rpc/src/v1/helpers/errors.rs | 10 ++- rpc/src/v1/impls/blockchain.rs | 142 ++++++++++++++++++++++++++++++--- 2 files changed, 138 insertions(+), 14 deletions(-) diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index 42603189..3c510a49 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -41,7 +41,15 @@ pub fn execution(data: T) -> Error { pub fn block_not_found(data: T) -> Error { Error { code: ErrorCode::ServerError(codes::BLOCK_NOT_FOUND), - message: "Block not found".into(), + message: "Block with given hash is not found".into(), + data: Some(Value::String(format!("{:?}", data))), + } +} + +pub fn block_at_height_not_found(data: T) -> Error { + Error { + code: ErrorCode::ServerError(codes::BLOCK_NOT_FOUND), + message: "Block at given height is not found".into(), data: Some(Value::String(format!("{:?}", data))), } } diff --git a/rpc/src/v1/impls/blockchain.rs b/rpc/src/v1/impls/blockchain.rs index 2af30e23..745f0672 100644 --- a/rpc/src/v1/impls/blockchain.rs +++ b/rpc/src/v1/impls/blockchain.rs @@ -1,12 +1,11 @@ use v1::traits::BlockChain; -use v1::types::{GetBlockResponse, VerboseBlock}; +use v1::types::{GetBlockResponse, VerboseBlock, RawBlock}; use v1::types::GetTransactionResponse; use v1::types::GetTxOutResponse; use v1::types::GetTxOutSetInfoResponse; -use v1::types::RawBlock; use v1::types::H256; use v1::types::U256; -use v1::helpers::errors::block_not_found; +use v1::helpers::errors::{block_not_found, block_at_height_not_found}; use jsonrpc_core::Error; use db; use verification; @@ -18,8 +17,11 @@ pub struct BlockChainClient { } pub trait BlockChainClientCoreApi: Send + Sync + 'static { - fn get_raw_block(&self, hash: GlobalH256) -> Option; - fn get_verbose_block(&self, hash: GlobalH256) -> Option; + fn best_block_hash(&self) -> GlobalH256; + fn block_hash(&self, height: u32) -> Option; + fn difficulty(&self) -> f64; + fn raw_block(&self, hash: GlobalH256) -> Option; + fn verbose_block(&self, hash: GlobalH256) -> Option; } pub struct BlockChainClientCore { @@ -28,6 +30,8 @@ pub struct BlockChainClientCore { impl BlockChainClientCore { pub fn new(storage: db::SharedStore) -> Self { + assert!(storage.best_block().is_some()); + BlockChainClientCore { storage: storage, } @@ -35,14 +39,26 @@ impl BlockChainClientCore { } impl BlockChainClientCoreApi for BlockChainClientCore { - fn get_raw_block(&self, hash: GlobalH256) -> Option { + fn best_block_hash(&self) -> GlobalH256 { + self.storage.best_block().expect("storage with genesis block required").hash + } + + fn block_hash(&self, height: u32) -> Option { + self.storage.block_hash(height) + } + + fn difficulty(&self) -> f64 { + unimplemented!() + } + + fn raw_block(&self, hash: GlobalH256) -> Option { self.storage.block(hash.into()) .map(|block| { serialize(&block).into() }) } - fn get_verbose_block(&self, hash: GlobalH256) -> Option { + fn verbose_block(&self, hash: GlobalH256) -> Option { self.storage.block(hash.into()) .map(|block| { let block: db::IndexedBlock = block.into(); @@ -87,24 +103,26 @@ impl BlockChainClient where T: BlockChainClientCoreApi { impl BlockChain for BlockChainClient where T: BlockChainClientCoreApi { fn best_block_hash(&self) -> Result { - rpc_unimplemented!() + Ok(self.core.best_block_hash().into()) } - fn block_hash(&self, _height: u32) -> Result { - rpc_unimplemented!() + fn block_hash(&self, height: u32) -> Result { + self.core.block_hash(height) + .map(H256::from) + .ok_or(block_at_height_not_found(height)) } fn difficulty(&self) -> Result { - rpc_unimplemented!() + Ok(self.core.difficulty()) } fn block(&self, hash: H256, verbose: Option) -> Result { let global_hash: GlobalH256 = hash.clone().into(); if verbose.unwrap_or_default() { - self.core.get_verbose_block(global_hash.reversed()) + self.core.verbose_block(global_hash.reversed()) .map(|block| GetBlockResponse::Verbose(block)) } else { - self.core.get_raw_block(global_hash.reversed()) + self.core.raw_block(global_hash.reversed()) .map(|block| GetBlockResponse::Raw(block)) } .ok_or(block_not_found(hash)) @@ -125,4 +143,102 @@ impl BlockChain for BlockChainClient where T: BlockChainClientCoreApi { #[cfg(test)] pub mod tests { + use primitives::hash::H256 as GlobalH256; + use v1::types::{VerboseBlock, RawBlock}; + use test_data; + use super::*; + + #[derive(Default)] + struct SuccessBlockChainClientCore; + #[derive(Default)] + struct ErrorBlockChainClientCore; + + impl BlockChainClientCoreApi for SuccessBlockChainClientCore { + fn best_block_hash(&self) -> GlobalH256 { + test_data::genesis().hash() + } + + fn block_hash(&self, _height: u32) -> Option { + Some(test_data::genesis().hash()) + } + + fn difficulty(&self) -> f64 { + 1f64 + } + + fn raw_block(&self, _hash: GlobalH256) -> Option { + Some(RawBlock::from(vec![0])) + } + + fn verbose_block(&self, _hash: GlobalH256) -> Option { + Some(VerboseBlock::default()) + } + } + + impl BlockChainClientCoreApi for ErrorBlockChainClientCore { + fn best_block_hash(&self) -> GlobalH256 { + test_data::genesis().hash() + } + + fn block_hash(&self, _height: u32) -> Option { + None + } + + fn difficulty(&self) -> f64 { + 1f64 + } + + fn raw_block(&self, _hash: GlobalH256) -> Option { + None + } + + fn verbose_block(&self, _hash: GlobalH256) -> Option { + None + } + } + + #[test] + fn best_block_hash_success() { + // TODO + } + + #[test] + fn block_hash_success() { + // TODO + } + + #[test] + fn block_hash_error() { + // TODO + } + + #[test] + fn difficulty_success() { + // TODO + } + + #[test] + fn verbose_block_contents() { + // TODO + } + + #[test] + fn raw_block_success() { + // TODO + } + + #[test] + fn raw_block_error() { + // TODO + } + + #[test] + fn verbose_block_success() { + // TODO + } + + #[test] + fn verbose_block_error() { + // TODO + } } From bca86010dfaf3dca3de5f733e0f0491d0c4617b0 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Sat, 10 Dec 2016 16:41:45 +0300 Subject: [PATCH 08/13] continue blockchain RPCs implementation --- Cargo.lock | 1 + db/src/storage.rs | 17 +- db/src/test_storage.rs | 4 + rpc/Cargo.toml | 1 + rpc/src/lib.rs | 2 + rpc/src/v1/helpers/errors.rs | 12 ++ rpc/src/v1/helpers/mod.rs | 1 + rpc/src/v1/impls/blockchain.rs | 270 ++++++++++++++++++++++++++++---- rpc/src/v1/traits/blockchain.rs | 45 +----- rpc/src/v1/types/hash.rs | 8 + 10 files changed, 286 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 227c4c7c..fe1f6f02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -800,6 +800,7 @@ version = "0.1.0" dependencies = [ "chain 0.1.0", "db 0.1.0", + "ethcore-devtools 1.3.0", "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)", "jsonrpc-macros 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)", diff --git a/db/src/storage.rs b/db/src/storage.rs index d6efc175..53954925 100644 --- a/db/src/storage.rs +++ b/db/src/storage.rs @@ -49,6 +49,9 @@ pub trait Store : BlockProvider + BlockStapler + TransactionProvider + Transacti /// get best header fn best_header(&self) -> Option; + + /// get blockchain difficulty + fn difficulty(&self) -> f64; } /// Blockchain storage with rocksdb database @@ -411,13 +414,6 @@ impl Storage { } } - - pub fn difficulty(&self) -> f64 { - self.best_hash() - .and_then(|h| self.block_header_by_hash(&h)) - .map(|header| header.bits.to_f64()) - .unwrap_or(1.0f64) - } } impl BlockHeaderProvider for Storage { @@ -713,6 +709,13 @@ impl Store for Storage { |bb| Some(self.block_header_by_hash(&bb.hash).expect("Best block exists but no such header. Race condition?")), ) } + + fn difficulty(&self) -> f64 { + self.best_hash() + .and_then(|h| self.block_header_by_hash(&h)) + .map(|header| header.bits.to_f64()) + .unwrap_or(1.0f64) + } } #[cfg(test)] diff --git a/db/src/test_storage.rs b/db/src/test_storage.rs index d95af684..1c0739ce 100644 --- a/db/src/test_storage.rs +++ b/db/src/test_storage.rs @@ -215,5 +215,9 @@ impl Store for TestStorage { |bb| Some(self.block_header(BlockRef::Hash(bb.hash.clone())).expect("Best block exists but no such header. Race condition?")) ) } + + fn difficulty(&self) -> f64 { + unimplemented!() + } } diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 4f499e29..f0a05ae7 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -17,6 +17,7 @@ jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" } jsonrpc-macros = { git = "https://github.com/ethcore/jsonrpc.git" } jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git" } +ethcore-devtools = { path = "../devtools" } sync = { path = "../sync" } serialization = { path = "../serialization" } chain = { path = "../chain" } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 0088da5c..b8a0376b 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -21,6 +21,8 @@ extern crate db; extern crate test_data; extern crate miner; extern crate verification; +#[cfg(test)] +extern crate ethcore_devtools as devtools; pub mod v1; pub mod rpc_server; diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index c575d6c3..8d15d0af 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -9,6 +9,18 @@ mod codes { use std::fmt; use jsonrpc_core::{Error, ErrorCode, Value}; +macro_rules! rpc_unimplemented { + () => (Err(::v1::helpers::errors::unimplemented(None))) +} + +pub fn unimplemented(details: Option) -> Error { + Error { + code: ErrorCode::InternalError, + message: "This request is not implemented yet. Please create an issue on Github repo.".into(), + data: details.map(Value::String), + } +} + pub fn invalid_params(param: &str, details: T) -> Error { Error { code: ErrorCode::InvalidParams, diff --git a/rpc/src/v1/helpers/mod.rs b/rpc/src/v1/helpers/mod.rs index 629e98fb..45112cb7 100644 --- a/rpc/src/v1/helpers/mod.rs +++ b/rpc/src/v1/helpers/mod.rs @@ -1 +1,2 @@ +#[macro_use] pub mod errors; diff --git a/rpc/src/v1/impls/blockchain.rs b/rpc/src/v1/impls/blockchain.rs index 745f0672..dfd39bdb 100644 --- a/rpc/src/v1/impls/blockchain.rs +++ b/rpc/src/v1/impls/blockchain.rs @@ -12,6 +12,10 @@ use verification; use ser::serialize; use primitives::hash::H256 as GlobalH256; +// TODO +// use jsonrpc_macros::Trailing; +type Trailing = Option; + pub struct BlockChainClient { core: T, } @@ -48,7 +52,7 @@ impl BlockChainClientCoreApi for BlockChainClientCore { } fn difficulty(&self) -> f64 { - unimplemented!() + self.storage.difficulty() } fn raw_block(&self, hash: GlobalH256) -> Option { @@ -68,7 +72,7 @@ impl BlockChainClientCoreApi for BlockChainClientCore { None => -1, }; let block_size = block.size(); - let median_time = verification::ChainVerifier::median_timestamp(self.storage.as_block_header_provider(), block.header()); + let median_time = verification::ChainVerifier::median_timestamp(self.storage.as_block_header_provider(), &block.header.raw); VerboseBlock { confirmations: confirmations, size: block_size as u32, @@ -76,18 +80,18 @@ impl BlockChainClientCoreApi for BlockChainClientCore { weight: block_size as u32, // TODO: segwit height: height, mediantime: median_time, - difficulty: 0f64, // TODO: https://en.bitcoin.it/wiki/Difficulty + https://www.bitcoinmining.com/what-is-bitcoin-mining-difficulty/ + difficulty: block.header.raw.bits.to_f64(), chainwork: U256::default(), // TODO: read from storage - previousblockhash: Some(block.header().previous_header_hash.clone().into()), + previousblockhash: Some(block.header.raw.previous_header_hash.clone().into()), nextblockhash: height.and_then(|h| self.storage.block_hash(h + 1).map(|h| h.into())), - bits: block.header().bits.into(), - hash: block.hash().clone().reversed().into(), - merkleroot: block.header().merkle_root_hash.clone().into(), - nonce: block.header().nonce, - time: block.header().time, - tx: vec![], // TODO - version: block.header().version, - version_hex: format!("{:x}", block.header().version), + bits: block.header.raw.bits.into(), + hash: block.hash().clone().into(), + merkleroot: block.header.raw.merkle_root_hash.clone().into(), + nonce: block.header.raw.nonce, + time: block.header.raw.time, + tx: block.transactions.into_iter().map(|t| t.hash.into()).collect(), + version: block.header.raw.version, + version_hex: format!("{:x}", &block.header.raw.version), } }) } @@ -103,12 +107,12 @@ impl BlockChainClient where T: BlockChainClientCoreApi { impl BlockChain for BlockChainClient where T: BlockChainClientCoreApi { fn best_block_hash(&self) -> Result { - Ok(self.core.best_block_hash().into()) + Ok(self.core.best_block_hash().reversed().into()) } fn block_hash(&self, height: u32) -> Result { self.core.block_hash(height) - .map(H256::from) + .map(|h| h.reversed().into()) .ok_or(block_at_height_not_found(height)) } @@ -116,11 +120,20 @@ impl BlockChain for BlockChainClient where T: BlockChainClientCoreApi { Ok(self.core.difficulty()) } - fn block(&self, hash: H256, verbose: Option) -> Result { + fn block(&self, hash: H256, verbose: Trailing) -> Result { let global_hash: GlobalH256 = hash.clone().into(); if verbose.unwrap_or_default() { - self.core.verbose_block(global_hash.reversed()) - .map(|block| GetBlockResponse::Verbose(block)) + let verbose_block = self.core.verbose_block(global_hash.reversed()); + if let Some(mut verbose_block) = verbose_block { + verbose_block.previousblockhash = verbose_block.previousblockhash.map(|h| h.reversed()); + verbose_block.nextblockhash = verbose_block.nextblockhash.map(|h| h.reversed()); + verbose_block.hash = verbose_block.hash.reversed(); + verbose_block.merkleroot = verbose_block.merkleroot.reversed(); + verbose_block.tx = verbose_block.tx.into_iter().map(|h| h.reversed()).collect(); + Some(GetBlockResponse::Verbose(verbose_block)) + } else { + None + } } else { self.core.raw_block(global_hash.reversed()) .map(|block| GetBlockResponse::Raw(block)) @@ -128,11 +141,11 @@ impl BlockChain for BlockChainClient where T: BlockChainClientCoreApi { .ok_or(block_not_found(hash)) } - fn transaction(&self, _hash: H256, _watch_only: Option) -> Result { + fn transaction(&self, _hash: H256, _watch_only: Trailing) -> Result { rpc_unimplemented!() } - fn transaction_out(&self, _transaction_hash: H256, _out_index: u32, _include_mempool: Option) -> Result { + fn transaction_out(&self, _transaction_hash: H256, _out_index: u32, _include_mempool: Trailing) -> Result { rpc_unimplemented!() } @@ -143,6 +156,11 @@ impl BlockChain for BlockChainClient where T: BlockChainClientCoreApi { #[cfg(test)] pub mod tests { + use std::sync::Arc; + use devtools::RandomTempPath; + use jsonrpc_core::{IoHandler, GenericIoHandler}; + use db::{self, BlockStapler}; + use primitives::bytes::Bytes as GlobalBytes; use primitives::hash::H256 as GlobalH256; use v1::types::{VerboseBlock, RawBlock}; use test_data; @@ -167,11 +185,34 @@ pub mod tests { } fn raw_block(&self, _hash: GlobalH256) -> Option { - Some(RawBlock::from(vec![0])) + let b2_bytes: GlobalBytes = "010000004860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000d5fdcc541e25de1c7a5addedf24858b8bb665c9f36ef744ee42c316022c90f9bb0bc6649ffff001d08d2bd610101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d010bffffffff0100f2052a010000004341047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77ac00000000".into(); + Some(RawBlock::from(b2_bytes)) } fn verbose_block(&self, _hash: GlobalH256) -> Option { - Some(VerboseBlock::default()) + // https://blockexplorer.com/block/000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd + // https://blockchain.info/ru/block/000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd + // https://webbtc.com/block/000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd.json + Some(VerboseBlock { + hash: "bddd99ccfda39da1b108ce1a5d70038d0a967bacb68b6b63065f626a00000000".into(), + confirmations: 1, // h2 + size: 215, + strippedsize: 215, + weight: 215, + height: Some(2), + version: 1, + version_hex: "1".to_owned(), + merkleroot: "d5fdcc541e25de1c7a5addedf24858b8bb665c9f36ef744ee42c316022c90f9b".into(), + tx: vec!["d5fdcc541e25de1c7a5addedf24858b8bb665c9f36ef744ee42c316022c90f9b".into()], + time: 1231469744, + mediantime: None, + nonce: 1639830024, + bits: 486604799, + difficulty: 1.0, + chainwork: 0.into(), + previousblockhash: Some("4860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000".into()), + nextblockhash: None, + }) } } @@ -199,46 +240,215 @@ pub mod tests { #[test] fn best_block_hash_success() { - // TODO + let client = BlockChainClient::new(SuccessBlockChainClientCore::default()); + let handler = IoHandler::new(); + handler.add_delegate(client.to_delegate()); + + let sample = handler.handle_request_sync(&(r#" + { + "jsonrpc": "2.0", + "method": "getbestblockhash", + "params": [], + "id": 1 + }"#)).unwrap(); + + // direct hash is 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000 + // but client expects reverse hash + assert_eq!(&sample, r#"{"jsonrpc":"2.0","result":"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f","id":1}"#); } #[test] fn block_hash_success() { - // TODO + let client = BlockChainClient::new(SuccessBlockChainClientCore::default()); + let handler = IoHandler::new(); + handler.add_delegate(client.to_delegate()); + + let sample = handler.handle_request_sync(&(r#" + { + "jsonrpc": "2.0", + "method": "getblockhash", + "params": [0], + "id": 1 + }"#)).unwrap(); + + // direct hash is 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000 + // but client expects reverse hash + assert_eq!(&sample, r#"{"jsonrpc":"2.0","result":"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f","id":1}"#); } #[test] fn block_hash_error() { - // TODO + let client = BlockChainClient::new(ErrorBlockChainClientCore::default()); + let handler = IoHandler::new(); + handler.add_delegate(client.to_delegate()); + + let sample = handler.handle_request_sync(&(r#" + { + "jsonrpc": "2.0", + "method": "getblockhash", + "params": [0], + "id": 1 + }"#)).unwrap(); + + assert_eq!(&sample, r#"{"jsonrpc":"2.0","error":{"code":-32099,"message":"Block at given height is not found","data":"0"},"id":1}"#); } #[test] fn difficulty_success() { - // TODO + let client = BlockChainClient::new(SuccessBlockChainClientCore::default()); + let handler = IoHandler::new(); + handler.add_delegate(client.to_delegate()); + + let sample = handler.handle_request_sync(&(r#" + { + "jsonrpc": "2.0", + "method": "getdifficulty", + "params": [], + "id": 1 + }"#)).unwrap(); + + assert_eq!(&sample, r#"{"jsonrpc":"2.0","result":1.0,"id":1}"#); } #[test] fn verbose_block_contents() { - // TODO + let path = RandomTempPath::create_dir(); + let storage = Arc::new(db::Storage::new(path.as_path()).unwrap()); + storage.insert_block(&test_data::genesis()).expect("no error"); + storage.insert_block(&test_data::block_h1()).expect("no error"); + storage.insert_block(&test_data::block_h2()).expect("no error"); + + let core = BlockChainClientCore::new(storage); + + // get info on block #1: + // https://blockexplorer.com/block/00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048 + // https://blockchain.info/block/00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048 + // https://webbtc.com/block/00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048.json + let verbose_block = core.verbose_block("4860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000".into()); + assert_eq!(verbose_block, Some(VerboseBlock { + hash: "4860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000".into(), + confirmations: 2, // h1 + h2 + size: 215, + strippedsize: 215, + weight: 215, + height: Some(1), + version: 1, + version_hex: "1".to_owned(), + merkleroot: "982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e".into(), + tx: vec!["982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e".into()], + time: 1231469665, + mediantime: None, + nonce: 2573394689, + bits: 486604799, + difficulty: 1.0, + chainwork: 0.into(), + previousblockhash: Some("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000".into()), + nextblockhash: Some("bddd99ccfda39da1b108ce1a5d70038d0a967bacb68b6b63065f626a00000000".into()), + })); + + // get info on block #2: + // https://blockexplorer.com/block/000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd + // https://blockchain.info/ru/block/000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd + // https://webbtc.com/block/000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd.json + let verbose_block = core.verbose_block("bddd99ccfda39da1b108ce1a5d70038d0a967bacb68b6b63065f626a00000000".into()); + assert_eq!(verbose_block, Some(VerboseBlock { + hash: "bddd99ccfda39da1b108ce1a5d70038d0a967bacb68b6b63065f626a00000000".into(), + confirmations: 1, // h2 + size: 215, + strippedsize: 215, + weight: 215, + height: Some(2), + version: 1, + version_hex: "1".to_owned(), + merkleroot: "d5fdcc541e25de1c7a5addedf24858b8bb665c9f36ef744ee42c316022c90f9b".into(), + tx: vec!["d5fdcc541e25de1c7a5addedf24858b8bb665c9f36ef744ee42c316022c90f9b".into()], + time: 1231469744, + mediantime: None, + nonce: 1639830024, + bits: 486604799, + difficulty: 1.0, + chainwork: 0.into(), + previousblockhash: Some("4860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000".into()), + nextblockhash: None, + })); } #[test] fn raw_block_success() { - // TODO + let client = BlockChainClient::new(SuccessBlockChainClientCore::default()); + let handler = IoHandler::new(); + handler.add_delegate(client.to_delegate()); + + let expected = r#"{"jsonrpc":"2.0","result":"010000004860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000d5fdcc541e25de1c7a5addedf24858b8bb665c9f36ef744ee42c316022c90f9bb0bc6649ffff001d08d2bd610101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d010bffffffff0100f2052a010000004341047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77ac00000000","id":1}"#; + + let sample = handler.handle_request_sync(&(r#" + { + "jsonrpc": "2.0", + "method": "getblock", + "params": ["000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd", false], + "id": 1 + }"#)).unwrap(); + assert_eq!(&sample, expected); + + // try without optional parameter + let sample = handler.handle_request_sync(&(r#" + { + "jsonrpc": "2.0", + "method": "getblock", + "params": ["000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd"], + "id": 1 + }"#)).unwrap(); + assert_eq!(&sample, expected); } #[test] fn raw_block_error() { - // TODO + let client = BlockChainClient::new(ErrorBlockChainClientCore::default()); + let handler = IoHandler::new(); + handler.add_delegate(client.to_delegate()); + + let sample = handler.handle_request_sync(&(r#" + { + "jsonrpc": "2.0", + "method": "getblock", + "params": ["000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd", false], + "id": 1 + }"#)).unwrap(); + + assert_eq!(&sample, r#"{"jsonrpc":"2.0","error":{"code":-32099,"message":"Block with given hash is not found","data":"000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd"},"id":1}"#); } #[test] fn verbose_block_success() { - // TODO + let client = BlockChainClient::new(SuccessBlockChainClientCore::default()); + let handler = IoHandler::new(); + handler.add_delegate(client.to_delegate()); + + let sample = handler.handle_request_sync(&(r#" + { + "jsonrpc": "2.0", + "method": "getblock", + "params": ["000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd",true], + "id": 1 + }"#)).unwrap(); + + assert_eq!(&sample, r#"{"jsonrpc":"2.0","result":{"bits":486604799,"chainwork":"","confirmations":1,"difficulty":1.0,"hash":"000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd","height":2,"mediantime":null,"merkleroot":"9b0fc92260312ce44e74ef369f5c66bbb85848f2eddd5a7a1cde251e54ccfdd5","nextblockhash":null,"nonce":1639830024,"previousblockhash":"00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048","size":215,"strippedsize":215,"time":1231469744,"tx":["9b0fc92260312ce44e74ef369f5c66bbb85848f2eddd5a7a1cde251e54ccfdd5"],"version":1,"versionHex":"1","weight":215},"id":1}"#); } #[test] fn verbose_block_error() { - // TODO + let client = BlockChainClient::new(ErrorBlockChainClientCore::default()); + let handler = IoHandler::new(); + handler.add_delegate(client.to_delegate()); + + let sample = handler.handle_request_sync(&(r#" + { + "jsonrpc": "2.0", + "method": "getblock", + "params": ["000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd", true], + "id": 1 + }"#)).unwrap(); + + assert_eq!(&sample, r#"{"jsonrpc":"2.0","error":{"code":-32099,"message":"Block with given hash is not found","data":"000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd"},"id":1}"#); } } diff --git a/rpc/src/v1/traits/blockchain.rs b/rpc/src/v1/traits/blockchain.rs index 50d06d6d..44228127 100644 --- a/rpc/src/v1/traits/blockchain.rs +++ b/rpc/src/v1/traits/blockchain.rs @@ -1,12 +1,15 @@ use jsonrpc_core::Error; -use v1::helpers::auto_args::Wrap; use v1::types::H256; use v1::types::GetBlockResponse; use v1::types::GetTransactionResponse; use v1::types::GetTxOutResponse; use v1::types::GetTxOutSetInfoResponse; +// TODO +// use jsonrpc_macros::Trailing; +type Trailing = Option; + build_rpc_trait! { /// Parity-bitcoin blockchain data interface. pub trait BlockChain { @@ -21,49 +24,15 @@ build_rpc_trait! { fn difficulty(&self) -> Result; /// Get information on given block. #[rpc(name = "getblock")] - fn block(&self, H256, Option) -> Result; + fn block(&self, H256, Trailing) -> Result; /// Get information on given transaction. #[rpc(name = "gettransaction")] - fn transaction(&self, H256, Option) -> Result; + fn transaction(&self, H256, Trailing) -> Result; /// Get details about an unspent transaction output. #[rpc(name = "gettxout")] - fn transaction_out(&self, H256, u32, Option) -> Result; + fn transaction_out(&self, H256, u32, Trailing) -> Result; /// Get statistics about the unspent transaction output set. #[rpc(name = "gettxoutsetinfo")] fn transaction_out_set_info(&self) -> Result; } } -/* -use db::IndexedBlock; -use primitives::hash::H256; -use primitives::uint::U256; - -/// Verbose block information -#[derive(Debug)] -pub struct VerboseBlock { - /// Block - pub block: IndexedBlock, - /// Number of confirmations. -1 if block is on the side chain - pub confirmations: i64, - /// Block size - pub size: u32, - /// Block size, excluding witness data - pub stripped_size: u32, - /// Block weight - pub weight: u32, - /// Block height. We only provide this for main chain blocks - pub height: Option, - /// Median block time in seconds since epoch (Jan 1 1970 GMT) - /// We only provide this when there are > 2 parent blocks - pub median_time: Option, - /// Block difficulty - pub difficulty: f64, - /// Expected number of hashes required to produce the chain up to this block - pub chain_work: Option, - /// Hash of previous block - pub previous_block_hash: Option, - /// Hash of next block - pub next_block_hash: Option, -} - -*/ \ No newline at end of file diff --git a/rpc/src/v1/types/hash.rs b/rpc/src/v1/types/hash.rs index 79e388e2..24c3f299 100644 --- a/rpc/src/v1/types/hash.rs +++ b/rpc/src/v1/types/hash.rs @@ -12,6 +12,14 @@ macro_rules! impl_hash { #[derive(Eq)] pub struct $name([u8; $size]); + impl $name { + pub fn reversed(&self) -> Self { + let mut result = self.clone(); + result.0.reverse(); + result + } + } + impl Default for $name { fn default() -> Self { $name([0; $size]) From fc146bd2250365b487a896f0446818a73e3c5fbc Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Sat, 10 Dec 2016 21:24:39 +0300 Subject: [PATCH 09/13] continue blockchain RPCs --- Cargo.lock | 7 +- rpc/Cargo.toml | 1 + rpc/src/lib.rs | 1 + rpc/src/v1/impls/blockchain.rs | 7 +- rpc/src/v1/traits/blockchain.rs | 4 +- rpc/src/v1/types/get_tx_out_response.rs | 35 ++++++++ rpc/src/v1/types/hash.rs | 18 +++-- rpc/src/v1/types/mod.rs.in | 2 + rpc/src/v1/types/script.rs | 102 ++++++++++++++++++++++++ script/src/lib.rs | 2 +- 10 files changed, 160 insertions(+), 19 deletions(-) create mode 100644 rpc/src/v1/types/script.rs diff --git a/Cargo.lock b/Cargo.lock index fe1f6f02..613d898e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -331,7 +331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jsonrpc-core" version = "4.0.0" -source = "git+https://github.com/ethcore/jsonrpc.git#140257f1a726e9190bdaafeb4625b2a5400de4da" +source = "git+https://github.com/ethcore/jsonrpc.git#ce49b762bc3e005f0cf549e1d98fc51b47f2aa54" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -343,7 +343,7 @@ dependencies = [ [[package]] name = "jsonrpc-http-server" version = "6.1.1" -source = "git+https://github.com/ethcore/jsonrpc.git#140257f1a726e9190bdaafeb4625b2a5400de4da" +source = "git+https://github.com/ethcore/jsonrpc.git#ce49b762bc3e005f0cf549e1d98fc51b47f2aa54" dependencies = [ "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", @@ -354,7 +354,7 @@ dependencies = [ [[package]] name = "jsonrpc-macros" version = "0.1.0" -source = "git+https://github.com/ethcore/jsonrpc.git#140257f1a726e9190bdaafeb4625b2a5400de4da" +source = "git+https://github.com/ethcore/jsonrpc.git#ce49b762bc3e005f0cf549e1d98fc51b47f2aa54" dependencies = [ "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -810,6 +810,7 @@ dependencies = [ "p2p 0.1.0", "primitives 0.1.0", "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "script 0.1.0", "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", "serde_codegen 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index f0a05ae7..d1d14c0d 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -28,6 +28,7 @@ db = { path = "../db" } test-data = { path = "../test-data" } miner = { path = "../miner" } verification = { path = "../verification" } +script = { path = "../script" } [build-dependencies] serde_codegen = { version = "0.8.0", optional = true } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index b8a0376b..816d4e4c 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -23,6 +23,7 @@ extern crate miner; extern crate verification; #[cfg(test)] extern crate ethcore_devtools as devtools; +extern crate script; pub mod v1; pub mod rpc_server; diff --git a/rpc/src/v1/impls/blockchain.rs b/rpc/src/v1/impls/blockchain.rs index dfd39bdb..ed9ed399 100644 --- a/rpc/src/v1/impls/blockchain.rs +++ b/rpc/src/v1/impls/blockchain.rs @@ -6,15 +6,13 @@ 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 jsonrpc_macros::Trailing; use jsonrpc_core::Error; use db; use verification; use ser::serialize; use primitives::hash::H256 as GlobalH256; -// TODO -// use jsonrpc_macros::Trailing; -type Trailing = Option; pub struct BlockChainClient { core: T, @@ -122,7 +120,7 @@ impl BlockChain for BlockChainClient where T: BlockChainClientCoreApi { fn block(&self, hash: H256, verbose: Trailing) -> Result { let global_hash: GlobalH256 = hash.clone().into(); - if verbose.unwrap_or_default() { + if verbose.0 { let verbose_block = self.core.verbose_block(global_hash.reversed()); if let Some(mut verbose_block) = verbose_block { verbose_block.previousblockhash = verbose_block.previousblockhash.map(|h| h.reversed()); @@ -142,6 +140,7 @@ impl BlockChain for BlockChainClient where T: BlockChainClientCoreApi { } fn transaction(&self, _hash: H256, _watch_only: Trailing) -> Result { + // TODO: we do not have wallet yet => we can not support rpc_unimplemented!() } diff --git a/rpc/src/v1/traits/blockchain.rs b/rpc/src/v1/traits/blockchain.rs index 44228127..74a925bc 100644 --- a/rpc/src/v1/traits/blockchain.rs +++ b/rpc/src/v1/traits/blockchain.rs @@ -1,3 +1,4 @@ +use jsonrpc_macros::Trailing; use jsonrpc_core::Error; use v1::types::H256; @@ -6,9 +7,6 @@ use v1::types::GetTransactionResponse; use v1::types::GetTxOutResponse; use v1::types::GetTxOutSetInfoResponse; -// TODO -// use jsonrpc_macros::Trailing; -type Trailing = Option; build_rpc_trait! { /// Parity-bitcoin blockchain data interface. diff --git a/rpc/src/v1/types/get_tx_out_response.rs b/rpc/src/v1/types/get_tx_out_response.rs index e7bfc620..7fa884c5 100644 --- a/rpc/src/v1/types/get_tx_out_response.rs +++ b/rpc/src/v1/types/get_tx_out_response.rs @@ -1,3 +1,38 @@ +use super::bytes::Bytes; +use super::hash::{H160, H256}; +use super::script::ScriptType; + +/// gettxout response #[derive(Debug, Serialize, Deserialize)] pub struct GetTxOutResponse { + /// Hash of the block this transaction output is included into. + bestblock: H256, + /// Number of confirmations of this transaction + confirmations: u32, + /// Transaction value in BTC + value: f64, + /// Script info + #[serde(rename = "scriptPubKey")] + script_pub_key: TxOutScriptPubKey, + /// This transaction version + version: i32, + /// Is this transactio a coinbase transaction? + coinbase: bool, +} + +/// Script pub key information +#[derive(Debug, Serialize, Deserialize)] +pub struct TxOutScriptPubKey { + /// Script code + asm: String, + /// Script hex + hex: Bytes, + /// Number of required signatures + #[serde(rename = "reqSigs")] + req_sigs: u32, + /// Type of script + #[serde(rename = "type")] + script_type: ScriptType, + /// Array of bitcoin addresses + addresses: Vec, } diff --git a/rpc/src/v1/types/hash.rs b/rpc/src/v1/types/hash.rs index 24c3f299..428765bc 100644 --- a/rpc/src/v1/types/hash.rs +++ b/rpc/src/v1/types/hash.rs @@ -5,6 +5,7 @@ use std::hash::{Hash, Hasher}; use serde; use rustc_serialize::hex::{ToHex, FromHex}; use primitives::hash::H256 as GlobalH256; +use primitives::hash::H160 as GlobalH160; macro_rules! impl_hash { ($name: ident, $other: ident, $size: expr) => { @@ -12,14 +13,6 @@ macro_rules! impl_hash { #[derive(Eq)] pub struct $name([u8; $size]); - impl $name { - pub fn reversed(&self) -> Self { - let mut result = self.clone(); - result.0.reverse(); - result - } - } - impl Default for $name { fn default() -> Self { $name([0; $size]) @@ -135,6 +128,15 @@ macro_rules! impl_hash { } impl_hash!(H256, GlobalH256, 32); +impl_hash!(H160, GlobalH160, 20); + +impl H256 { + pub fn reversed(&self) -> Self { + let mut result = self.clone(); + result.0.reverse(); + result + } +} #[cfg(test)] mod tests { diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index dbd4f77e..58d73215 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -8,6 +8,7 @@ mod get_tx_out_set_info_response; mod hash; mod raw_block; mod raw_transaction; +mod script; mod uint; pub use self::block_template::{BlockTemplate, BlockTemplateTransaction}; @@ -20,4 +21,5 @@ pub use self::get_tx_out_set_info_response::GetTxOutSetInfoResponse; pub use self::hash::H256; pub use self::raw_block::RawBlock; pub use self::raw_transaction::RawTransaction; +pub use self::script::ScriptType; pub use self::uint::U256; diff --git a/rpc/src/v1/types/script.rs b/rpc/src/v1/types/script.rs new file mode 100644 index 00000000..bf9fbe13 --- /dev/null +++ b/rpc/src/v1/types/script.rs @@ -0,0 +1,102 @@ +use serde::{Serialize, Deserialize, Serializer, Deserializer}; +use script::ScriptType as GlobalScriptType; + +#[derive(Debug, PartialEq)] +pub enum ScriptType { + NonStandard, + PubKey, + PubKeyHash, + ScriptHash, + Multisig, + NullData, + WitnessScript, + WitnessKey, +} + +impl From for ScriptType { + fn from(script_type: GlobalScriptType) -> Self { + match script_type { + GlobalScriptType::NonStandard => ScriptType::NonStandard, + GlobalScriptType::PubKey => ScriptType::PubKey, + GlobalScriptType::PubKeyHash => ScriptType::PubKeyHash, + GlobalScriptType::ScriptHash => ScriptType::ScriptHash, + GlobalScriptType::Multisig => ScriptType::Multisig, + GlobalScriptType::NullData => ScriptType::NullData, + GlobalScriptType::WitnessScript => ScriptType::WitnessScript, + GlobalScriptType::WitnessKey => ScriptType::WitnessKey, + } + } +} + +impl Serialize for ScriptType { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { + match *self { + ScriptType::NonStandard => "nonstandard".serialize(serializer), + ScriptType::PubKey => "pubkey".serialize(serializer), + ScriptType::PubKeyHash => "pubkeyhash".serialize(serializer), + ScriptType::ScriptHash => "scripthash".serialize(serializer), + ScriptType::Multisig => "multisig".serialize(serializer), + ScriptType::NullData => "nulldata".serialize(serializer), + ScriptType::WitnessScript => "witness_v0_scripthash".serialize(serializer), + ScriptType::WitnessKey => "witness_v0_keyhash".serialize(serializer), + } + } +} + +impl Deserialize for ScriptType { + fn deserialize(deserializer: &mut D) -> Result where D: Deserializer { + use serde::de::Visitor; + + struct ScriptTypeVisitor; + + impl Visitor for ScriptTypeVisitor { + type Value = ScriptType; + + fn visit_str(&mut self, value: &str) -> Result where E: ::serde::de::Error { + match value { + "nonstandard" => Ok(ScriptType::NonStandard), + "pubkey" => Ok(ScriptType::PubKey), + "pubkeyhash" => Ok(ScriptType::PubKeyHash), + "scripthash" => Ok(ScriptType::ScriptHash), + "multisig" => Ok(ScriptType::Multisig), + "nulldata" => Ok(ScriptType::NullData), + "witness_v0_scripthash" => Ok(ScriptType::WitnessScript), + "witness_v0_keyhash" => Ok(ScriptType::WitnessKey), + _ => Err(E::invalid_value(&format!("unknown ScriptType variant: {}", value))), + } + } + } + + deserializer.deserialize(ScriptTypeVisitor) + } +} + +#[cfg(test)] +mod tests { + use super::ScriptType; + use serde_json; + + #[test] + fn script_type_serialize() { + assert_eq!(serde_json::to_string(&ScriptType::NonStandard).unwrap(), r#""nonstandard""#); + assert_eq!(serde_json::to_string(&ScriptType::PubKey).unwrap(), r#""pubkey""#); + assert_eq!(serde_json::to_string(&ScriptType::PubKeyHash).unwrap(), r#""pubkeyhash""#); + assert_eq!(serde_json::to_string(&ScriptType::ScriptHash).unwrap(), r#""scripthash""#); + assert_eq!(serde_json::to_string(&ScriptType::Multisig).unwrap(), r#""multisig""#); + assert_eq!(serde_json::to_string(&ScriptType::NullData).unwrap(), r#""nulldata""#); + assert_eq!(serde_json::to_string(&ScriptType::WitnessScript).unwrap(), r#""witness_v0_scripthash""#); + assert_eq!(serde_json::to_string(&ScriptType::WitnessKey).unwrap(), r#""witness_v0_keyhash""#); + } + + #[test] + fn script_type_deserialize() { + assert_eq!(serde_json::from_str::(r#""nonstandard""#).unwrap(), ScriptType::NonStandard); + assert_eq!(serde_json::from_str::(r#""pubkey""#).unwrap(), ScriptType::PubKey); + assert_eq!(serde_json::from_str::(r#""pubkeyhash""#).unwrap(), ScriptType::PubKeyHash); + assert_eq!(serde_json::from_str::(r#""scripthash""#).unwrap(), ScriptType::ScriptHash); + assert_eq!(serde_json::from_str::(r#""multisig""#).unwrap(), ScriptType::Multisig); + assert_eq!(serde_json::from_str::(r#""nulldata""#).unwrap(), ScriptType::NullData); + assert_eq!(serde_json::from_str::(r#""witness_v0_scripthash""#).unwrap(), ScriptType::WitnessScript); + assert_eq!(serde_json::from_str::(r#""witness_v0_keyhash""#).unwrap(), ScriptType::WitnessKey); + } +} diff --git a/script/src/lib.rs b/script/src/lib.rs index 09ba56c6..e90f8bf3 100644 --- a/script/src/lib.rs +++ b/script/src/lib.rs @@ -25,7 +25,7 @@ pub use self::flags::VerificationFlags; pub use self::interpreter::{eval_script, verify_script}; pub use self::opcode::Opcode; pub use self::num::Num; -pub use self::script::Script; +pub use self::script::{Script, ScriptType}; pub use self::sign::{ TransactionInputSigner, UnsignedTransactionInput, Sighash, SighashBase, SignatureVersion From de3786a301a693a10e8b4cbd8407d062f02816c5 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Sat, 10 Dec 2016 21:49:21 +0300 Subject: [PATCH 10/13] cleaning up --- rpc/src/v1/impls/blockchain.rs | 6 ------ rpc/src/v1/traits/blockchain.rs | 4 ---- rpc/src/v1/types/get_transaction_response.rs | 3 --- rpc/src/v1/types/mod.rs.in | 2 -- 4 files changed, 15 deletions(-) delete mode 100644 rpc/src/v1/types/get_transaction_response.rs diff --git a/rpc/src/v1/impls/blockchain.rs b/rpc/src/v1/impls/blockchain.rs index ed9ed399..76a17295 100644 --- a/rpc/src/v1/impls/blockchain.rs +++ b/rpc/src/v1/impls/blockchain.rs @@ -1,6 +1,5 @@ use v1::traits::BlockChain; use v1::types::{GetBlockResponse, VerboseBlock, RawBlock}; -use v1::types::GetTransactionResponse; use v1::types::GetTxOutResponse; use v1::types::GetTxOutSetInfoResponse; use v1::types::H256; @@ -139,11 +138,6 @@ impl BlockChain for BlockChainClient where T: BlockChainClientCoreApi { .ok_or(block_not_found(hash)) } - fn transaction(&self, _hash: H256, _watch_only: Trailing) -> Result { - // TODO: we do not have wallet yet => we can not support - rpc_unimplemented!() - } - fn transaction_out(&self, _transaction_hash: H256, _out_index: u32, _include_mempool: Trailing) -> Result { rpc_unimplemented!() } diff --git a/rpc/src/v1/traits/blockchain.rs b/rpc/src/v1/traits/blockchain.rs index 74a925bc..2b75ddd0 100644 --- a/rpc/src/v1/traits/blockchain.rs +++ b/rpc/src/v1/traits/blockchain.rs @@ -3,7 +3,6 @@ use jsonrpc_core::Error; use v1::types::H256; use v1::types::GetBlockResponse; -use v1::types::GetTransactionResponse; use v1::types::GetTxOutResponse; use v1::types::GetTxOutSetInfoResponse; @@ -23,9 +22,6 @@ build_rpc_trait! { /// Get information on given block. #[rpc(name = "getblock")] fn block(&self, H256, Trailing) -> Result; - /// Get information on given transaction. - #[rpc(name = "gettransaction")] - fn transaction(&self, H256, Trailing) -> Result; /// Get details about an unspent transaction output. #[rpc(name = "gettxout")] fn transaction_out(&self, H256, u32, Trailing) -> Result; diff --git a/rpc/src/v1/types/get_transaction_response.rs b/rpc/src/v1/types/get_transaction_response.rs deleted file mode 100644 index 3a926574..00000000 --- a/rpc/src/v1/types/get_transaction_response.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[derive(Debug, Serialize, Deserialize)] -pub struct GetTransactionResponse { -} diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index 58d73215..29fc5c2b 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -2,7 +2,6 @@ mod block_template; mod block_template_request; mod bytes; mod get_block_response; -mod get_transaction_response; mod get_tx_out_response; mod get_tx_out_set_info_response; mod hash; @@ -15,7 +14,6 @@ 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_transaction_response::GetTransactionResponse; pub use self::get_tx_out_response::GetTxOutResponse; pub use self::get_tx_out_set_info_response::GetTxOutSetInfoResponse; pub use self::hash::H256; From 8185a2f7c7c07ca94918c46abde772d7e2d1be6b Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Sat, 10 Dec 2016 21:55:24 +0300 Subject: [PATCH 11/13] fixed strange compilation issues --- rpc/src/v1/impls/blockchain.rs | 1 + rpc/src/v1/types/get_block_response.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/rpc/src/v1/impls/blockchain.rs b/rpc/src/v1/impls/blockchain.rs index 76a17295..785eb628 100644 --- a/rpc/src/v1/impls/blockchain.rs +++ b/rpc/src/v1/impls/blockchain.rs @@ -156,6 +156,7 @@ pub mod tests { use primitives::bytes::Bytes as GlobalBytes; use primitives::hash::H256 as GlobalH256; use v1::types::{VerboseBlock, RawBlock}; + use v1::traits::BlockChain; use test_data; use super::*; diff --git a/rpc/src/v1/types/get_block_response.rs b/rpc/src/v1/types/get_block_response.rs index c48b9c98..864e9d0b 100644 --- a/rpc/src/v1/types/get_block_response.rs +++ b/rpc/src/v1/types/get_block_response.rs @@ -69,6 +69,7 @@ impl Serialize for GetBlockResponse { mod tests { use super::super::bytes::Bytes; use super::super::hash::H256; + use super::super::uint::U256; use serde_json; use super::*; From 9e380b8ccd12151961f0ee00eede9ccded565917 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Sun, 11 Dec 2016 00:30:19 +0300 Subject: [PATCH 12/13] 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; From 5a7688c3cc65e0715650cfbfe009ad2f2a64e56b Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Sun, 11 Dec 2016 07:02:45 +0300 Subject: [PATCH 13/13] fixed compilation --- rpc/src/v1/impls/blockchain.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rpc/src/v1/impls/blockchain.rs b/rpc/src/v1/impls/blockchain.rs index 29349dbb..bfac0b40 100644 --- a/rpc/src/v1/impls/blockchain.rs +++ b/rpc/src/v1/impls/blockchain.rs @@ -210,12 +210,15 @@ pub mod tests { use std::sync::Arc; use devtools::RandomTempPath; use jsonrpc_core::{IoHandler, GenericIoHandler}; + use jsonrpc_core::Error; use db::{self, BlockStapler}; use primitives::bytes::Bytes as GlobalBytes; use primitives::hash::H256 as GlobalH256; use v1::types::{VerboseBlock, RawBlock}; use v1::traits::BlockChain; + use v1::types::GetTxOutResponse; use v1::helpers::errors::block_not_found; + use chain::OutPoint; use test_data; use super::*;