1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use super::errors::BuilderResult;
use kaspa_consensus_core::{
api::ConsensusApi,
block::{BlockTemplate, TemplateBuildMode, TemplateTransactionSelector},
coinbase::MinerData,
tx::COINBASE_TRANSACTION_INDEX,
};
use kaspa_core::time::{unix_now, Stopwatch};
pub(crate) struct BlockTemplateBuilder {}
impl BlockTemplateBuilder {
pub(crate) fn new() -> Self {
Self {}
}
/// BuildBlockTemplate creates a block template for a miner to consume
/// BuildBlockTemplate returns a new block template that is ready to be solved
/// using the transactions from the passed transaction source pool and a coinbase
/// that either pays to the passed address if it is not nil, or a coinbase that
/// is redeemable by anyone if the passed address is nil. The nil address
/// functionality is useful since there are cases such as the get_block_template
/// RPC where external mining software is responsible for creating their own
/// coinbase which will replace the one generated for the block template. Thus
/// the need to have configured address can be avoided.
///
/// The transactions selected and included are prioritized according to several
/// factors. First, each transaction has a priority calculated based on its
/// value, age of inputs, and size. Transactions which consist of larger
/// amounts, older inputs, and small sizes have the highest priority. Second, a
/// fee per kilobyte is calculated for each transaction. Transactions with a
/// higher fee per kilobyte are preferred. Finally, the block generation related
/// policy settings are all taken into account.
///
/// Transactions which only spend outputs from other transactions already in the
/// block DAG are immediately added to a priority queue which either
/// prioritizes based on the priority (then fee per kilobyte) or the fee per
/// kilobyte (then priority) depending on whether or not the BlockPrioritySize
/// policy setting allots space for high-priority transactions. Transactions
/// which spend outputs from other transactions in the source pool are added to a
/// dependency map so they can be added to the priority queue once the
/// transactions they depend on have been included.
///
/// Once the high-priority area (if configured) has been filled with
/// transactions, or the priority falls below what is considered high-priority,
/// the priority queue is updated to prioritize by fees per kilobyte (then
/// priority).
///
/// When the fees per kilobyte drop below the TxMinFreeFee policy setting, the
/// transaction will be skipped unless the BlockMinSize policy setting is
/// nonzero, in which case the block will be filled with the low-fee/free
/// transactions until the block size reaches that minimum size.
///
/// Any transactions which would cause the block to exceed the BlockMaxMass
/// policy setting, exceed the maximum allowed signature operations per block, or
/// otherwise cause the block to be invalid are skipped.
///
/// Given the above, a block generated by this function is of the following form:
///
/// ----------------------------------- -- --
/// | Coinbase Transaction | | |
/// |-----------------------------------| | |
/// | | | | ----- policy.BlockPrioritySize
/// | High-priority Transactions | | |
/// | | | |
/// |-----------------------------------| | --
/// | | |
/// | | |
/// | | |--- policy.BlockMaxMass
/// | Transactions prioritized by fee | |
/// | until <= policy.TxMinFreeFee | |
/// | | |
/// | | |
/// | | |
/// |-----------------------------------| |
/// | Low-fee/Non high-priority (free) | |
/// | transactions (while block size | |
/// | <= policy.BlockMinSize) | |
/// ----------------------------------- --
pub(crate) fn build_block_template(
&self,
consensus: &dyn ConsensusApi,
miner_data: &MinerData,
selector: Box<dyn TemplateTransactionSelector>,
build_mode: TemplateBuildMode,
) -> BuilderResult<BlockTemplate> {
let _sw = Stopwatch::<20>::with_threshold("build_block_template op");
Ok(consensus.build_block_template(miner_data.clone(), selector, build_mode)?)
}
/// modify_block_template clones an existing block template, modifies it to the requested coinbase data and updates the timestamp
pub(crate) fn modify_block_template(
consensus: &dyn ConsensusApi,
new_miner_data: &MinerData,
block_template_to_modify: &BlockTemplate,
) -> BuilderResult<BlockTemplate> {
let mut block_template = block_template_to_modify.clone();
// The first transaction is always the coinbase transaction
let coinbase_tx = &mut block_template.block.transactions[COINBASE_TRANSACTION_INDEX];
let new_payload = consensus.modify_coinbase_payload(coinbase_tx.payload.clone(), new_miner_data)?;
coinbase_tx.payload = new_payload;
if block_template.coinbase_has_red_reward {
// The last output is always the coinbase red blocks reward
coinbase_tx.outputs.last_mut().unwrap().script_public_key = new_miner_data.script_public_key.clone();
}
// Update the hash merkle root according to the modified transactions
block_template.block.header.hash_merkle_root =
consensus.calc_transaction_hash_merkle_root(&block_template.block.transactions, block_template.block.header.daa_score);
let new_timestamp = unix_now();
if new_timestamp > block_template.block.header.timestamp {
// Only if new time stamp is later than current, update the header. Otherwise,
// we keep the previous time as built by internal consensus median time logic
block_template.block.header.timestamp = new_timestamp;
}
block_template.block.header.finalize();
block_template.miner_data = new_miner_data.clone();
Ok(block_template)
}
}