diff --git a/Cargo.lock b/Cargo.lock index 9a08f4b6..3cdc1dcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -791,6 +791,7 @@ dependencies = [ "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)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "miner 0.1.0", "network 0.1.0", "p2p 0.1.0", "primitives 0.1.0", diff --git a/miner/src/lib.rs b/miner/src/lib.rs index 73ef2ad1..27cd1bde 100644 --- a/miner/src/lib.rs +++ b/miner/src/lib.rs @@ -15,7 +15,7 @@ mod cpu_miner; mod fee; mod memory_pool; -pub use block_assembler::BlockAssembler; +pub use block_assembler::{BlockAssembler, BlockTemplate}; pub use cpu_miner::find_solution; pub use memory_pool::{MemoryPool, Information as MemoryPoolInformation, OrderingStrategy as MemoryPoolOrderingStrategy}; pub use fee::{transaction_fee, transaction_fee_rate}; diff --git a/pbtc/rpc_apis.rs b/pbtc/rpc_apis.rs index 5c64ad4d..af3682d6 100644 --- a/pbtc/rpc_apis.rs +++ b/pbtc/rpc_apis.rs @@ -7,6 +7,8 @@ use ethcore_rpc::Extendable; pub enum Api { /// Raw Raw, + /// Miner + Miner, } #[derive(Debug, PartialEq, Eq)] @@ -26,6 +28,7 @@ impl FromStr for Api { fn from_str(s: &str) -> Result { match s { "raw" => Ok(Api::Raw), + "miner" => Ok(Api::Miner), api => Err(format!("Unknown api: {}", api)), } } @@ -45,6 +48,7 @@ pub fn setup_rpc(server: T, apis: ApiSet, deps: Dependencies) -> for api in apis.list_apis() { match api { Api::Raw => server.add_delegate(RawClient::new(RawClientCore::new(deps.local_sync_node.clone())).to_delegate()), + Api::Miner => server.add_delegate(MinerClient::new(MinerClientCore::new(deps.local_sync_node.clone())).to_delegate()), } } server diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 00ebf876..3d677e74 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -23,6 +23,7 @@ p2p = { path = "../p2p" } network = { path = "../network" } db = { path = "../db" } test-data = { path = "../test-data" } +miner = { path = "../miner" } [build-dependencies] serde_codegen = { version = "0.8.0", optional = true } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 945bf685..e061b965 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -15,7 +15,9 @@ extern crate primitives; extern crate p2p; extern crate network; extern crate db; +#[cfg(test)] extern crate test_data; +extern crate miner; pub mod v1; pub mod rpc_server; diff --git a/rpc/src/v1/impls/miner.rs b/rpc/src/v1/impls/miner.rs new file mode 100644 index 00000000..11a16f9a --- /dev/null +++ b/rpc/src/v1/impls/miner.rs @@ -0,0 +1,96 @@ +use v1::traits::Miner; +use v1::types::{BlockTemplate, BlockTemplateRequest}; +use jsonrpc_core::Error; +use sync; +use miner; + +pub struct MinerClient { + core: T, +} + +pub trait MinerClientCoreApi: Send + Sync + 'static { + fn get_block_template(&self) -> miner::BlockTemplate; +} + +pub struct MinerClientCore { + local_sync_node: sync::LocalNodeRef, +} + +impl MinerClientCore { + pub fn new(local_sync_node: sync::LocalNodeRef) -> Self { + MinerClientCore { + local_sync_node: local_sync_node, + } + } +} + +impl MinerClientCoreApi for MinerClientCore { + fn get_block_template(&self) -> miner::BlockTemplate { + self.local_sync_node.get_block_template() + } +} + +impl MinerClient where T: MinerClientCoreApi { + pub fn new(core: T) -> Self { + MinerClient { + core: core, + } + } +} + +impl Miner for MinerClient where T: MinerClientCoreApi { + fn get_block_template(&self, _request: BlockTemplateRequest) -> Result { + Ok(self.core.get_block_template().into()) + } +} + +#[cfg(test)] +pub mod tests { + use jsonrpc_core::{IoHandler, GenericIoHandler}; + use v1::traits::Miner; + use primitives::hash::H256; + use chain; + use miner; + use super::*; + + #[derive(Default)] + struct SuccessMinerClientCore; + + impl MinerClientCoreApi for SuccessMinerClientCore { + fn get_block_template(&self) -> miner::BlockTemplate { + let tx: chain::Transaction = "00000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a0000000000000000000101000000000000000000000000".into(); + miner::BlockTemplate { + version: 777, + previous_header_hash: H256::from(1), + time: 33, + nbits: 44, + height: 55, + transactions: vec![ + tx.into(), + ], + coinbase_value: 66, + size_limit: 77, + sigop_limit: 88, + } + } + } + + #[test] + fn getblocktemplate_accepted() { + let client = MinerClient::new(SuccessMinerClientCore::default()); + let handler = IoHandler::new(); + handler.add_delegate(client.to_delegate()); + + let sample = handler.handle_request_sync(&(r#" + { + "jsonrpc": "2.0", + "method": "getblocktemplate", + "params": [{}], + "id": 1 + }"#)).unwrap(); + + // direct hash is 0100000000000000000000000000000000000000000000000000000000000000 + // but client expects reverse hash + assert_eq!(&sample, r#"{"jsonrpc":"2.0","result":{"bits":44,"coinbaseaux":null,"coinbasetxn":null,"coinbasevalue":66,"curtime":33,"height":55,"mintime":null,"mutable":null,"noncerange":null,"previousblockhash":"0000000000000000000000000000000000000000000000000000000000000001","rules":null,"sigoplimit":88,"sizelimit":77,"target":"0000000000000000000000000000000000000000000000000000000000000000","transactions":[{"data":"00000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a0000000000000000000101000000000000000000000000","depends":null,"fee":null,"hash":null,"required":false,"sigops":null,"txid":null,"weight":null}],"vbavailable":null,"vbrequired":null,"version":777,"weightlimit":null},"id":1}"#); + } +} diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index 85256508..d079c545 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -1,3 +1,5 @@ +mod miner; mod raw; +pub use self::miner::{MinerClient, MinerClientCore}; pub use self::raw::{RawClient, RawClientCore}; diff --git a/rpc/src/v1/mod.rs b/rpc/src/v1/mod.rs index 32ef8c22..e63f6d7b 100644 --- a/rpc/src/v1/mod.rs +++ b/rpc/src/v1/mod.rs @@ -5,4 +5,6 @@ pub mod traits; pub mod types; pub use self::traits::Raw; +pub use self::traits::Miner; pub use self::impls::{RawClient, RawClientCore}; +pub use self::impls::{MinerClient, MinerClientCore}; diff --git a/rpc/src/v1/traits/miner.rs b/rpc/src/v1/traits/miner.rs new file mode 100644 index 00000000..32d371e9 --- /dev/null +++ b/rpc/src/v1/traits/miner.rs @@ -0,0 +1,13 @@ +use jsonrpc_core::Error; + +use v1::helpers::auto_args::Wrap; +use v1::types::{BlockTemplate, BlockTemplateRequest}; + +build_rpc_trait! { + /// Partiy-bitcoin miner data interface. + pub trait Miner { + /// Get block template for mining. + #[rpc(name = "getblocktemplate")] + fn get_block_template(&self, BlockTemplateRequest) -> Result; + } +} diff --git a/rpc/src/v1/traits/mod.rs b/rpc/src/v1/traits/mod.rs index 16bb2fc5..6311c282 100644 --- a/rpc/src/v1/traits/mod.rs +++ b/rpc/src/v1/traits/mod.rs @@ -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; \ No newline at end of file diff --git a/rpc/src/v1/types/block_template.rs b/rpc/src/v1/types/block_template.rs index 35b86b39..cc7b598e 100644 --- a/rpc/src/v1/types/block_template.rs +++ b/rpc/src/v1/types/block_template.rs @@ -1,9 +1,8 @@ -// TODO: remove after implementing getblocktmplate RPC -#![warn(dead_code)] - use std::collections::HashMap; use super::hash::H256; use super::raw_transaction::RawTransaction; +use db; +use miner; /// Block template as described in: /// https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki @@ -43,13 +42,13 @@ pub struct BlockTemplate { /// A range of valid nonces (constant 00000000ffffffff) pub noncerange: Option, /// Limit of sigops in blocks - pub sigoplimit: Option, + pub sigoplimit: Option, /// Limit of block size pub sizelimit: Option, /// Limit of block weight pub weightlimit: Option, /// Current timestamp in seconds since epoch (Jan 1 1970 GMT) - pub curtime: i64, + pub curtime: u32, /// Compressed target of next block pub bits: u32, /// The height of the next block @@ -80,6 +79,35 @@ pub struct BlockTemplateTransaction { pub required: bool, } +impl From for BlockTemplate { + fn from(block: miner::BlockTemplate) -> Self { + BlockTemplate { + version: block.version, + previousblockhash: block.previous_header_hash.reversed().into(), + curtime: block.time, + bits: block.nbits, + height: block.height, + transactions: block.transactions.into_iter().map(Into::into).collect(), + coinbasevalue: Some(block.coinbase_value), + sizelimit: Some(block.size_limit), + sigoplimit: Some(block.sigop_limit), + ..Default::default() + } + } +} + +impl From for BlockTemplateTransaction { + fn from(transaction: db::IndexedTransaction) -> Self { + use ser::serialize; + let serialize = serialize(&transaction.transaction); + BlockTemplateTransaction { + data: RawTransaction::new(Vec::from((*serialize).clone())), + ..Default::default() + } + } +} + + #[cfg(test)] mod tests { use serde_json; diff --git a/rpc/src/v1/types/block_template_request.rs b/rpc/src/v1/types/block_template_request.rs index 2305076c..0503586c 100644 --- a/rpc/src/v1/types/block_template_request.rs +++ b/rpc/src/v1/types/block_template_request.rs @@ -1,6 +1,3 @@ -// TODO: remove after implementing getblocktmplate RPC -#![warn(dead_code)] - use std::collections::HashSet; /// Block template request mode diff --git a/sync/src/lib.rs b/sync/src/lib.rs index fa78b3ed..edc9dd2d 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -77,6 +77,7 @@ pub fn create_local_sync_node(handle: &Handle, network: Magic, db: db::SharedSto use synchronization_verifier::AsyncVerifier; let sync_client_config = SynchronizationConfig { + network: network, // during regtests, peer is providing us with bad blocks => we shouldn't close connection because of this close_connection_on_bad_block: network != Magic::Regtest, // TODO: remove me diff --git a/sync/src/local_node.rs b/sync/src/local_node.rs index 4c5e3d7f..1a0b2853 100644 --- a/sync/src/local_node.rs +++ b/sync/src/local_node.rs @@ -11,6 +11,7 @@ use synchronization_executor::{Task as SynchronizationTask, TaskExecutor as Sync use synchronization_server::{Server, SynchronizationServer}; use synchronization_verifier::{AsyncVerifier, TransactionVerificationSink}; use primitives::hash::H256; +use miner::BlockTemplate; // TODO: check messages before processing (filterload' filter is max 36000, nHashFunc is <= 50, etc) @@ -262,6 +263,11 @@ impl LocalNode where T: SynchronizationTaskExecutor + PeersCon sink_data.wait() } + pub fn get_block_template(&self) -> BlockTemplate { + let client = self.client.lock(); + client.get_block_template() + } + fn transactions_inventory(&self, inventory: &[InventoryVector]) -> Vec { inventory.iter() .filter(|item| item.inv_type == InventoryType::MessageTx) @@ -376,7 +382,7 @@ mod tests { let chain = Arc::new(RwLock::new(Chain::new(Arc::new(db::TestStorage::with_genesis_block())))); let executor = DummyTaskExecutor::new(); let server = Arc::new(DummyServer::new()); - let config = Config { threads_num: 1, close_connection_on_bad_block: true }; + let config = Config { network: Magic::Mainnet, threads_num: 1, close_connection_on_bad_block: true }; let chain_verifier = Arc::new(ChainVerifier::new(chain.read().storage(), Magic::Mainnet)); let client_core = SynchronizationClientCore::new(config, &handle, executor.clone(), chain.clone(), chain_verifier); let mut verifier = match verifier { diff --git a/sync/src/synchronization_client.rs b/sync/src/synchronization_client.rs index 437a08ee..7e64eb3d 100644 --- a/sync/src/synchronization_client.rs +++ b/sync/src/synchronization_client.rs @@ -32,6 +32,8 @@ use miner::transaction_fee_rate; use verification::ChainVerifier; use time; use std::time::Duration; +use miner::{BlockAssembler, BlockTemplate}; +use network::Magic; #[cfg_attr(feature="cargo-clippy", allow(doc_markdown))] ///! TODO: update with headers-first corrections @@ -215,6 +217,7 @@ pub trait Client : Send + 'static { fn on_peer_disconnected(&mut self, peer_index: usize); fn after_peer_nearly_blocks_verified(&mut self, peer_index: usize, future: EmptyBoxFuture); fn accept_transaction(&mut self, transaction: Transaction, sink: Box) -> Result<(), String>; + fn get_block_template(&self) -> BlockTemplate; } /// Synchronization client trait @@ -238,6 +241,7 @@ pub trait ClientCore { fn on_peer_disconnected(&mut self, peer_index: usize); fn after_peer_nearly_blocks_verified(&mut self, peer_index: usize, future: EmptyBoxFuture); fn accept_transaction(&mut self, transaction: Transaction, sink: Box) -> Result, String>; + fn get_block_template(&self) -> BlockTemplate; fn execute_synchronization_tasks(&mut self, forced_blocks_requests: Option>, final_blocks_requests: Option>); fn try_switch_to_saturated_state(&mut self) -> bool; fn on_block_verification_success(&mut self, block: IndexedBlock) -> Option>; @@ -250,6 +254,8 @@ pub trait ClientCore { /// Synchronization client configuration options. #[derive(Debug)] pub struct Config { + /// Network + pub network: Magic, /// If true, connection to peer who has provided us with bad block is closed pub close_connection_on_bad_block: bool, /// Number of threads to allocate in synchronization CpuPool. @@ -514,6 +520,10 @@ impl Client for SynchronizationClient where T: TaskExecutor, U: Veri } Ok(()) } + + fn get_block_template(&self) -> BlockTemplate { + self.core.lock().get_block_template() + } } impl SynchronizationClient where T: TaskExecutor, U: Verifier { @@ -838,6 +848,14 @@ impl ClientCore for SynchronizationClientCore where T: TaskExecutor { } } + fn get_block_template(&self) -> BlockTemplate { + let block_assembler = BlockAssembler::default(); + let chain = self.chain.read(); + let store = chain.storage(); + let memory_pool = chain.memory_pool(); + block_assembler.create_new_block(&store, memory_pool, time::get_time().sec as u32, self.config.network) + } + /// Schedule new synchronization tasks, if any. fn execute_synchronization_tasks(&mut self, forced_blocks_requests: Option>, final_blocks_requests: Option>) { let mut tasks: Vec = Vec::new(); @@ -1822,7 +1840,7 @@ pub mod tests { }; let chain = ChainRef::new(RwLock::new(Chain::new(storage.clone()))); let executor = DummyTaskExecutor::new(); - let config = Config { threads_num: 1, close_connection_on_bad_block: true }; + let config = Config { network: Magic::Mainnet, threads_num: 1, close_connection_on_bad_block: true }; let chain_verifier = Arc::new(ChainVerifier::new(storage.clone(), Magic::Testnet)); let client_core = SynchronizationClientCore::new(config, &handle, executor.clone(), chain.clone(), chain_verifier);