Skip to content

Commit b9bbd71

Browse files
authored
Mining Rule Engine Scaffolding and Sync Rate Rule implementation (#654)
* Initial implementation of Mining Rule Engine - monitor sync state and update anything that cares about is_sycned to use the updated rules - sync rate rule: allow mining if sync rate is below threshold and finality point is recent Move most is_nearly_synced logic to mining_rule_engine - instantiate Hub in daemon - Reverse the dependency of flow context and MRE - remove the async_is_nearly_synced consensus api - add get_sink_daa_score_timestamp consensus API Move is_nearly_synced logic from params to rule engine * Add MiningRules and wire it up * Implement MiningRule trait in a submodule * Implement Sync Rate Rule * Update is_synced for non-mining context - Make it not depend on sync rate rule * comment fixes * Process relay blocks if sync rate rule is triggered
1 parent 1243a04 commit b9bbd71

File tree

28 files changed

+472
-78
lines changed

28 files changed

+472
-78
lines changed

Cargo.lock

+21
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ kaspa-muhash = { version = "0.17.1", path = "crypto/muhash" }
110110
kaspa-notify = { version = "0.17.1", path = "notify" }
111111
kaspa-p2p-flows = { version = "0.17.1", path = "protocol/flows" }
112112
kaspa-p2p-lib = { version = "0.17.1", path = "protocol/p2p" }
113+
kaspa-p2p-mining = { version = "0.17.1", path = "protocol/mining" }
113114
kaspa-perf-monitor = { version = "0.17.1", path = "metrics/perf_monitor" }
114115
kaspa-pow = { version = "0.17.1", path = "consensus/pow" }
115116
kaspa-rpc-core = { version = "0.17.1", path = "rpc/core" }

components/consensusmanager/src/session.rs

+4-7
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ impl ConsensusSessionOwned {
250250
self.clone().spawn_blocking(|c| c.get_sink_timestamp()).await
251251
}
252252

253+
pub async fn async_get_sink_daa_score_timestamp(&self) -> DaaScoreTimestamp {
254+
self.clone().spawn_blocking(|c| c.get_sink_daa_score_timestamp()).await
255+
}
256+
253257
pub async fn async_get_current_block_color(&self, hash: Hash) -> Option<bool> {
254258
self.clone().spawn_blocking(move |c| c.get_current_block_color(hash)).await
255259
}
@@ -263,13 +267,6 @@ impl ConsensusSessionOwned {
263267
self.clone().spawn_blocking(|c| c.estimate_block_count()).await
264268
}
265269

266-
/// Returns whether this consensus is considered synced or close to being synced.
267-
///
268-
/// This info is used to determine if it's ok to use a block template from this node for mining purposes.
269-
pub async fn async_is_nearly_synced(&self) -> bool {
270-
self.clone().spawn_blocking(|c| c.is_nearly_synced()).await
271-
}
272-
273270
pub async fn async_get_virtual_chain_from_block(
274271
&self,
275272
low: Hash,

consensus/core/src/api/mod.rs

+4-7
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ pub trait ConsensusApi: Send + Sync {
135135
unimplemented!()
136136
}
137137

138+
fn get_sink_daa_score_timestamp(&self) -> DaaScoreTimestamp {
139+
unimplemented!()
140+
}
141+
138142
fn get_current_block_color(&self, hash: Hash) -> Option<bool> {
139143
unimplemented!()
140144
}
@@ -152,13 +156,6 @@ pub trait ConsensusApi: Send + Sync {
152156
unimplemented!()
153157
}
154158

155-
/// Returns whether this consensus is considered synced or close to being synced.
156-
///
157-
/// This info is used to determine if it's ok to use a block template from this node for mining purposes.
158-
fn is_nearly_synced(&self) -> bool {
159-
unimplemented!()
160-
}
161-
162159
/// Gets the virtual chain paths from `low` to the `sink` hash, or until `chain_path_added_limit` is reached
163160
///
164161
/// Note:

consensus/core/src/config/params.rs

+2-32
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@ use crate::{
1010
};
1111
use kaspa_addresses::Prefix;
1212
use kaspa_math::Uint256;
13-
use std::{
14-
cmp::min,
15-
time::{SystemTime, UNIX_EPOCH},
16-
};
13+
use std::cmp::min;
1714

1815
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1916
pub struct ForkActivation(u64);
@@ -260,10 +257,6 @@ pub struct Params {
260257
pub crescendo_activation: ForkActivation,
261258
}
262259

263-
fn unix_now() -> u64 {
264-
SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as u64
265-
}
266-
267260
impl Params {
268261
/// Returns the size of the full blocks window that is inspected to calculate the past median time (legacy)
269262
#[inline]
@@ -377,7 +370,7 @@ impl Params {
377370
)
378371
}
379372

380-
fn expected_difficulty_window_duration_in_milliseconds(&self) -> ForkedParam<u64> {
373+
pub fn expected_difficulty_window_duration_in_milliseconds(&self) -> ForkedParam<u64> {
381374
ForkedParam::new(
382375
self.prior_target_time_per_block * self.prior_difficulty_window_size as u64,
383376
self.crescendo.target_time_per_block
@@ -431,29 +424,6 @@ impl Params {
431424
ForkedParam::new(self.prior_max_script_public_key_len, self.crescendo.max_script_public_key_len, self.crescendo_activation)
432425
}
433426

434-
/// Returns whether the sink timestamp is recent enough and the node is considered synced or nearly synced.
435-
pub fn is_nearly_synced(&self, sink_timestamp: u64, sink_daa_score: u64) -> bool {
436-
if self.net.is_mainnet() {
437-
// We consider the node close to being synced if the sink (virtual selected parent) block
438-
// timestamp is within DAA window duration far in the past. Blocks mined over such DAG state would
439-
// enter the DAA window of fully-synced nodes and thus contribute to overall network difficulty
440-
//
441-
// [Crescendo]: both durations are nearly equal so this decision is negligible
442-
unix_now() < sink_timestamp + self.expected_difficulty_window_duration_in_milliseconds().get(sink_daa_score)
443-
} else {
444-
// For testnets we consider the node to be synced if the sink timestamp is within a time range which
445-
// is overwhelmingly unlikely to pass without mined blocks even if net hashrate decreased dramatically.
446-
//
447-
// This period is smaller than the above mainnet calculation in order to ensure that an IBDing miner
448-
// with significant testnet hashrate does not overwhelm the network with deep side-DAGs.
449-
//
450-
// We use DAA duration as baseline and scale it down with BPS (and divide by 3 for mining only when very close to current time on TN11)
451-
let max_expected_duration_without_blocks_in_milliseconds =
452-
self.prior_target_time_per_block * NEW_DIFFICULTY_WINDOW_DURATION / 3; // = DAA duration in milliseconds / bps / 3
453-
unix_now() < sink_timestamp + max_expected_duration_without_blocks_in_milliseconds
454-
}
455-
}
456-
457427
pub fn network_name(&self) -> String {
458428
self.net.to_prefixed()
459429
}

consensus/core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub mod hashing;
2727
pub mod header;
2828
pub mod mass;
2929
pub mod merkle;
30+
pub mod mining_rules;
3031
pub mod muhash;
3132
pub mod network;
3233
pub mod pruning;

consensus/core/src/mining_rules.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#[derive(Debug)]
2+
pub struct MiningRules {}
3+
4+
impl MiningRules {
5+
pub fn new() -> Self {
6+
Self {}
7+
}
8+
}
9+
10+
impl Default for MiningRules {
11+
fn default() -> Self {
12+
Self::new()
13+
}
14+
}

consensus/src/consensus/factory.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use super::utxo_set_override::{set_genesis_utxo_commitment_from_config, set_init
33
use super::{ctl::Ctl, Consensus};
44
use crate::{model::stores::U64Key, pipeline::ProcessingCounters};
55
use itertools::Itertools;
6-
use kaspa_consensus_core::config::Config;
6+
use kaspa_consensus_core::{config::Config, mining_rules::MiningRules};
77
use kaspa_consensus_notify::root::ConsensusNotificationRoot;
88
use kaspa_consensusmanager::{ConsensusFactory, ConsensusInstance, DynConsensusCtl, SessionLock};
99
use kaspa_core::{debug, time::unix_now, warn};
@@ -254,6 +254,7 @@ pub struct Factory {
254254
counters: Arc<ProcessingCounters>,
255255
tx_script_cache_counters: Arc<TxScriptCacheCounters>,
256256
fd_budget: i32,
257+
mining_rules: Arc<MiningRules>,
257258
}
258259

259260
impl Factory {
@@ -266,6 +267,7 @@ impl Factory {
266267
counters: Arc<ProcessingCounters>,
267268
tx_script_cache_counters: Arc<TxScriptCacheCounters>,
268269
fd_budget: i32,
270+
mining_rules: Arc<MiningRules>,
269271
) -> Self {
270272
assert!(fd_budget > 0, "fd_budget has to be positive");
271273
let mut config = config.clone();
@@ -283,6 +285,7 @@ impl Factory {
283285
counters,
284286
tx_script_cache_counters,
285287
fd_budget,
288+
mining_rules,
286289
};
287290
factory.delete_inactive_consensus_entries();
288291
factory
@@ -325,6 +328,7 @@ impl ConsensusFactory for Factory {
325328
self.counters.clone(),
326329
self.tx_script_cache_counters.clone(),
327330
entry.creation_timestamp,
331+
self.mining_rules.clone(),
328332
));
329333

330334
// We write the new active entry only once the instance was created successfully.
@@ -359,6 +363,7 @@ impl ConsensusFactory for Factory {
359363
self.counters.clone(),
360364
self.tx_script_cache_counters.clone(),
361365
entry.creation_timestamp,
366+
self.mining_rules.clone(),
362367
));
363368

364369
(ConsensusInstance::new(session_lock, consensus.clone()), Arc::new(Ctl::new(self.management_store.clone(), db, consensus)))

consensus/src/consensus/mod.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ use kaspa_consensus_core::{
6565
header::Header,
6666
mass::{ContextualMasses, NonContextualMasses},
6767
merkle::calc_hash_merkle_root,
68+
mining_rules::MiningRules,
6869
muhash::MuHashExtensions,
6970
network::NetworkType,
7071
pruning::{PruningPointProof, PruningPointTrustedData, PruningPointsList, PruningProofMetadata},
@@ -162,6 +163,7 @@ impl Consensus {
162163
counters: Arc<ProcessingCounters>,
163164
tx_script_cache_counters: Arc<TxScriptCacheCounters>,
164165
creation_timestamp: u64,
166+
mining_rules: Arc<MiningRules>,
165167
) -> Self {
166168
let params = &config.params;
167169
let perf_params = &config.perf;
@@ -266,6 +268,7 @@ impl Consensus {
266268
pruning_lock.clone(),
267269
notification_root.clone(),
268270
counters.clone(),
271+
mining_rules,
269272
));
270273

271274
let pruning_processor = Arc::new(PruningProcessor::new(
@@ -585,6 +588,12 @@ impl ConsensusApi for Consensus {
585588
self.headers_store.get_timestamp(self.get_sink()).unwrap()
586589
}
587590

591+
fn get_sink_daa_score_timestamp(&self) -> DaaScoreTimestamp {
592+
let sink = self.get_sink();
593+
let compact = self.headers_store.get_compact_header_data(sink).unwrap();
594+
DaaScoreTimestamp { daa_score: compact.daa_score, timestamp: compact.timestamp }
595+
}
596+
588597
fn get_current_block_color(&self, hash: Hash) -> Option<bool> {
589598
let _guard = self.pruning_lock.blocking_read();
590599

@@ -670,13 +679,6 @@ impl ConsensusApi for Consensus {
670679
BlockCount { header_count, block_count }
671680
}
672681

673-
fn is_nearly_synced(&self) -> bool {
674-
// See comment within `config.is_nearly_synced`
675-
let sink = self.get_sink();
676-
let compact = self.headers_store.get_compact_header_data(sink).unwrap();
677-
self.config.is_nearly_synced(compact.timestamp, compact.daa_score)
678-
}
679-
680682
fn get_virtual_chain_from_block(&self, low: Hash, chain_path_added_limit: Option<usize>) -> ConsensusResult<ChainPath> {
681683
// Calculate chain changes between the given `low` and the current sink hash (up to `limit` amount of block hashes).
682684
// Note:

consensus/src/consensus/test_consensus.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use async_channel::Sender;
22
use kaspa_consensus_core::coinbase::MinerData;
3+
use kaspa_consensus_core::mining_rules::MiningRules;
34
use kaspa_consensus_core::tx::ScriptPublicKey;
45
use kaspa_consensus_core::{
56
api::ConsensusApi, block::MutableBlock, blockstatus::BlockStatus, header::Header, merkle::calc_hash_merkle_root,
@@ -58,6 +59,7 @@ impl TestConsensus {
5859
counters,
5960
tx_script_cache_counters,
6061
0,
62+
Arc::new(MiningRules::default()),
6163
));
6264
let block_builder = TestBlockBuilder::new(consensus.virtual_processor.clone());
6365

@@ -78,6 +80,7 @@ impl TestConsensus {
7880
counters,
7981
tx_script_cache_counters,
8082
0,
83+
Arc::new(MiningRules::default()),
8184
));
8285
let block_builder = TestBlockBuilder::new(consensus.virtual_processor.clone());
8386

@@ -99,6 +102,7 @@ impl TestConsensus {
99102
counters,
100103
tx_script_cache_counters,
101104
0,
105+
Arc::new(MiningRules::default()),
102106
));
103107
let block_builder = TestBlockBuilder::new(consensus.virtual_processor.clone());
104108

consensus/src/pipeline/virtual_processor/processor.rs

+6
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ use kaspa_consensus_core::{
6060
},
6161
header::Header,
6262
merkle::calc_hash_merkle_root,
63+
mining_rules::MiningRules,
6364
pruning::PruningPointsList,
6465
tx::{MutableTransaction, Transaction},
6566
utxo::{
@@ -175,6 +176,9 @@ pub struct VirtualStateProcessor {
175176

176177
// Crescendo hardfork activation score (used here for activating KIPs 9,10)
177178
pub(crate) crescendo_activation: ForkActivation,
179+
180+
// Mining Rule
181+
mining_rules: Arc<MiningRules>,
178182
}
179183

180184
impl VirtualStateProcessor {
@@ -191,6 +195,7 @@ impl VirtualStateProcessor {
191195
pruning_lock: SessionLock,
192196
notification_root: Arc<ConsensusNotificationRoot>,
193197
counters: Arc<ProcessingCounters>,
198+
mining_rules: Arc<MiningRules>,
194199
) -> Self {
195200
Self {
196201
receiver,
@@ -240,6 +245,7 @@ impl VirtualStateProcessor {
240245
counters,
241246
crescendo_logger: CrescendoLogger::new(),
242247
crescendo_activation: params.crescendo_activation,
248+
mining_rules,
243249
}
244250
}
245251

kaspad/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ kaspa-index-processor.workspace = true
3131
kaspa-mining.workspace = true
3232
kaspa-notify.workspace = true
3333
kaspa-p2p-flows.workspace = true
34+
kaspa-p2p-lib.workspace = true
35+
kaspa-p2p-mining.workspace = true
3436
kaspa-perf-monitor.workspace = true
3537
kaspa-rpc-core.workspace = true
3638
kaspa-rpc-service.workspace = true

0 commit comments

Comments
 (0)