updated getblocktemplate RPC
This commit is contained in:
parent
e50dc7be14
commit
700ba96726
|
@ -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",
|
||||
|
|
|
@ -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<IndexedTransaction>,
|
||||
/// 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<It
|
|||
}
|
||||
}
|
||||
|
||||
impl BlockAssembler {
|
||||
pub fn create_new_block(&self, store: &SharedStore, mempool: &MemoryPool, time: u32, consensus: &ConsensusParams) -> 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)
|
||||
|
|
|
@ -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::<LittleEndian>(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::<LittleEndian>(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<T>(block: &BlockTemplate, mut coinbase_transaction_builder: T, max_extranonce: U256) -> Option<Solution> 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());
|
||||
}
|
||||
}
|
|
@ -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};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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<String>,
|
||||
pub verification_params: VerificationParameters,
|
||||
pub db: storage::SharedStore,
|
||||
pub miner_address: Option<Address>,
|
||||
}
|
||||
|
||||
pub const DEFAULT_DB_CACHE: usize = 512;
|
||||
|
@ -139,6 +141,11 @@ pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> {
|
|||
_ => 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<Config, String> {
|
|||
verification_edge: verification_edge,
|
||||
},
|
||||
db: db,
|
||||
miner_address: miner_address,
|
||||
};
|
||||
|
||||
Ok(config)
|
||||
|
|
|
@ -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<p2p::Context>,
|
||||
pub miner_address: Option<Address>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
|
|
@ -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()),
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<T: MinerClientCoreApi> {
|
|||
}
|
||||
|
||||
pub trait MinerClientCoreApi: Send + Sync + 'static {
|
||||
fn get_block_template(&self) -> miner::BlockTemplate;
|
||||
fn get_block_template(&self) -> Option<miner::BlockTemplate>;
|
||||
}
|
||||
|
||||
pub struct MinerClientCore {
|
||||
local_sync_node: sync::LocalNodeRef,
|
||||
miner_address: Option<Address>,
|
||||
}
|
||||
|
||||
impl MinerClientCore {
|
||||
pub fn new(local_sync_node: sync::LocalNodeRef) -> Self {
|
||||
pub fn new(local_sync_node: sync::LocalNodeRef, miner_address: Option<Address>) -> 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<miner::BlockTemplate> {
|
||||
self.miner_address.as_ref()
|
||||
.map(|miner_address| self.local_sync_node.get_block_template(miner_address))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,7 +45,9 @@ impl<T> MinerClient<T> where T: MinerClientCoreApi {
|
|||
|
||||
impl<T> Miner for MinerClient<T> where T: MinerClientCoreApi {
|
||||
fn get_block_template(&self, _request: BlockTemplateRequest) -> Result<BlockTemplate, Error> {
|
||||
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<miner::BlockTemplate> {
|
||||
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}"#);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Vec<String>>,
|
||||
/// 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<HashMap<String, u32>>,
|
||||
/// Bit mask of versionbits the server requires set in submissions
|
||||
pub vbrequired: Option<u32>,
|
||||
/// 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<BlockTemplateTransaction>,
|
||||
/// Data that should be included in the coinbase's scriptSig content
|
||||
/// Keys: ignored
|
||||
/// Values: value to be included in scriptSig
|
||||
pub coinbaseaux: Option<HashMap<String, String>>,
|
||||
/// Maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis)
|
||||
pub coinbasevalue: Option<u64>,
|
||||
/// information for coinbase transaction
|
||||
/// Information for coinbase transaction
|
||||
pub coinbasetxn: Option<BlockTemplateTransaction>,
|
||||
/// The hash target
|
||||
pub target: H256,
|
||||
|
@ -45,8 +29,6 @@ pub struct BlockTemplate {
|
|||
pub sigoplimit: Option<u32>,
|
||||
/// Limit of block size
|
||||
pub sizelimit: Option<u32>,
|
||||
/// Limit of block weight
|
||||
pub weightlimit: Option<u32>,
|
||||
/// 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<H256>,
|
||||
/// Hash encoded in little-endian hexadecimal
|
||||
pub hash: Option<H256>,
|
||||
/// 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<i64>,
|
||||
/// Total transaction weight, as counted for purposes of block limits.
|
||||
pub weight: Option<i64>,
|
||||
/// If provided and true, this transaction must be in the final block
|
||||
pub required: bool,
|
||||
}
|
||||
|
@ -88,7 +66,7 @@ impl From<miner::BlockTemplate> 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::<BlockTemplateTransaction>(r#"{"data":"00010203","txid":null,"hash":null,"depends":null,"fee":null,"sigops":null,"weight":null,"required":false}"#).unwrap(),
|
||||
serde_json::from_str::<BlockTemplateTransaction>(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::<BlockTemplateTransaction>(r#"{"data":"00010203","txid":"0100000000000000000000000000000000000000000000000000000000000000","hash":"0200000000000000000000000000000000000000000000000000000000000000","depends":[1,2],"fee":100,"sigops":200,"weight":300,"required":true}"#).unwrap(),
|
||||
serde_json::from_str::<BlockTemplateTransaction>(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::<BlockTemplate>(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::<BlockTemplate>(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::<BlockTemplate>(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::<BlockTemplate>(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,
|
||||
|
|
|
@ -24,8 +24,6 @@ pub struct BlockTemplateRequest {
|
|||
pub mode: Option<BlockTemplateRequestMode>,
|
||||
/// Capabilities, supported by client
|
||||
pub capabilities: Option<HashSet<String>>,
|
||||
/// Softfork deployments, supported by client
|
||||
pub rules: Option<HashSet<String>>,
|
||||
}
|
||||
|
||||
#[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::<BlockTemplateRequest>(r#"{"mode":null,"capabilities":null,"rules":null}"#).unwrap(),
|
||||
serde_json::from_str::<BlockTemplateRequest>(r#"{"mode":null,"capabilities":null}"#).unwrap(),
|
||||
BlockTemplateRequest {
|
||||
mode: None,
|
||||
capabilities: None,
|
||||
rules: None,
|
||||
});
|
||||
assert_eq!(
|
||||
serde_json::from_str::<BlockTemplateRequest>(r#"{"mode":"template","capabilities":["a"],"rules":["b"]}"#).unwrap(),
|
||||
serde_json::from_str::<BlockTemplateRequest>(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()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<T, U, V> LocalNode<T, U, V> 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,
|
||||
};
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue