Merge branch 'master' of github.com:ethcore/parity-bitcoin into block_assembler_tests

This commit is contained in:
debris 2016-12-12 15:23:18 +01:00
commit c59e84ebe5
24 changed files with 1067 additions and 14 deletions

9
Cargo.lock generated
View File

@ -329,7 +329,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)",
@ -341,7 +341,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)",
@ -352,7 +352,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)",
@ -809,6 +809,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)",
@ -818,6 +819,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)",
@ -826,6 +828,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]]

View File

@ -49,6 +49,9 @@ pub trait Store: AsSubstore {
/// get best header
fn best_header(&self) -> Option<chain::BlockHeader>;
/// get blockchain difficulty
fn difficulty(&self) -> f64;
}
/// Allows casting Arc<Store> to reference to any substore type
@ -457,14 +460,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 {
@ -747,6 +742,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)]

View File

@ -212,5 +212,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!()
}
}

View File

@ -12,6 +12,10 @@ impl Bytes {
pub fn new_with_len(len: usize) -> Self {
Bytes(vec![0; len])
}
pub fn take(self) -> Vec<u8> {
self.0
}
}
impl<'a> From<&'a [u8]> for Bytes {

View File

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

View File

@ -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" }
@ -26,6 +27,8 @@ network = { path = "../network" }
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 }

View File

@ -20,6 +20,10 @@ extern crate db;
#[cfg(test)]
extern crate test_data;
extern crate miner;
extern crate verification;
#[cfg(test)]
extern crate ethcore_devtools as devtools;
extern crate script;
pub mod v1;
pub mod rpc_server;

View File

@ -3,11 +3,27 @@
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;
}
use std::fmt;
use jsonrpc_core::{Error, ErrorCode, Value};
macro_rules! rpc_unimplemented {
() => (Err(::v1::helpers::errors::unimplemented(None)))
}
pub fn unimplemented(details: Option<String>) -> 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<T: fmt::Debug>(param: &str, details: T) -> Error {
Error {
code: ErrorCode::InvalidParams,
@ -24,3 +40,42 @@ pub fn execution<T: fmt::Debug>(data: T) -> Error {
}
}
pub fn block_not_found<T: fmt::Debug>(data: T) -> Error {
Error {
code: ErrorCode::ServerError(codes::BLOCK_NOT_FOUND),
message: "Block with given hash is not found".into(),
data: Some(Value::String(format!("{:?}", data))),
}
}
pub fn block_at_height_not_found<T: fmt::Debug>(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))),
}
}
pub fn transaction_not_found<T: fmt::Debug>(data: T) -> Error {
Error {
code: ErrorCode::ServerError(codes::TRANSACTION_NOT_FOUND),
message: "Transaction with given hash is not found".into(),
data: Some(Value::String(format!("{:?}", data))),
}
}
pub fn transaction_output_not_found<T: fmt::Debug>(data: T) -> Error {
Error {
code: ErrorCode::ServerError(codes::TRANSACTION_OUTPUT_NOT_FOUND),
message: "Transaction output is not found".into(),
data: Some(Value::String(format!("{:?}", data))),
}
}
pub fn transaction_of_side_branch<T: fmt::Debug>(data: T) -> Error {
Error {
code: ErrorCode::ServerError(codes::TRANSACTION_OF_SIDE_BRANCH),
message: "Transaction is of side branch".into(),
data: Some(Value::String(format!("{:?}", data))),
}
}

View File

@ -1 +1,2 @@
#[macro_use]
pub mod errors;

View File

