diff --git a/stratum-core/Cargo.toml b/stratum-core/Cargo.toml index 06ece1abeb..ca07cc0b3a 100644 --- a/stratum-core/Cargo.toml +++ b/stratum-core/Cargo.toml @@ -20,7 +20,7 @@ framing_sv2 = { path = "../sv2/framing-sv2", version = "^6.0.0" } noise_sv2 = { path = "../sv2/noise-sv2", version = "^1.0.0" } parsers_sv2 = { path = "../sv2/parsers-sv2", version = "^0.2.0" } handlers_sv2 = { path = "../sv2/handlers-sv2", version = "^0.2.0" } -channels_sv2 = { path = "../sv2/channels-sv2", version = "^3.0.0" } +channels_sv2 = { path = "../sv2/channels-sv2", version = "^4.0.0" } common_messages_sv2 = { path = "../sv2/subprotocols/common-messages", version = "^7.0.0" } mining_sv2 = { path = "../sv2/subprotocols/mining", version = "^7.0.0" } template_distribution_sv2 = { path = "../sv2/subprotocols/template-distribution", version = "^5.0.0" } diff --git a/stratum-core/stratum-translation/Cargo.toml b/stratum-core/stratum-translation/Cargo.toml index 2ac885b046..e86e426d8f 100644 --- a/stratum-core/stratum-translation/Cargo.toml +++ b/stratum-core/stratum-translation/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stratum_translation" -version = "0.1.2" +version = "0.1.3" edition = "2021" license = "MIT OR Apache-2.0" description = "Stratum V1 ↔ Stratum V2 translation utilities for reuse across proxies, apps, and firmware" @@ -12,7 +12,7 @@ path = "src/lib.rs" [dependencies] binary_sv2 = { path = "../../sv2/binary-sv2", version = "^5.0.0" } mining_sv2 = { path = "../../sv2/subprotocols/mining", version = "^7.0.0" } -channels_sv2 = { path = "../../sv2/channels-sv2", version = "^3.0.0" } +channels_sv2 = { path = "../../sv2/channels-sv2", version = "^4.0.0" } v1 = { path = "../../sv1", package = "sv1_api", version = "^2.0.0" } tracing = { workspace = true } bitcoin = { workspace = true } diff --git a/sv2/channels-sv2/Cargo.toml b/sv2/channels-sv2/Cargo.toml index 5958d3aa86..0e13ddabdf 100644 --- a/sv2/channels-sv2/Cargo.toml +++ b/sv2/channels-sv2/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "channels_sv2" -version = "3.0.1" +version = "4.0.0" authors = ["The Stratum V2 Developers"] edition = "2021" readme = "README.md" diff --git a/sv2/channels-sv2/src/client/extended.rs b/sv2/channels-sv2/src/client/extended.rs index a622b316bb..57ab6b9bc2 100644 --- a/sv2/channels-sv2/src/client/extended.rs +++ b/sv2/channels-sv2/src/client/extended.rs @@ -212,6 +212,17 @@ impl<'a> ExtendedChannel<'a> { &self.share_accounting } + /// Updates share accounting based on a [`SubmitSharesSuccess`] message from the + /// upstream server. Delegates to [`ShareAccounting::on_share_acknowledgement`]. + pub fn on_share_acknowledgement( + &mut self, + new_submits_accepted_count: u32, + new_shares_sum: f64, + ) { + self.share_accounting + .on_share_acknowledgement(new_submits_accepted_count, new_shares_sum); + } + /// Handles a [`NewExtendedMiningJob`] message received from upstream. /// /// The message could be either directed at this channel, or at a group channel it belongs to. @@ -555,11 +566,8 @@ impl<'a> ExtendedChannel<'a> { // check if a block was found if network_target.is_met_by(share_hash) { - self.share_accounting.update_share_accounting( - job_target.difficulty_float(), - share.sequence_number, - share_hash.to_raw_hash(), - ); + self.share_accounting + .track_validated_share(share.sequence_number, share_hash.to_raw_hash()); return Ok(ShareValidationResult::BlockFound(share_hash.to_raw_hash())); } @@ -572,11 +580,8 @@ impl<'a> ExtendedChannel<'a> { return Err(ShareValidationError::DuplicateShare); } - self.share_accounting.update_share_accounting( - job_target.difficulty_float(), - share.sequence_number, - share_hash.to_raw_hash(), - ); + self.share_accounting + .track_validated_share(share.sequence_number, share_hash.to_raw_hash()); // update the best diff self.share_accounting.update_best_diff(share_hash_as_diff); diff --git a/sv2/channels-sv2/src/client/share_accounting.rs b/sv2/channels-sv2/src/client/share_accounting.rs index 0c53fbaaea..40605dff88 100644 --- a/sv2/channels-sv2/src/client/share_accounting.rs +++ b/sv2/channels-sv2/src/client/share_accounting.rs @@ -40,15 +40,23 @@ pub enum ShareValidationError { BadExtranonceSize, } -/// Tracks share validation state for a specific channel (Extended or Standard). +/// Tracks share validation and acceptance state for a specific channel (Extended or Standard). /// -/// Used only on Mining Clients. -/// Keeps statistics and state for shares submitted through the channel: +/// Used only on Mining Clients. Share accounting is split into two phases: +/// +/// **Validation phase** (updated by [`validate_share`] via [`track_validated_share`]): +/// - hashes of seen shares (for duplicate detection) /// - last received share's sequence number -/// - total accepted shares +/// - highest difficulty seen in validated shares +/// +/// **Acceptance phase** (updated by the application layer via [`on_share_acknowledgement`]): +/// - total accepted shares (confirmed by upstream [`SubmitSharesSuccess`]) /// - cumulative work from accepted shares -/// - hashes of seen shares (for duplicate detection) -/// - highest difficulty seen in accepted shares +/// +/// [`validate_share`]: super::extended::ExtendedChannel::validate_share +/// [`track_validated_share`]: ShareAccounting::track_validated_share +/// [`on_share_acknowledgement`]: ShareAccounting::on_share_acknowledgement +/// [`SubmitSharesSuccess`]: mining_sv2::SubmitSharesSuccess #[derive(Clone, Debug)] pub struct ShareAccounting { last_share_sequence_number: u32, @@ -76,20 +84,30 @@ impl ShareAccounting { } } - /// Updates the accounting state with a newly accepted share. + /// Updates acceptance accounting based on a [`SubmitSharesSuccess`] message from the + /// upstream server. /// - /// - Increments share count and total work. - /// - Updates last share sequence number. - /// - Records share hash to detect duplicates. - pub fn update_share_accounting( + /// This should be called by the application layer when it receives upstream confirmation + /// that shares were accepted. It is intentionally **not** called from [`validate_share`] — + /// local validation only tracks the share for duplicate detection (via + /// [`track_validated_share`]). + pub fn on_share_acknowledgement( &mut self, - share_work: f64, - share_sequence_number: u32, - share_hash: Hash, + new_submits_accepted_count: u32, + new_shares_sum: f64, ) { + self.shares_accepted += new_submits_accepted_count; + self.share_work_sum += new_shares_sum; + } + + /// Records a share that passed local validation. + /// + /// Adds the hash to the seen set for duplicate detection and updates the last sequence + /// number. Called from [`validate_share`] — does **not** count the share as accepted. + /// Acceptance accounting is deferred to [`on_share_acknowledgement`], which should be + /// called when the upstream server confirms via [`SubmitSharesSuccess`]. + pub fn track_validated_share(&mut self, share_sequence_number: u32, share_hash: Hash) { self.last_share_sequence_number = share_sequence_number; - self.shares_accepted += 1; - self.share_work_sum += share_work; self.seen_shares.insert(share_hash); } diff --git a/sv2/channels-sv2/src/client/standard.rs b/sv2/channels-sv2/src/client/standard.rs index fda1e4b638..9d7fb6dec6 100644 --- a/sv2/channels-sv2/src/client/standard.rs +++ b/sv2/channels-sv2/src/client/standard.rs @@ -168,6 +168,17 @@ impl<'a> StandardChannel<'a> { &self.share_accounting } + /// Updates share accounting based on a [`SubmitSharesSuccess`] message from the + /// upstream server. Delegates to [`ShareAccounting::on_share_acknowledgement`]. + pub fn on_share_acknowledgement( + &mut self, + new_submits_accepted_count: u32, + new_shares_sum: f64, + ) { + self.share_accounting + .on_share_acknowledgement(new_submits_accepted_count, new_shares_sum); + } + /// Handles a new group channel job by converting it into a standard job /// and activating it in this channel's context. /// @@ -336,11 +347,8 @@ impl<'a> StandardChannel<'a> { // check if a block was found if network_target.is_met_by(share_hash) { - self.share_accounting.update_share_accounting( - job_target.difficulty_float(), - share.sequence_number, - share_hash.to_raw_hash(), - ); + self.share_accounting + .track_validated_share(share.sequence_number, share_hash.to_raw_hash()); return Ok(ShareValidationResult::BlockFound(share_hash.to_raw_hash())); } @@ -353,11 +361,8 @@ impl<'a> StandardChannel<'a> { return Err(ShareValidationError::DuplicateShare); } - self.share_accounting.update_share_accounting( - job_target.difficulty_float(), - share.sequence_number, - share_hash.to_raw_hash(), - ); + self.share_accounting + .track_validated_share(share.sequence_number, share_hash.to_raw_hash()); // update the best diff self.share_accounting.update_best_diff(share_hash_as_diff);