updated getblocktemplate RPC

This commit is contained in:
Svyatoslav Nikolsky 2019-01-15 12:16:27 +03:00
parent e50dc7be14
commit 700ba96726
18 changed files with 144 additions and 332 deletions

1
Cargo.lock generated
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -32,7 +32,7 @@ impl FromStr for Api {
fn from_str(s: &str) -> Result<Self, Self::Err> {
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()),
}

View File

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

View File

@ -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}"#);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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),
}
}