diff --git a/Cargo.lock b/Cargo.lock index 206ca606..af1ee9d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1532,6 +1532,7 @@ dependencies = [ "chain 0.1.0", "db 0.1.0", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "keys 0.1.0", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "message 0.1.0", diff --git a/miner/src/block_assembler.rs b/miner/src/block_assembler.rs index dd5aaf7e..44b457d4 100644 --- a/miner/src/block_assembler.rs +++ b/miner/src/block_assembler.rs @@ -1,14 +1,17 @@ use std::collections::HashSet; use primitives::hash::H256; use primitives::compact::Compact; -use chain::{OutPoint, TransactionOutput, IndexedTransaction}; +use chain::{OutPoint, TransactionOutput, TransactionInput, IndexedTransaction, Transaction, + SAPLING_TX_VERSION, SAPLING_TX_VERSION_GROUP_ID}; +use keys::Address; use storage::{SharedStore, TransactionOutputProvider}; +use script::Builder; use network::ConsensusParams; use memory_pool::{MemoryPool, OrderingStrategy, Entry}; use verification::{work_required, transaction_sigops}; -const BLOCK_VERSION: u32 = 0x20000000; -const BLOCK_HEADER_SIZE: u32 = 4 + 32 + 32 + 4 + 4 + 4; +const BLOCK_VERSION: u32 = 4; +const BLOCK_HEADER_SIZE: u32 = 4 + 32 + 32 + 32 + 4 + 4 + 32 + 1344; /// Block template as described in [BIP0022](https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki#block-template-request) pub struct BlockTemplate { @@ -16,6 +19,8 @@ pub struct BlockTemplate { pub version: u32, /// The hash of previous block pub previous_header_hash: H256, + /// The hash of the final sapling root + pub final_sapling_root_hash: H256, /// The current time as seen by the server pub time: u32, /// The compressed difficulty @@ -25,7 +30,7 @@ pub struct BlockTemplate { /// Block transactions (excluding coinbase) pub transactions: Vec, /// Total funds available for the coinbase (in Satoshis) - pub coinbase_value: u64, + pub coinbase_tx: IndexedTransaction, /// Number of bytes allowed in the block pub size_limit: u32, /// Number of sigops allowed in the block @@ -115,7 +120,9 @@ impl SizePolicy { } /// Block assembler -pub struct BlockAssembler { +pub struct BlockAssembler<'a> { + /// Miner address. + pub miner_address: &'a Address, /// Maximal block size. pub max_block_size: u32, /// Maximal # of sigops in the block. @@ -245,8 +252,14 @@ impl<'a, T> Iterator for FittingTransactionsIterator<'a, T> where T: Iterator BlockTemplate { +impl<'a> BlockAssembler<'a> { + pub fn create_new_block( + &self, + store: &SharedStore, + mempool: &MemoryPool, + time: u32, + consensus: &ConsensusParams, + ) -> BlockTemplate { // get best block // take it's hash && height let best_block = store.best_block(); @@ -255,10 +268,10 @@ impl BlockAssembler { let bits = work_required(previous_header_hash.clone(), time, height, store.as_block_header_provider(), consensus); let version = BLOCK_VERSION; - // TODO: sync with ZCash RPC - need to return founder reward? let mut miner_reward = consensus.miner_reward(height); let mut transactions = Vec::new(); + let final_sapling_root_hash = Default::default(); // TODO: compute me let mempool_iter = mempool.iter(OrderingStrategy::ByTransactionScore); let tx_iter = FittingTransactionsIterator::new( store.as_transaction_output_provider(), @@ -276,14 +289,46 @@ impl BlockAssembler { transactions.push(tx); } + // prepare coinbase transaction + let mut coinbase_tx = Transaction { + overwintered: true, + version: SAPLING_TX_VERSION, + version_group_id: SAPLING_TX_VERSION_GROUP_ID, + inputs: vec![ + TransactionInput::coinbase(Builder::default() + .push_i64(height.into()) + .into_script() + .into()) + ], + outputs: vec![ + TransactionOutput { + value: miner_reward, + script_pubkey: Builder::build_p2pkh(&self.miner_address.hash).into(), + }, + ], + lock_time: 0, + expiry_height: 0, + join_split: None, + sapling: None, + }; + + // insert founder reward if required + if let Some(founder_address) = consensus.founder_address(height) { + coinbase_tx.outputs.push(TransactionOutput { + value: consensus.founder_reward(height), + script_pubkey: Builder::build_p2sh(&founder_address.hash).into(), + }); + } + BlockTemplate { version: version, previous_header_hash: previous_header_hash, + final_sapling_root_hash: final_sapling_root_hash, time: time, bits: bits, height: height, transactions: transactions, - coinbase_value: miner_reward, + coinbase_tx: coinbase_tx.into(), size_limit: self.max_block_size, sigop_limit: self.max_block_sigops, } @@ -363,6 +408,7 @@ mod tests { pool.insert_verified(chain.at(1).into()); (BlockAssembler { + miner_address: &"t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe".into(), max_block_size: 0xffffffff, max_block_sigops: 0xffffffff, }.create_new_block(&storage, &pool, 0, &consensus), hash0, hash1) diff --git a/miner/src/cpu_miner.rs b/miner/src/cpu_miner.rs deleted file mode 100644 index d336529a..00000000 --- a/miner/src/cpu_miner.rs +++ /dev/null @@ -1,206 +0,0 @@ -use byteorder::{WriteBytesExt, LittleEndian}; -use primitives::bytes::Bytes; -use primitives::hash::H256; -use primitives::bigint::{U256, Uint}; -use primitives::compact::Compact; -use chain::{merkle_root, Transaction}; -use crypto::dhash256; -use ser::Stream; -use verification::is_valid_proof_of_work_hash; -use block_assembler::BlockTemplate; - -/// Instead of serializing `BlockHeader` from scratch over and over again, -/// let's keep it serialized in memory and replace needed bytes -struct BlockHeaderBytes { - data: Bytes, -} - -impl BlockHeaderBytes { - /// Creates new instance of block header bytes. - fn new(version: u32, previous_header_hash: H256, bits: Compact) -> Self { - let merkle_root_hash = H256::default(); - let time = 0u32; - let nonce = 0u32; - - let mut stream = Stream::default(); - stream - .append(&version) - .append(&previous_header_hash) - .append(&merkle_root_hash) - .append(&time) - .append(&bits) - .append(&nonce); - - BlockHeaderBytes { - data: stream.out(), - } - } - - /// Set merkle root hash - fn set_merkle_root_hash(&mut self, hash: &H256) { - let merkle_bytes: &mut [u8] = &mut self.data[4 + 32..4 + 32 + 32]; - merkle_bytes.copy_from_slice(&**hash); - } - - /// Set block header time - fn set_time(&mut self, time: u32) { - let mut time_bytes: &mut [u8] = &mut self.data[4 + 32 + 32..]; - time_bytes.write_u32::(time).unwrap(); - } - - /// Set block header nonce - fn set_nonce(&mut self, nonce: u32) { - let mut nonce_bytes: &mut [u8] = &mut self.data[4 + 32 + 32 + 4 + 4..]; - nonce_bytes.write_u32::(nonce).unwrap(); - } - - /// Returns block header hash - fn hash(&self) -> H256 { - dhash256(&self.data) - } -} - -/// This trait should be implemented by coinbase transaction. -pub trait CoinbaseTransactionBuilder { - /// Should be used to increase number of hash possibities for miner - fn set_extranonce(&mut self, extranonce: &[u8]); - /// Returns transaction hash - fn hash(&self) -> H256; - /// Coverts transaction into raw bytes - fn finish(self) -> Transaction; -} - -/// Cpu miner solution. -pub struct Solution { - /// Block header nonce. - pub nonce: u32, - /// Coinbase transaction extra nonce (modyfiable by miner). - pub extranonce: U256, - /// Block header time. - pub time: u32, - /// Coinbase transaction (extranonce is already set). - pub coinbase_transaction: Transaction, -} - -/// Simple bitcoin cpu miner. -/// -/// First it tries to find solution by changing block header nonce. -/// Once all nonce values have been tried, it increases extranonce. -/// Once all of them have been tried (quite unlikely on cpu ;), -/// and solution still hasn't been found it returns None. -/// It's possible to also experiment with time, but I find it pointless -/// to implement on CPU. -pub fn find_solution(block: &BlockTemplate, mut coinbase_transaction_builder: T, max_extranonce: U256) -> Option where T: CoinbaseTransactionBuilder { - let mut extranonce = U256::default(); - let mut extranonce_bytes = [0u8; 32]; - - let mut header_bytes = BlockHeaderBytes::new(block.version, block.previous_header_hash.clone(), block.bits); - // update header with time - header_bytes.set_time(block.time); - - while extranonce < max_extranonce { - extranonce.to_little_endian(&mut extranonce_bytes); - // update coinbase transaction with new extranonce - coinbase_transaction_builder.set_extranonce(&extranonce_bytes); - - // recalculate merkle root hash - let coinbase_hash = coinbase_transaction_builder.hash(); - let mut merkle_tree = vec![&coinbase_hash]; - merkle_tree.extend(block.transactions.iter().map(|tx| &tx.hash)); - let merkle_root_hash = merkle_root(&merkle_tree); - - // update header with new merkle root hash - header_bytes.set_merkle_root_hash(&merkle_root_hash); - - for nonce in 0..(u32::max_value() as u64 + 1) { - // update ยง - header_bytes.set_nonce(nonce as u32); - let hash = header_bytes.hash(); - if is_valid_proof_of_work_hash(block.bits, &hash) { - let solution = Solution { - nonce: nonce as u32, - extranonce: extranonce, - time: block.time, - coinbase_transaction: coinbase_transaction_builder.finish(), - }; - - return Some(solution); - } - } - - extranonce = extranonce + 1.into(); - } - - None -} - -#[cfg(test)] -mod tests { - use primitives::bigint::{U256, Uint}; - use primitives::bytes::Bytes; - use primitives::hash::H256; - use block_assembler::BlockTemplate; - use chain::{Transaction, TransactionInput, TransactionOutput}; - use keys::AddressHash; - use script::Builder; - use super::{find_solution, CoinbaseTransactionBuilder}; - - pub struct P2shCoinbaseTransactionBuilder { - transaction: Transaction, - } - - impl P2shCoinbaseTransactionBuilder { - pub fn new(hash: &AddressHash, value: u64) -> Self { - let script_pubkey = Builder::build_p2sh(hash).into(); - - let transaction = Transaction { - version: 0, - inputs: vec![TransactionInput::coinbase(Bytes::default())], - outputs: vec![TransactionOutput { - value: value, - script_pubkey: script_pubkey, - }], - lock_time: 0, - ..Default::default() - }; - - P2shCoinbaseTransactionBuilder { - transaction: transaction, - } - } - } - - impl CoinbaseTransactionBuilder for P2shCoinbaseTransactionBuilder { - fn set_extranonce(&mut self, extranonce: &[u8]) { - self.transaction.inputs[0].script_sig = extranonce.to_vec().into(); - } - - fn hash(&self) -> H256 { - self.transaction.hash() - } - - fn finish(self) -> Transaction { - self.transaction - } - } - - #[test] - fn test_cpu_miner_low_difficulty() { - let block_template = BlockTemplate { - version: 0, - previous_header_hash: 0.into(), - time: 0, - bits: U256::max_value().into(), - height: 0, - transactions: Vec::new(), - coinbase_value: 10, - size_limit: 1000, - sigop_limit: 100 - }; - - let hash = Default::default(); - let coinbase_builder = P2shCoinbaseTransactionBuilder::new(&hash, 10); - let solution = find_solution(&block_template, coinbase_builder, U256::max_value()); - assert!(solution.is_some()); - } -} diff --git a/miner/src/lib.rs b/miner/src/lib.rs index bc7e01ee..46b59957 100644 --- a/miner/src/lib.rs +++ b/miner/src/lib.rs @@ -13,12 +13,10 @@ extern crate serialization as ser; extern crate verification; mod block_assembler; -mod cpu_miner; mod fee; mod memory_pool; pub use block_assembler::{BlockAssembler, BlockTemplate}; -pub use cpu_miner::find_solution; pub use memory_pool::{MemoryPool, HashedOutPoint, Information as MemoryPoolInformation, OrderingStrategy as MemoryPoolOrderingStrategy, DoubleSpendCheckResult, NonFinalDoubleSpendSet}; pub use fee::{transaction_fee, transaction_fee_rate}; diff --git a/network/src/consensus.rs b/network/src/consensus.rs index de966c6d..77f8cb0c 100644 --- a/network/src/consensus.rs +++ b/network/src/consensus.rs @@ -427,8 +427,8 @@ impl ConsensusParams { height >= self.sapling_height } - /// Block reward (goes to miner) at given height. - pub fn miner_reward(&self, height: u32) -> u64 { + /// Block subsidy (total block reward). + pub fn block_reward(&self, height: u32) -> u64 { let mut reward = 1_250_000_000u64; if height < self.subsidy_slow_start_interval / 2 { reward /= self.subsidy_slow_start_interval as u64; @@ -448,9 +448,18 @@ impl ConsensusParams { reward } + /// Block reward (goes to miner) at given height. + pub fn miner_reward(&self, height: u32) -> u64 { + let mut miner_reward = self.block_reward(height); + if self.founder_address(height).is_some() { + miner_reward -= self.founder_reward(height); + } + miner_reward + } + /// Founders reward (goes to founders) at given height. pub fn founder_reward(&self, height: u32) -> u64 { - self.miner_reward(height) / 5 + self.block_reward(height) / 5 } /// Address (transparent) where founders reward goes at given height. @@ -487,16 +496,16 @@ mod tests { use super::*; #[test] - fn miner_reward() { + fn block_reward() { let consensus = ConsensusParams::new(Network::Mainnet); - assert_eq!(consensus.miner_reward(1), 62_500); - assert_eq!(consensus.miner_reward(10_000), 625_062_500); - assert_eq!(consensus.miner_reward(20_000), 1_250_000_000); - assert_eq!(consensus.miner_reward(1_000_000), 625_000_000); - assert_eq!(consensus.miner_reward(2_000_000), 312_500_000); - assert_eq!(consensus.miner_reward(3_000_000), 156_250_000); - assert_eq!(consensus.miner_reward(4_000_000), 78_125_000); - assert_eq!(consensus.miner_reward(20_000_000), 149); - assert_eq!(consensus.miner_reward(30_000_000), 0); + assert_eq!(consensus.block_reward(1), 62_500); + assert_eq!(consensus.block_reward(10_000), 625_062_500); + assert_eq!(consensus.block_reward(20_000), 1_250_000_000); + assert_eq!(consensus.block_reward(1_000_000), 625_000_000); + assert_eq!(consensus.block_reward(2_000_000), 312_500_000); + assert_eq!(consensus.block_reward(3_000_000), 156_250_000); + assert_eq!(consensus.block_reward(4_000_000), 78_125_000); + assert_eq!(consensus.block_reward(20_000_000), 149); + assert_eq!(consensus.block_reward(30_000_000), 0); } } diff --git a/pzec/cli.yml b/pzec/cli.yml index 009f2716..c0a41cea 100644 --- a/pzec/cli.yml +++ b/pzec/cli.yml @@ -95,6 +95,11 @@ args: help: Non-default verification-level is applied until a block with given hash is met. takes_value: true value_name: BLOCK + - miner-address: + long: miner-address + help: Sets the address to use in pubkey scripts of freshly generated coinbase transactions. + takes_value: true + value_name: ADDRESS subcommands: - import: about: Import blocks from a Bitcoin Core database. diff --git a/pzec/commands/start.rs b/pzec/commands/start.rs index 293845e4..cc50cb7b 100644 --- a/pzec/commands/start.rs +++ b/pzec/commands/start.rs @@ -123,6 +123,7 @@ pub fn start(cfg: config::Config) -> Result<(), String> { storage: cfg.db, local_sync_node: local_sync_node, p2p_context: p2p.context().clone(), + miner_address: cfg.miner_address, }; let _rpc_server = try!(rpc::new_http(cfg.rpc_config, rpc_deps)); diff --git a/pzec/config.rs b/pzec/config.rs index 9a91a406..3d83691b 100644 --- a/pzec/config.rs +++ b/pzec/config.rs @@ -1,6 +1,7 @@ use std::net; use clap; use storage; +use keys::Address; use message::Services; use network::{Network, ConsensusParams}; use p2p::InternetProtocol; @@ -33,6 +34,7 @@ pub struct Config { pub block_notify_command: Option, pub verification_params: VerificationParameters, pub db: storage::SharedStore, + pub miner_address: Option
, } pub const DEFAULT_DB_CACHE: usize = 512; @@ -139,6 +141,11 @@ pub fn parse(matches: &clap::ArgMatches) -> Result { _ => network.default_verification_edge(), }; + let miner_address = match matches.value_of("miner-address") { + Some(s) => Some(s.parse().map_err(|_| "Invalid miner-address commmand".to_owned())?), + None => None, + }; + let config = Config { quiet: quiet, network: network, @@ -162,6 +169,7 @@ pub fn parse(matches: &clap::ArgMatches) -> Result { verification_edge: verification_edge, }, db: db, + miner_address: miner_address, }; Ok(config) diff --git a/pzec/rpc.rs b/pzec/rpc.rs index aa9c607f..069851db 100644 --- a/pzec/rpc.rs +++ b/pzec/rpc.rs @@ -4,6 +4,7 @@ use rpc_apis::{self, ApiSet}; use ethcore_rpc::{Server, start_http, MetaIoHandler, Compatibility}; use network::ConsensusParams; use std::io; +use keys::Address; use sync; use storage; use p2p; @@ -13,6 +14,7 @@ pub struct Dependencies { pub local_sync_node: sync::LocalNodeRef, pub storage: storage::SharedStore, pub p2p_context: Arc, + pub miner_address: Option
, } #[derive(Debug, PartialEq)] diff --git a/pzec/rpc_apis.rs b/pzec/rpc_apis.rs index 13404f03..9a118170 100644 --- a/pzec/rpc_apis.rs +++ b/pzec/rpc_apis.rs @@ -32,7 +32,7 @@ impl FromStr for Api { fn from_str(s: &str) -> Result { match s { "raw" => Ok(Api::Raw), - "miner" => Ok(Api::Miner), + "miner" => Ok(Api::Miner), "blockchain" => Ok(Api::BlockChain), "network" => Ok(Api::Network), api => Err(format!("Unknown api: {}", api)), @@ -54,7 +54,7 @@ pub fn setup_rpc(mut handler: MetaIoHandler<()>, apis: ApiSet, deps: Dependencie for api in apis.list_apis() { match api { Api::Raw => handler.extend_with(RawClient::new(RawClientCore::new(deps.local_sync_node.clone())).to_delegate()), - Api::Miner => handler.extend_with(MinerClient::new(MinerClientCore::new(deps.local_sync_node.clone())).to_delegate()), + Api::Miner => handler.extend_with(MinerClient::new(MinerClientCore::new(deps.local_sync_node.clone(), deps.miner_address.clone())).to_delegate()), Api::BlockChain => handler.extend_with(BlockChainClient::new(BlockChainClientCore::new(deps.consensus.clone(), deps.storage.clone())).to_delegate()), Api::Network => handler.extend_with(NetworkClient::new(NetworkClientCore::new(deps.p2p_context.clone())).to_delegate()), } diff --git a/rpc/src/v1/impls/blockchain.rs b/rpc/src/v1/impls/blockchain.rs index 3771a8ee..d528cb7d 100644 --- a/rpc/src/v1/impls/blockchain.rs +++ b/rpc/src/v1/impls/blockchain.rs @@ -585,7 +585,6 @@ pub mod tests { assert_eq!(&sample, r#"{"jsonrpc":"2.0","error":{"code":-32099,"message":"Block with given hash is not found","data":"000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd"},"id":1}"#); } - #[ignore("TODO: Needs ZCash address")] #[test] fn verbose_transaction_out_contents() { let storage = Arc::new(BlockChainDatabase::init_test_chain(vec![test_data::genesis().into(), test_data::block_h1().into()])); @@ -603,10 +602,10 @@ pub mod tests { value: 0.0005, script: TransactionOutputScript { asm: "OP_PUSHBYTES_33 0x027a46eb513588b01b37ea24303f4b628afd12cc20df789fede0921e43cad3e875\nOP_CHECKSIG\n".to_owned(), - hex: Bytes::from("4104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"), + hex: Bytes::from("21027a46eb513588b01b37ea24303f4b628afd12cc20df789fede0921e43cad3e875ac"), req_sigs: 1, script_type: ScriptType::PubKey, - addresses: vec!["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa".into()] + addresses: vec!["t1KstPVzcNEK4ZeauQ6cogoqxQBMDSiRnGr".into()] }, version: 1, coinbase: true diff --git a/rpc/src/v1/impls/miner.rs b/rpc/src/v1/impls/miner.rs index e23ae2cb..fed372a3 100644 --- a/rpc/src/v1/impls/miner.rs +++ b/rpc/src/v1/impls/miner.rs @@ -1,6 +1,8 @@ +use v1::helpers::errors::execution; use v1::traits::Miner; use v1::types::{BlockTemplate, BlockTemplateRequest}; use jsonrpc_core::Error; +use keys::Address; use sync; use miner; @@ -9,24 +11,27 @@ pub struct MinerClient { } pub trait MinerClientCoreApi: Send + Sync + 'static { - fn get_block_template(&self) -> miner::BlockTemplate; + fn get_block_template(&self) -> Option; } pub struct MinerClientCore { local_sync_node: sync::LocalNodeRef, + miner_address: Option
, } impl MinerClientCore { - pub fn new(local_sync_node: sync::LocalNodeRef) -> Self { + pub fn new(local_sync_node: sync::LocalNodeRef, miner_address: Option
) -> Self { MinerClientCore { local_sync_node: local_sync_node, + miner_address: miner_address, } } } impl MinerClientCoreApi for MinerClientCore { - fn get_block_template(&self) -> miner::BlockTemplate { - self.local_sync_node.get_block_template() + fn get_block_template(&self) -> Option { + self.miner_address.as_ref() + .map(|miner_address| self.local_sync_node.get_block_template(miner_address)) } } @@ -40,7 +45,9 @@ impl MinerClient where T: MinerClientCoreApi { impl Miner for MinerClient where T: MinerClientCoreApi { fn get_block_template(&self, _request: BlockTemplateRequest) -> Result { - Ok(self.core.get_block_template().into()) + self.core.get_block_template() + .map(Into::into) + .ok_or_else(|| execution("miner address not set")) } } @@ -57,21 +64,22 @@ pub mod tests { struct SuccessMinerClientCore; impl MinerClientCoreApi for SuccessMinerClientCore { - fn get_block_template(&self) -> miner::BlockTemplate { + fn get_block_template(&self) -> Option { let tx: chain::Transaction = "00000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a0000000000000000000101000000000000000000000000".into(); - miner::BlockTemplate { + Some(miner::BlockTemplate { version: 777, previous_header_hash: H256::from(1), + final_sapling_root_hash: H256::from(2), time: 33, bits: 44.into(), height: 55, transactions: vec![ tx.into(), ], - coinbase_value: 66, + coinbase_tx: Default::default(), size_limit: 77, sigop_limit: 88, - } + }) } } @@ -91,6 +99,6 @@ pub mod tests { // 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}"#); + assert_eq!(&sample, r#"{"jsonrpc":"2.0","result":{"bits":44,"coinbasetxn":{"data":"00000000000000000000","depends":null,"fee":null,"hash":null,"required":false,"sigops":null},"curtime":33,"finalsaplingroothash":"0000000000000000000000000000000000000000000000000000000000000000","height":55,"mintime":null,"mutable":null,"noncerange":null,"previousblockhash":"0000000000000000000000000000000000000000000000000000000000000001","sigoplimit":88,"sizelimit":77,"target":"0000000000000000000000000000000000000000000000000000000000000000","transactions":[{"data":"00000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a0000000000000000000101000000000000000000000000","depends":null,"fee":null,"hash":null,"required":false,"sigops":null}],"version":777},"id":1}"#); } } diff --git a/rpc/src/v1/types/block_template.rs b/rpc/src/v1/types/block_template.rs index 7aa8d1bd..616384b4 100644 --- a/rpc/src/v1/types/block_template.rs +++ b/rpc/src/v1/types/block_template.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use super::hash::H256; use chain; use super::transaction::RawTransaction; @@ -6,32 +5,17 @@ use miner; /// Block template as described in: /// https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki -/// https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki -/// https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes -/// https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki #[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)] pub struct BlockTemplate { - /// The preferred block version + /// The block version pub version: u32, - /// Specific block rules that are to be enforced - pub rules: Option>, - /// Set of pending, supported versionbit (BIP 9) softfork deployments - /// Keys: named softfork rules - /// Values: identifies the bit number as indicating acceptance and readiness for given key - pub vbavailable: Option>, - /// Bit mask of versionbits the server requires set in submissions - pub vbrequired: Option, - /// The hash of previous (best known) block + /// The hash of current highest block pub previousblockhash: H256, + /// The hash of the final sapling root + pub finalsaplingroothash: H256, /// Contents of non-coinbase transactions that should be included in the next block pub transactions: Vec, - /// Data that should be included in the coinbase's scriptSig content - /// Keys: ignored - /// Values: value to be included in scriptSig - pub coinbaseaux: Option>, - /// Maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis) - pub coinbasevalue: Option, - /// information for coinbase transaction + /// Information for coinbase transaction pub coinbasetxn: Option, /// The hash target pub target: H256, @@ -45,8 +29,6 @@ pub struct BlockTemplate { 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: u32, /// Compressed target of next block @@ -60,8 +42,6 @@ pub struct BlockTemplate { pub struct BlockTemplateTransaction { /// Transaction data encoded in hexadecimal pub data: RawTransaction, - /// Transaction id encoded in little-endian hexadecimal - pub txid: Option, /// Hash encoded in little-endian hexadecimal pub hash: Option, /// Transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is @@ -73,8 +53,6 @@ pub struct BlockTemplateTransaction { /// Total SigOps cost, as counted for purposes of block limits. /// If key is not present, sigop cost is unknown and clients MUST NOT assume it is zero. pub sigops: Option, - /// Total transaction weight, as counted for purposes of block limits. - pub weight: Option, /// If provided and true, this transaction must be in the final block pub required: bool, } @@ -88,7 +66,7 @@ impl From for BlockTemplate { bits: block.bits.into(), height: block.height, transactions: block.transactions.into_iter().map(Into::into).collect(), - coinbasevalue: Some(block.coinbase_value), + coinbasetxn: Some(block.coinbase_tx.into()), sizelimit: Some(block.size_limit), sigoplimit: Some(block.sigop_limit), ..Default::default() @@ -120,50 +98,42 @@ mod tests { fn block_template_transaction_serialize() { assert_eq!(serde_json::to_string(&BlockTemplateTransaction { data: Bytes("00010203".from_hex().unwrap()), - txid: None, hash: None, depends: None, fee: None, sigops: None, - weight: None, required: false, - }).unwrap(), r#"{"data":"00010203","txid":null,"hash":null,"depends":null,"fee":null,"sigops":null,"weight":null,"required":false}"#); + }).unwrap(), r#"{"data":"00010203","hash":null,"depends":null,"fee":null,"sigops":null,"required":false}"#); assert_eq!(serde_json::to_string(&BlockTemplateTransaction { data: Bytes("00010203".from_hex().unwrap()), - txid: Some(H256::from(1)), hash: Some(H256::from(2)), depends: Some(vec![1, 2]), fee: Some(100), sigops: Some(200), - weight: Some(300), required: true, - }).unwrap(), r#"{"data":"00010203","txid":"0100000000000000000000000000000000000000000000000000000000000000","hash":"0200000000000000000000000000000000000000000000000000000000000000","depends":[1,2],"fee":100,"sigops":200,"weight":300,"required":true}"#); + }).unwrap(), r#"{"data":"00010203","hash":"0200000000000000000000000000000000000000000000000000000000000000","depends":[1,2],"fee":100,"sigops":200,"required":true}"#); } #[test] fn block_template_transaction_deserialize() { assert_eq!( - serde_json::from_str::(r#"{"data":"00010203","txid":null,"hash":null,"depends":null,"fee":null,"sigops":null,"weight":null,"required":false}"#).unwrap(), + serde_json::from_str::(r#"{"data":"00010203","hash":null,"depends":null,"fee":null,"sigops":null,"required":false}"#).unwrap(), BlockTemplateTransaction { data: Bytes("00010203".from_hex().unwrap()), - txid: None, hash: None, depends: None, fee: None, sigops: None, - weight: None, required: false, }); assert_eq!( - serde_json::from_str::(r#"{"data":"00010203","txid":"0100000000000000000000000000000000000000000000000000000000000000","hash":"0200000000000000000000000000000000000000000000000000000000000000","depends":[1,2],"fee":100,"sigops":200,"weight":300,"required":true}"#).unwrap(), + serde_json::from_str::(r#"{"data":"00010203","hash":"0200000000000000000000000000000000000000000000000000000000000000","depends":[1,2],"fee":100,"sigops":200,"required":true}"#).unwrap(), BlockTemplateTransaction { data: Bytes("00010203".from_hex().unwrap()), - txid: Some(H256::from(1)), hash: Some(H256::from(2)), depends: Some(vec![1, 2]), fee: Some(100), sigops: Some(200), - weight: Some(300), required: true, }); } @@ -172,13 +142,9 @@ mod tests { fn block_template_serialize() { assert_eq!(serde_json::to_string(&BlockTemplate { version: 0, - rules: None, - vbavailable: None, - vbrequired: None, previousblockhash: H256::default(), + finalsaplingroothash: H256::default(), transactions: vec![], - coinbaseaux: None, - coinbasevalue: None, coinbasetxn: None, target: H256::default(), mintime: None, @@ -186,37 +152,28 @@ mod tests { noncerange: None, sigoplimit: None, sizelimit: None, - weightlimit: None, curtime: 100, bits: 200, height: 300, - }).unwrap(), r#"{"version":0,"rules":null,"vbavailable":null,"vbrequired":null,"previousblockhash":"0000000000000000000000000000000000000000000000000000000000000000","transactions":[],"coinbaseaux":null,"coinbasevalue":null,"coinbasetxn":null,"target":"0000000000000000000000000000000000000000000000000000000000000000","mintime":null,"mutable":null,"noncerange":null,"sigoplimit":null,"sizelimit":null,"weightlimit":null,"curtime":100,"bits":200,"height":300}"#); + }).unwrap(), r#"{"version":0,"previousblockhash":"0000000000000000000000000000000000000000000000000000000000000000","finalsaplingroothash":"0000000000000000000000000000000000000000000000000000000000000000","transactions":[],"coinbasetxn":null,"target":"0000000000000000000000000000000000000000000000000000000000000000","mintime":null,"mutable":null,"noncerange":null,"sigoplimit":null,"sizelimit":null,"curtime":100,"bits":200,"height":300}"#); assert_eq!(serde_json::to_string(&BlockTemplate { version: 0, - rules: Some(vec!["a".to_owned()]), - vbavailable: Some(vec![("b".to_owned(), 5)].into_iter().collect()), - vbrequired: Some(10), previousblockhash: H256::from(10), + finalsaplingroothash: H256::from(11), transactions: vec![BlockTemplateTransaction { data: Bytes("00010203".from_hex().unwrap()), - txid: None, hash: None, depends: None, fee: None, sigops: None, - weight: None, required: false, }], - coinbaseaux: Some(vec![("c".to_owned(), "d".to_owned())].into_iter().collect()), - coinbasevalue: Some(30), coinbasetxn: Some(BlockTemplateTransaction { data: Bytes("555555".from_hex().unwrap()), - txid: Some(H256::from(44)), hash: Some(H256::from(55)), depends: Some(vec![1]), fee: Some(300), sigops: Some(400), - weight: Some(500), required: true, }), target: H256::from(100), @@ -225,26 +182,21 @@ mod tests { noncerange: Some("00000000ffffffff".to_owned()), sigoplimit: Some(45), sizelimit: Some(449), - weightlimit: Some(523), curtime: 100, bits: 200, height: 300, - }).unwrap(), r#"{"version":0,"rules":["a"],"vbavailable":{"b":5},"vbrequired":10,"previousblockhash":"0a00000000000000000000000000000000000000000000000000000000000000","transactions":[{"data":"00010203","txid":null,"hash":null,"depends":null,"fee":null,"sigops":null,"weight":null,"required":false}],"coinbaseaux":{"c":"d"},"coinbasevalue":30,"coinbasetxn":{"data":"555555","txid":"2c00000000000000000000000000000000000000000000000000000000000000","hash":"3700000000000000000000000000000000000000000000000000000000000000","depends":[1],"fee":300,"sigops":400,"weight":500,"required":true},"target":"6400000000000000000000000000000000000000000000000000000000000000","mintime":7,"mutable":["afg"],"noncerange":"00000000ffffffff","sigoplimit":45,"sizelimit":449,"weightlimit":523,"curtime":100,"bits":200,"height":300}"#); + }).unwrap(), r#"{"version":0,"previousblockhash":"0a00000000000000000000000000000000000000000000000000000000000000","finalsaplingroothash":"0b00000000000000000000000000000000000000000000000000000000000000","transactions":[{"data":"00010203","hash":null,"depends":null,"fee":null,"sigops":null,"required":false}],"coinbasetxn":{"data":"555555","hash":"3700000000000000000000000000000000000000000000000000000000000000","depends":[1],"fee":300,"sigops":400,"required":true},"target":"6400000000000000000000000000000000000000000000000000000000000000","mintime":7,"mutable":["afg"],"noncerange":"00000000ffffffff","sigoplimit":45,"sizelimit":449,"curtime":100,"bits":200,"height":300}"#); } #[test] fn block_template_deserialize() { assert_eq!( - serde_json::from_str::(r#"{"version":0,"rules":null,"vbavailable":null,"vbrequired":null,"previousblockhash":"0000000000000000000000000000000000000000000000000000000000000000","transactions":[],"coinbaseaux":null,"coinbasevalue":null,"coinbasetxn":null,"target":"0000000000000000000000000000000000000000000000000000000000000000","mintime":null,"mutable":null,"noncerange":null,"sigoplimit":null,"sizelimit":null,"weightlimit":null,"curtime":100,"bits":200,"height":300}"#).unwrap(), + serde_json::from_str::(r#"{"version":0,"previousblockhash":"0000000000000000000000000000000000000000000000000000000000000000","finalsaplingroothash":"0000000000000000000000000000000000000000000000000000000000000000","transactions":[],"coinbasetxn":null,"target":"0000000000000000000000000000000000000000000000000000000000000000","mintime":null,"mutable":null,"noncerange":null,"sigoplimit":null,"sizelimit":null,"curtime":100,"bits":200,"height":300}"#).unwrap(), BlockTemplate { version: 0, - rules: None, - vbavailable: None, - vbrequired: None, previousblockhash: H256::default(), + finalsaplingroothash: H256::default(), transactions: vec![], - coinbaseaux: None, - coinbasevalue: None, coinbasetxn: None, target: H256::default(), mintime: None, @@ -252,39 +204,30 @@ mod tests { noncerange: None, sigoplimit: None, sizelimit: None, - weightlimit: None, curtime: 100, bits: 200, height: 300, }); assert_eq!( - serde_json::from_str::(r#"{"version":0,"rules":["a"],"vbavailable":{"b":5},"vbrequired":10,"previousblockhash":"0a00000000000000000000000000000000000000000000000000000000000000","transactions":[{"data":"00010203","txid":null,"hash":null,"depends":null,"fee":null,"sigops":null,"weight":null,"required":false}],"coinbaseaux":{"c":"d"},"coinbasevalue":30,"coinbasetxn":{"data":"555555","txid":"2c00000000000000000000000000000000000000000000000000000000000000","hash":"3700000000000000000000000000000000000000000000000000000000000000","depends":[1],"fee":300,"sigops":400,"weight":500,"required":true},"target":"6400000000000000000000000000000000000000000000000000000000000000","mintime":7,"mutable":["afg"],"noncerange":"00000000ffffffff","sigoplimit":45,"sizelimit":449,"weightlimit":523,"curtime":100,"bits":200,"height":300}"#).unwrap(), + serde_json::from_str::(r#"{"version":0,"previousblockhash":"0a00000000000000000000000000000000000000000000000000000000000000","finalsaplingroothash":"0b00000000000000000000000000000000000000000000000000000000000000","transactions":[{"data":"00010203","hash":null,"depends":null,"fee":null,"sigops":null,"required":false}],"coinbasetxn":{"data":"555555","hash":"3700000000000000000000000000000000000000000000000000000000000000","depends":[1],"fee":300,"sigops":400,"required":true},"target":"6400000000000000000000000000000000000000000000000000000000000000","mintime":7,"mutable":["afg"],"noncerange":"00000000ffffffff","sigoplimit":45,"sizelimit":449,"curtime":100,"bits":200,"height":300}"#).unwrap(), BlockTemplate { version: 0, - rules: Some(vec!["a".to_owned()]), - vbavailable: Some(vec![("b".to_owned(), 5)].into_iter().collect()), - vbrequired: Some(10), previousblockhash: H256::from(10), + finalsaplingroothash: H256::from(11), transactions: vec![BlockTemplateTransaction { data: Bytes("00010203".from_hex().unwrap()), - txid: None, hash: None, depends: None, fee: None, sigops: None, - weight: None, required: false, }], - coinbaseaux: Some(vec![("c".to_owned(), "d".to_owned())].into_iter().collect()), - coinbasevalue: Some(30), coinbasetxn: Some(BlockTemplateTransaction { data: Bytes("555555".from_hex().unwrap()), - txid: Some(H256::from(44)), hash: Some(H256::from(55)), depends: Some(vec![1]), fee: Some(300), sigops: Some(400), - weight: Some(500), required: true, }), target: H256::from(100), @@ -293,7 +236,6 @@ mod tests { noncerange: Some("00000000ffffffff".to_owned()), sigoplimit: Some(45), sizelimit: Some(449), - weightlimit: Some(523), curtime: 100, bits: 200, height: 300, diff --git a/rpc/src/v1/types/block_template_request.rs b/rpc/src/v1/types/block_template_request.rs index 0503586c..bc4d8038 100644 --- a/rpc/src/v1/types/block_template_request.rs +++ b/rpc/src/v1/types/block_template_request.rs @@ -24,8 +24,6 @@ pub struct BlockTemplateRequest { pub mode: Option, /// Capabilities, supported by client pub capabilities: Option>, - /// Softfork deployments, supported by client - pub rules: Option>, } #[cfg(test)] @@ -47,29 +45,26 @@ mod tests { #[test] fn block_template_request_serialize() { - assert_eq!(serde_json::to_string(&BlockTemplateRequest::default()).unwrap(), r#"{"mode":null,"capabilities":null,"rules":null}"#); + assert_eq!(serde_json::to_string(&BlockTemplateRequest::default()).unwrap(), r#"{"mode":null,"capabilities":null}"#); assert_eq!(serde_json::to_string(&BlockTemplateRequest { mode: Some(BlockTemplateRequestMode::Template), capabilities: Some(vec!["a".to_owned()].into_iter().collect()), - rules: Some(vec!["b".to_owned()].into_iter().collect()), - }).unwrap(), r#"{"mode":"template","capabilities":["a"],"rules":["b"]}"#); + }).unwrap(), r#"{"mode":"template","capabilities":["a"]}"#); } #[test] fn block_template_request_deserialize() { assert_eq!( - serde_json::from_str::(r#"{"mode":null,"capabilities":null,"rules":null}"#).unwrap(), + serde_json::from_str::(r#"{"mode":null,"capabilities":null}"#).unwrap(), BlockTemplateRequest { mode: None, capabilities: None, - rules: None, }); assert_eq!( - serde_json::from_str::(r#"{"mode":"template","capabilities":["a"],"rules":["b"]}"#).unwrap(), + serde_json::from_str::(r#"{"mode":"template","capabilities":["a"]}"#).unwrap(), BlockTemplateRequest { mode: Some(BlockTemplateRequestMode::Template), capabilities: Some(vec!["a".to_owned()].into_iter().collect()), - rules: Some(vec!["b".to_owned()].into_iter().collect()), }); } } diff --git a/sync/Cargo.toml b/sync/Cargo.toml index 7ed70474..ccce052d 100644 --- a/sync/Cargo.toml +++ b/sync/Cargo.toml @@ -18,6 +18,7 @@ chain = { path = "../chain" } bitcrypto = { path = "../crypto" } storage = { path = "../storage" } db = { path = "../db" } +keys = { path = "../keys" } message = { path = "../message" } miner = { path = "../miner" } p2p = { path = "../p2p" } diff --git a/sync/src/lib.rs b/sync/src/lib.rs index 17533df3..7da0ac63 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -20,6 +20,7 @@ extern crate script; extern crate serialization as ser; extern crate rand; extern crate network; +extern crate keys; mod blocks_writer; mod inbound_connection; diff --git a/sync/src/local_node.rs b/sync/src/local_node.rs index ab1dc579..df006bad 100644 --- a/sync/src/local_node.rs +++ b/sync/src/local_node.rs @@ -3,6 +3,7 @@ use parking_lot::{Mutex, Condvar}; use time; use futures::{lazy, finished}; use chain::{Transaction, IndexedTransaction, IndexedBlock}; +use keys::Address; use message::types; use miner::BlockAssembler; use network::ConsensusParams; @@ -228,10 +229,11 @@ impl LocalNode where T: TaskExecutor, U: Server, V: Client { } /// Get block template for mining - pub fn get_block_template(&self) -> BlockTemplate { + pub fn get_block_template(&self, miner_address: &Address) -> BlockTemplate { let max_block_size = self.consensus.max_block_size(); let max_block_sigops = self.consensus.max_block_sigops(); let block_assembler = BlockAssembler { + miner_address: miner_address, max_block_size: max_block_size as u32, max_block_sigops: max_block_sigops as u32, }; diff --git a/verification/src/accept_block.rs b/verification/src/accept_block.rs index 1fc73f05..296936f0 100644 --- a/verification/src/accept_block.rs +++ b/verification/src/accept_block.rs @@ -163,7 +163,7 @@ impl<'a> BlockCoinbaseMinerReward<'a> { BlockCoinbaseMinerReward { block: block, store: store, - max_reward: consensus.miner_reward(height), + max_reward: consensus.block_reward(height), } }