Merge branch 'master' of github.com:ethcore/parity-bitcoin into block_assembler_tests
This commit is contained in:
commit
c59e84ebe5
|
@ -329,7 +329,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-core"
|
name = "jsonrpc-core"
|
||||||
version = "4.0.0"
|
version = "4.0.0"
|
||||||
source = "git+https://github.com/ethcore/jsonrpc.git#140257f1a726e9190bdaafeb4625b2a5400de4da"
|
source = "git+https://github.com/ethcore/jsonrpc.git#ce49b762bc3e005f0cf549e1d98fc51b47f2aa54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -341,7 +341,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-http-server"
|
name = "jsonrpc-http-server"
|
||||||
version = "6.1.1"
|
version = "6.1.1"
|
||||||
source = "git+https://github.com/ethcore/jsonrpc.git#140257f1a726e9190bdaafeb4625b2a5400de4da"
|
source = "git+https://github.com/ethcore/jsonrpc.git#ce49b762bc3e005f0cf549e1d98fc51b47f2aa54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
||||||
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||||
|
@ -352,7 +352,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-macros"
|
name = "jsonrpc-macros"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/ethcore/jsonrpc.git#140257f1a726e9190bdaafeb4625b2a5400de4da"
|
source = "git+https://github.com/ethcore/jsonrpc.git#ce49b762bc3e005f0cf549e1d98fc51b47f2aa54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
"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)",
|
"serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -809,6 +809,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chain 0.1.0",
|
"chain 0.1.0",
|
||||||
"db 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-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-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||||
"jsonrpc-macros 0.1.0 (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",
|
"p2p 0.1.0",
|
||||||
"primitives 0.1.0",
|
"primitives 0.1.0",
|
||||||
"rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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 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_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)",
|
"serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -826,6 +828,7 @@ dependencies = [
|
||||||
"sync 0.1.0",
|
"sync 0.1.0",
|
||||||
"test-data 0.1.0",
|
"test-data 0.1.0",
|
||||||
"tokio-core 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tokio-core 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"verification 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -49,6 +49,9 @@ pub trait Store: AsSubstore {
|
||||||
|
|
||||||
/// get best header
|
/// get best header
|
||||||
fn best_header(&self) -> Option<chain::BlockHeader>;
|
fn best_header(&self) -> Option<chain::BlockHeader>;
|
||||||
|
|
||||||
|
/// get blockchain difficulty
|
||||||
|
fn difficulty(&self) -> f64;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allows casting Arc<Store> to reference to any substore type
|
/// 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 {
|
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?")),
|
|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)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -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?"))
|
|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!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,10 @@ impl Bytes {
|
||||||
pub fn new_with_len(len: usize) -> Self {
|
pub fn new_with_len(len: usize) -> Self {
|
||||||
Bytes(vec![0; len])
|
Bytes(vec![0; len])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn take(self) -> Vec<u8> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a [u8]> for Bytes {
|
impl<'a> From<&'a [u8]> for Bytes {
|
||||||
|
|
|
@ -884,7 +884,6 @@ macro_rules! construct_uint {
|
||||||
impl fmt::LowerHex for $name {
|
impl fmt::LowerHex for $name {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let &$name(ref data) = self;
|
let &$name(ref data) = self;
|
||||||
try!(write!(f, "0x"));
|
|
||||||
let mut latch = false;
|
let mut latch = false;
|
||||||
for ch in data.iter().rev() {
|
for ch in data.iter().rev() {
|
||||||
for x in 0..16 {
|
for x in 0..16 {
|
||||||
|
|
|
@ -17,6 +17,7 @@ jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" }
|
||||||
jsonrpc-macros = { 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" }
|
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git" }
|
||||||
|
|
||||||
|
ethcore-devtools = { path = "../devtools" }
|
||||||
sync = { path = "../sync" }
|
sync = { path = "../sync" }
|
||||||
serialization = { path = "../serialization" }
|
serialization = { path = "../serialization" }
|
||||||
chain = { path = "../chain" }
|
chain = { path = "../chain" }
|
||||||
|
@ -26,6 +27,8 @@ network = { path = "../network" }
|
||||||
db = { path = "../db" }
|
db = { path = "../db" }
|
||||||
test-data = { path = "../test-data" }
|
test-data = { path = "../test-data" }
|
||||||
miner = { path = "../miner" }
|
miner = { path = "../miner" }
|
||||||
|
verification = { path = "../verification" }
|
||||||
|
script = { path = "../script" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
serde_codegen = { version = "0.8.0", optional = true }
|
serde_codegen = { version = "0.8.0", optional = true }
|
||||||
|
|
|
@ -20,6 +20,10 @@ extern crate db;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate test_data;
|
extern crate test_data;
|
||||||
extern crate miner;
|
extern crate miner;
|
||||||
|
extern crate verification;
|
||||||
|
#[cfg(test)]
|
||||||
|
extern crate ethcore_devtools as devtools;
|
||||||
|
extern crate script;
|
||||||
|
|
||||||
pub mod v1;
|
pub mod v1;
|
||||||
pub mod rpc_server;
|
pub mod rpc_server;
|
||||||
|
|
|
@ -3,11 +3,27 @@
|
||||||
mod codes {
|
mod codes {
|
||||||
// NOTE [ToDr] Codes from [-32099, -32000]
|
// NOTE [ToDr] Codes from [-32099, -32000]
|
||||||
pub const EXECUTION_ERROR: i64 = -32015;
|
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 std::fmt;
|
||||||
use jsonrpc_core::{Error, ErrorCode, Value};
|
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 {
|
pub fn invalid_params<T: fmt::Debug>(param: &str, details: T) -> Error {
|
||||||
Error {
|
Error {
|
||||||
code: ErrorCode::InvalidParams,
|
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))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
|
#[macro_use]
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
|
|
@ -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}"#);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
|
mod blockchain;
|
||||||
mod miner;
|
mod miner;
|
||||||
mod raw;
|
mod raw;
|
||||||
|
|
||||||
|
pub use self::blockchain::{BlockChainClient, BlockChainClientCore};
|
||||||
pub use self::miner::{MinerClient, MinerClientCore};
|
pub use self::miner::{MinerClient, MinerClientCore};
|
||||||
pub use self::raw::{RawClient, RawClientCore};
|
pub use self::raw::{RawClient, RawClientCore};
|
||||||
|
|
|
@ -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>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
|
mod blockchain;
|
||||||
mod miner;
|
mod miner;
|
||||||
mod raw;
|
mod raw;
|
||||||
|
|
||||||
|
pub use self::blockchain::BlockChain;
|
||||||
pub use self::miner::Miner;
|
pub use self::miner::Miner;
|
||||||
pub use self::raw::Raw;
|
pub use self::raw::Raw;
|
|
@ -2,6 +2,7 @@
|
||||||
use rustc_serialize::hex::{ToHex, FromHex};
|
use rustc_serialize::hex::{ToHex, FromHex};
|
||||||
use serde::{Serialize, Serializer, Deserialize, Deserializer, Error};
|
use serde::{Serialize, Serializer, Deserialize, Deserializer, Error};
|
||||||
use serde::de::Visitor;
|
use serde::de::Visitor;
|
||||||
|
use primitives::bytes::Bytes as GlobalBytes;
|
||||||
|
|
||||||
/// Wrapper structure around vector of bytes.
|
/// Wrapper structure around vector of bytes.
|
||||||
#[derive(Debug, PartialEq, Eq, Default, Hash, Clone)]
|
#[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 {
|
impl From<Vec<u8>> for Bytes {
|
||||||
fn from(bytes: Vec<u8>) -> Bytes {
|
fn from(bytes: Vec<u8>) -> Bytes {
|
||||||
Bytes(bytes)
|
Bytes(bytes)
|
||||||
|
|
|
@ -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}"#);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct GetTxOutSetInfoResponse {
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ use std::hash::{Hash, Hasher};
|
||||||
use serde;
|
use serde;
|
||||||
use rustc_serialize::hex::{ToHex, FromHex};
|
use rustc_serialize::hex::{ToHex, FromHex};
|
||||||
use primitives::hash::H256 as GlobalH256;
|
use primitives::hash::H256 as GlobalH256;
|
||||||
|
use primitives::hash::H160 as GlobalH160;
|
||||||
|
|
||||||
macro_rules! impl_hash {
|
macro_rules! impl_hash {
|
||||||
($name: ident, $other: ident, $size: expr) => {
|
($name: ident, $other: ident, $size: expr) => {
|
||||||
|
@ -127,6 +128,15 @@ macro_rules! impl_hash {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_hash!(H256, GlobalH256, 32);
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
|
@ -1,11 +1,23 @@
|
||||||
mod block_template;
|
mod block_template;
|
||||||
mod block_template_request;
|
mod block_template_request;
|
||||||
mod bytes;
|
mod bytes;
|
||||||
|
mod get_block_response;
|
||||||
|
mod get_tx_out_response;
|
||||||
|
mod get_tx_out_set_info_response;
|
||||||
mod hash;
|
mod hash;
|
||||||
|
mod raw_block;
|
||||||
mod raw_transaction;
|
mod raw_transaction;
|
||||||
|
mod script;
|
||||||
|
mod uint;
|
||||||
|
|
||||||
pub use self::block_template::{BlockTemplate, BlockTemplateTransaction};
|
pub use self::block_template::{BlockTemplate, BlockTemplateTransaction};
|
||||||
pub use self::block_template_request::{BlockTemplateRequest, BlockTemplateRequestMode};
|
pub use self::block_template_request::{BlockTemplateRequest, BlockTemplateRequestMode};
|
||||||
pub use self::bytes::Bytes;
|
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::hash::H256;
|
||||||
|
pub use self::raw_block::RawBlock;
|
||||||
pub use self::raw_transaction::RawTransaction;
|
pub use self::raw_transaction::RawTransaction;
|
||||||
|
pub use self::script::ScriptType;
|
||||||
|
pub use self::uint::U256;
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
use super::bytes::Bytes;
|
||||||
|
|
||||||
|
pub type RawBlock = Bytes;
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ pub use self::flags::VerificationFlags;
|
||||||
pub use self::interpreter::{eval_script, verify_script};
|
pub use self::interpreter::{eval_script, verify_script};
|
||||||
pub use self::opcode::Opcode;
|
pub use self::opcode::Opcode;
|
||||||
pub use self::num::Num;
|
pub use self::num::Num;
|
||||||
pub use self::script::Script;
|
pub use self::script::{Script, ScriptType};
|
||||||
pub use self::sign::{
|
pub use self::sign::{
|
||||||
TransactionInputSigner, UnsignedTransactionInput,
|
TransactionInputSigner, UnsignedTransactionInput,
|
||||||
Sighash, SighashBase, SignatureVersion
|
Sighash, SighashBase, SignatureVersion
|
||||||
|
|
|
@ -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 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_executor = SyncExecutor::new(sync_chain.clone());
|
||||||
let sync_server = Arc::new(SynchronizationServer::new(sync_chain.clone(), sync_executor.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());
|
let sync_client_core = SynchronizationClientCore::new(sync_client_config, handle, sync_executor.clone(), sync_chain.clone(), chain_verifier.clone());
|
||||||
|
|
Loading…
Reference in New Issue