@ -0,0 +1,518 @@
use v1::traits::BlockChain;
use v1::types::{GetBlockResponse, VerboseBlock, RawBlock};
use v1::types::{GetTxOutResponse, TxOutScriptPubKey};
use v1::types::GetTxOutSetInfoResponse;
use v1::types::H256;
use v1::types::U256;
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;
pub struct BlockChainClient<T: BlockChainClientCoreApi> {
core: T,
}
pub trait BlockChainClientCoreApi: Send + Sync + 'static {
fn best_block_hash(&self) -> GlobalH256;
fn block_hash(&self, height: u32) -> Option<GlobalH256>;
fn difficulty(&self) -> f64;
fn raw_block(&self, hash: GlobalH256) -> Option<RawBlock>;
fn verbose_block(&self, hash: GlobalH256) -> Option<VerboseBlock>;
fn verbose_transaction_out(&self, prev_out: OutPoint) -> Result<GetTxOutResponse, Error>;
}
pub struct BlockChainClientCore {
storage: db::SharedStore,
}
impl BlockChainClientCore {
pub fn new(storage: db::SharedStore) -> Self {
assert!(storage.best_block().is_some());
BlockChainClientCore {
storage: storage,
}
}
}
impl BlockChainClientCoreApi for BlockChainClientCore {
fn best_block_hash(&self) -> GlobalH256 {
self.storage.best_block().expect("storage with genesis block required").hash
}
fn block_hash(&self, height: u32) -> Option<GlobalH256> {
self.storage.block_hash(height)
}
fn difficulty(&self) -> f64 {
self.storage.difficulty()
}
fn raw_block(&self, hash: GlobalH256) -> Option<RawBlock> {
self.storage.block(hash.into())
.map(|block| {
serialize(&block).into()
})
}
fn verbose_block(&self, hash: GlobalH256) -> Option<VerboseBlock> {
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.raw);
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: block.header.raw.bits.to_f64(),
chainwork: U256::default(), // TODO: read from storage
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.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),
}
})
}
fn verbose_transaction_out(&self, prev_out: OutPoint) -> Result<GetTxOutResponse, Error> {
let transaction = match self.storage.transaction(&prev_out.hash) {
Some(transaction) => transaction,
// no transaction => no response
None => return Err(transaction_not_found(prev_out.hash)),
};
if prev_out.index >= transaction.outputs.len() as u32 {
return Err(transaction_output_not_found(prev_out));
}
let meta = match self.storage.transaction_meta(&prev_out.hash) {
Some(meta) => meta,
// not in the main branch => no response
None => return Err(transaction_of_side_branch(prev_out.hash)),
};
let block_header = match self.storage.block_header(meta.height().into()) {
Some(block_header) => block_header,
// this is possible during reorgs
None => return Err(transaction_not_found(prev_out.hash)),
};
let best_block = self.storage.best_block().expect("storage with genesis block is required");
if best_block.number < meta.height() {
// this is possible during reorgs
return Err(transaction_not_found(prev_out.hash));
}
let ref script_bytes = transaction.outputs[prev_out.index as usize].script_pubkey;
let script: Script = script_bytes.clone().into();
let script_asm = format!("{}", script);
Ok(GetTxOutResponse {
bestblock: block_header.hash().into(),
confirmations: best_block.number - meta.height() + 1,
value: 0.00000001f64 * (transaction.outputs[prev_out.index as usize].value as f64),
script_pub_key: TxOutScriptPubKey {
asm: script_asm,
hex: script_bytes.clone().into(),
req_sigs: 0, // TODO
script_type: ScriptType::NonStandard, // TODO
addresses: vec![],
},
version: transaction.version,
coinbase: transaction.is_coinbase(),
})
}
}
impl<T> BlockChainClient<T> where T: BlockChainClientCoreApi {
pub fn new(core: T) -> Self {
BlockChainClient {
core: core,
}
}
}
impl<T> BlockChain for BlockChainClient<T> where T: BlockChainClientCoreApi {
fn best_block_hash(&self) -> Result<H256, Error> {
Ok(self.core.best_block_hash().reversed().into())
}
fn block_hash(&self, height: u32) -> Result<H256, Error> {
self.core.block_hash(height)
.map(|h| h.reversed().into())
.ok_or(block_at_height_not_found(height))
}
fn difficulty(&self) -> Result<f64, Error> {
Ok(self.core.difficulty())
}
fn block(&self, hash: H256, verbose: Trailing<bool>) -> Result<GetBlockResponse, Error> {
let global_hash: GlobalH256 = hash.clone().into();
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());
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))
}
.ok_or(block_not_found(hash))
}
fn transaction_out(&self, transaction_hash: H256, out_index: u32, _include_mempool: Trailing<bool>) -> Result<GetTxOutResponse, Error> {
self.core.verbose_transaction_out(OutPoint { hash: transaction_hash.into(), index: out_index })
.map(|mut response| {
response.bestblock = response.bestblock.reversed();
response
})
}
fn transaction_out_set_info(&self) -> Result<GetTxOutSetInfoResponse, Error> {
rpc_unimplemented!()
}
}
#[cfg(test)]
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::*;
#[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<GlobalH256> {
Some(test_data::genesis().hash())
}
fn difficulty(&self) -> f64 {
1f64
}
fn raw_block(&self, _hash: GlobalH256) -> Option<RawBlock> {
let b2_bytes: GlobalBytes = "010000004860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000d5fdcc541e25de1c7a5addedf24858b8bb665c9f36ef744ee42c316022c90f9bb0bc6649ffff001d08d2bd610101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d010bffffffff0100f2052a010000004341047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77ac00000000".into();
Some(RawBlock::from(b2_bytes))
}
fn verbose_block(&self, _hash: GlobalH256) -> Option<VerboseBlock> {
// 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,
})
}
fn verbose_transaction_out(&self, _prev_out: OutPoint) -> Result<GetTxOutResponse, Error> {
Ok(GetTxOutResponse::default()) // TODO: non-default
}
}
impl BlockChainClientCoreApi for ErrorBlockChainClientCore {
fn best_block_hash(&self) -> GlobalH256 {
test_data::genesis().hash()
}
fn block_hash(&self, _height: u32) -> Option<GlobalH256> {
None
}
fn difficulty(&self) -> f64 {
1f64
}
fn raw_block(&self, _hash: GlobalH256) -> Option<RawBlock> {
None
}
fn verbose_block(&self, _hash: GlobalH256) -> Option<VerboseBlock> {
None
}
fn verbose_transaction_out(&self, prev_out: OutPoint) -> Result<GetTxOutResponse, Error> {
Err(block_not_found(prev_out.hash))
}
}
#[test]
fn best_block_hash_success() {
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() {
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() {
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() {
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() {
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() {
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() {
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() {
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() {
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}"#);
}
}

View File

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

View File

@ -0,0 +1,32 @@
use jsonrpc_macros::Trailing;
use jsonrpc_core::Error;
use v1::types::H256;
use v1::types::GetBlockResponse;
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<H256, Error>;
/// Get hash of block at given height.
#[rpc(name = "getblockhash")]
fn block_hash(&self, u32) -> Result<H256, Error>;
/// Get proof-of-work difficulty as a multiple of the minimum difficulty
#[rpc(name = "getdifficulty")]
fn difficulty(&self) -> Result<f64, Error>;
/// Get information on given block.
#[rpc(name = "getblock")]
fn block(&self, H256, Trailing<bool>) -> Result<GetBlockResponse, Error>;
/// Get details about an unspent transaction output.
#[rpc(name = "gettxout")]
fn transaction_out(&self, H256, u32, Trailing<bool>) -> Result<GetTxOutResponse, Error>;
/// Get statistics about the unspent transaction output set.
#[rpc(name = "gettxoutsetinfo")]
fn transaction_out_set_info(&self) -> Result<GetTxOutSetInfoResponse, Error>;
}
}

View File

@ -1,5 +1,7 @@
mod blockchain;
mod miner;
mod raw;
pub use self::blockchain::BlockChain;
pub use self::miner::Miner;
pub use self::raw::Raw;

View File

@ -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<GlobalBytes> for Bytes {
fn from(v: GlobalBytes) -> Self {
Bytes(v.take())
}
}
impl From<Vec<u8>> for Bytes {
fn from(bytes: Vec<u8>) -> Bytes {
Bytes(bytes)

View File

@ -0,0 +1,148 @@
use serde::{Serialize, Serializer};
use super::hash::H256;
use super::uint::U256;
use super::raw_block::RawBlock;
/// Response to getblock RPC request
#[derive(Debug)]
pub enum GetBlockResponse {
/// When asking for short response
Raw(RawBlock),
/// When asking for verbose response
Verbose(VerboseBlock),
}
/// Verbose block information
#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct VerboseBlock {
/// 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
/// TODO: bitcoind always returns value, but we hold this value for main chain blocks only
pub height: Option<u32>,
/// Block version
pub version: u32,
/// Block version as hex
#[serde(rename = "versionHex")]
pub version_hex: String,
/// Merkle root of this block
pub merkleroot: H256,
/// Transactions ids
pub tx: Vec<H256>,
/// Block time in seconds since epoch (Jan 1 1970 GMT)
pub time: u32,
/// Median block time in seconds since epoch (Jan 1 1970 GMT)
/// TODO: bitcoind always returns value, but we can calculate this only if height(block) > 2
pub mediantime: Option<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: U256,
/// Hash of previous block
pub previousblockhash: Option<H256>,
/// Hash of next block
pub nextblockhash: Option<H256>,
}
impl Serialize for GetBlockResponse {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
match *self {
GetBlockResponse::Raw(ref raw_block) => raw_block.serialize(serializer),
GetBlockResponse::Verbose(ref verbose_block) => verbose_block.serialize(serializer),
}
}
}
#[cfg(test)]
mod tests {
use super::super::bytes::Bytes;
use super::super::hash::H256;
use super::super::uint::U256;
use serde_json;
use super::*;
#[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":"","previousblockhash":null,"nextblockhash":null}"#);
let block = VerboseBlock {
hash: H256::from(1),
confirmations: -1,
size: 500000,
strippedsize: 444444,
weight: 5236235,
height: Some(3513513),
version: 1,
version_hex: "01".to_owned(),
merkleroot: H256::from(2),
tx: vec![H256::from(3), H256::from(4)],
time: 111,
mediantime: Some(100),
nonce: 124,
bits: 13513,
difficulty: 555.555,
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":"3","previousblockhash":"0400000000000000000000000000000000000000000000000000000000000000","nextblockhash":"0500000000000000000000000000000000000000000000000000000000000000"}"#);
}
#[test]
fn verbose_block_deserialize() {
let block = VerboseBlock::default();
assert_eq!(
serde_json::from_str::<VerboseBlock>(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 = VerboseBlock {
hash: H256::from(1),
confirmations: -1,
size: 500000,
strippedsize: 444444,
weight: 5236235,
height: Some(3513513),
version: 1,
version_hex: "01".to_owned(),
merkleroot: H256::from(2),
tx: vec![H256::from(3), H256::from(4)],
time: 111,
mediantime: Some(100),
nonce: 124,
bits: 13513,
difficulty: 555.555,
chainwork: U256::from(3),
previousblockhash: Some(H256::from(4)),
nextblockhash: Some(H256::from(5)),
};
assert_eq!(
serde_json::from_str::<VerboseBlock>(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);
}
#[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 = 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":"","previousblockhash":null,"nextblockhash":null}"#);
}
}

View File

@ -0,0 +1,52 @@
use super::bytes::Bytes;
use super::hash::{H160, H256};
use super::script::ScriptType;
/// gettxout response
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct GetTxOutResponse {
/// Hash of the block this transaction output is included into.
/// Why it's called 'best'? Who knows
pub bestblock: H256,
/// Number of confirmations of this transaction
pub confirmations: u32,
/// Transaction value in BTC
pub value: f64,
/// Script info
#[serde(rename = "scriptPubKey")]
pub script_pub_key: TxOutScriptPubKey,
/// This transaction version
pub version: i32,
/// Is this transactio a coinbase transaction?
pub coinbase: bool,
}
/// Script pub key information
#[derive(Debug, Serialize, Deserialize)]
pub struct TxOutScriptPubKey {
/// Script code
pub asm: String,
/// Script hex
pub hex: Bytes,
/// Number of required signatures
#[serde(rename = "reqSigs")]
pub req_sigs: u32,
/// Type of script
#[serde(rename = "type")]
pub script_type: ScriptType,
/// Array of bitcoin addresses
pub addresses: Vec<H160>,
}
// TODO: remove me
impl Default for TxOutScriptPubKey {
fn default() -> Self {
TxOutScriptPubKey {
asm: String::default(),
hex: Bytes::default(),
req_sigs: u32::default(),
script_type: ScriptType::NonStandard,
addresses: vec![],
}
}
}

View File

@ -0,0 +1,3 @@
#[derive(Debug, Serialize, Deserialize)]
pub struct GetTxOutSetInfoResponse {
}

View File

@ -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) => {
@ -127,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 {

View File

@ -1,11 +1,23 @@
mod block_template;
mod block_template_request;
mod bytes;
mod get_block_response;
mod get_tx_out_response;
mod get_tx_out_set_info_response;
mod hash;
mod raw_block;
mod raw_transaction;
mod script;
mod 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, VerboseBlock};
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;
pub use self::raw_transaction::RawTransaction;
pub use self::script::ScriptType;
pub use self::uint::U256;

View File

@ -0,0 +1,3 @@
use super::bytes::Bytes;
pub type RawBlock = Bytes;

102
rpc/src/v1/types/script.rs Normal file
View File

@ -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<GlobalScriptType> 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<S>(&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<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: Deserializer {
use serde::de::Visitor;
struct ScriptTypeVisitor;
impl Visitor for ScriptTypeVisitor {
type Value = ScriptType;
fn visit_str<E>(&mut self, value: &str) -> Result<ScriptType, E> 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::<ScriptType>(r#""nonstandard""#).unwrap(), ScriptType::NonStandard);
assert_eq!(serde_json::from_str::<ScriptType>(r#""pubkey""#).unwrap(), ScriptType::PubKey);
assert_eq!(serde_json::from_str::<ScriptType>(r#""pubkeyhash""#).unwrap(), ScriptType::PubKeyHash);
assert_eq!(serde_json::from_str::<ScriptType>(r#""scripthash""#).unwrap(), ScriptType::ScriptHash);
assert_eq!(serde_json::from_str::<ScriptType>(r#""multisig""#).unwrap(), ScriptType::Multisig);
assert_eq!(serde_json::from_str::<ScriptType>(r#""nulldata""#).unwrap(), ScriptType::NullData);
assert_eq!(serde_json::from_str::<ScriptType>(r#""witness_v0_scripthash""#).unwrap(), ScriptType::WitnessScript);
assert_eq!(serde_json::from_str::<ScriptType>(r#""witness_v0_keyhash""#).unwrap(), ScriptType::WitnessKey);
}
}

87
rpc/src/v1/types/uint.rs Normal file
View File

@ -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<T> From<T> for $name where $other: From<T> {
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<Self, Self::Err> {
$other::from_str(s).map($name)
}
}
impl Into<$other> for $name {
fn into(self) -> $other {
self.0
}
}
impl serde::Serialize for $name {
fn serialize<S>(&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<D>(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<E>(&mut self, value: &str) -> Result<Self::Value, E> 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<E>(&mut self, value: String) -> Result<Self::Value, E> 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::<U256>(r#""100""#).unwrap();
assert_eq!(deserialized, u256);
}
}

View File

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

View File

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