Skip to content

Commit dda2a47

Browse files
authored
Merge pull request #2090 from plebhash/2026-02-18-fix-client-share-accounting-update
defer `channels_sv2::client` share acceptance to upstream acknowledgement
2 parents cac1059 + bebe82f commit dda2a47

File tree

6 files changed

+68
-40
lines changed

6 files changed

+68
-40
lines changed

stratum-core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ framing_sv2 = { path = "../sv2/framing-sv2", version = "^6.0.0" }
2020
noise_sv2 = { path = "../sv2/noise-sv2", version = "^1.0.0" }
2121
parsers_sv2 = { path = "../sv2/parsers-sv2", version = "^0.2.0" }
2222
handlers_sv2 = { path = "../sv2/handlers-sv2", version = "^0.2.0" }
23-
channels_sv2 = { path = "../sv2/channels-sv2", version = "^3.0.0" }
23+
channels_sv2 = { path = "../sv2/channels-sv2", version = "^4.0.0" }
2424
common_messages_sv2 = { path = "../sv2/subprotocols/common-messages", version = "^7.0.0" }
2525
mining_sv2 = { path = "../sv2/subprotocols/mining", version = "^7.0.0" }
2626
template_distribution_sv2 = { path = "../sv2/subprotocols/template-distribution", version = "^5.0.0" }

stratum-core/stratum-translation/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "stratum_translation"
3-
version = "0.1.2"
3+
version = "0.1.3"
44
edition = "2021"
55
license = "MIT OR Apache-2.0"
66
description = "Stratum V1 ↔ Stratum V2 translation utilities for reuse across proxies, apps, and firmware"
@@ -12,7 +12,7 @@ path = "src/lib.rs"
1212
[dependencies]
1313
binary_sv2 = { path = "../../sv2/binary-sv2", version = "^5.0.0" }
1414
mining_sv2 = { path = "../../sv2/subprotocols/mining", version = "^7.0.0" }
15-
channels_sv2 = { path = "../../sv2/channels-sv2", version = "^3.0.0" }
15+
channels_sv2 = { path = "../../sv2/channels-sv2", version = "^4.0.0" }
1616
v1 = { path = "../../sv1", package = "sv1_api", version = "^2.0.0" }
1717
tracing = { workspace = true }
1818
bitcoin = { workspace = true }

sv2/channels-sv2/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "channels_sv2"
3-
version = "3.0.1"
3+
version = "4.0.0"
44
authors = ["The Stratum V2 Developers"]
55
edition = "2021"
66
readme = "README.md"

sv2/channels-sv2/src/client/extended.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,17 @@ impl<'a> ExtendedChannel<'a> {
212212
&self.share_accounting
213213
}
214214

215+
/// Updates share accounting based on a [`SubmitSharesSuccess`] message from the
216+
/// upstream server. Delegates to [`ShareAccounting::on_share_acknowledgement`].
217+
pub fn on_share_acknowledgement(
218+
&mut self,
219+
new_submits_accepted_count: u32,
220+
new_shares_sum: f64,
221+
) {
222+
self.share_accounting
223+
.on_share_acknowledgement(new_submits_accepted_count, new_shares_sum);
224+
}
225+
215226
/// Handles a [`NewExtendedMiningJob`] message received from upstream.
216227
///
217228
/// 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> {
555566

556567
// check if a block was found
557568
if network_target.is_met_by(share_hash) {
558-
self.share_accounting.update_share_accounting(
559-
job_target.difficulty_float(),
560-
share.sequence_number,
561-
share_hash.to_raw_hash(),
562-
);
569+
self.share_accounting
570+
.track_validated_share(share.sequence_number, share_hash.to_raw_hash());
563571
return Ok(ShareValidationResult::BlockFound(share_hash.to_raw_hash()));
564572
}
565573

@@ -572,11 +580,8 @@ impl<'a> ExtendedChannel<'a> {
572580
return Err(ShareValidationError::DuplicateShare);
573581
}
574582

575-
self.share_accounting.update_share_accounting(
576-
job_target.difficulty_float(),
577-
share.sequence_number,
578-
share_hash.to_raw_hash(),
579-
);
583+
self.share_accounting
584+
.track_validated_share(share.sequence_number, share_hash.to_raw_hash());
580585

581586
// update the best diff
582587
self.share_accounting.update_best_diff(share_hash_as_diff);

sv2/channels-sv2/src/client/share_accounting.rs

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,23 @@ pub enum ShareValidationError {
4040
BadExtranonceSize,
4141
}
4242

43-
/// Tracks share validation state for a specific channel (Extended or Standard).
43+
/// Tracks share validation and acceptance state for a specific channel (Extended or Standard).
4444
///
45-
/// Used only on Mining Clients.
46-
/// Keeps statistics and state for shares submitted through the channel:
45+
/// Used only on Mining Clients. Share accounting is split into two phases:
46+
///
47+
/// **Validation phase** (updated by [`validate_share`] via [`track_validated_share`]):
48+
/// - hashes of seen shares (for duplicate detection)
4749
/// - last received share's sequence number
48-
/// - total accepted shares
50+
/// - highest difficulty seen in validated shares
51+
///
52+
/// **Acceptance phase** (updated by the application layer via [`on_share_acknowledgement`]):
53+
/// - total accepted shares (confirmed by upstream [`SubmitSharesSuccess`])
4954
/// - cumulative work from accepted shares
50-
/// - hashes of seen shares (for duplicate detection)
51-
/// - highest difficulty seen in accepted shares
55+
///
56+
/// [`validate_share`]: super::extended::ExtendedChannel::validate_share
57+
/// [`track_validated_share`]: ShareAccounting::track_validated_share
58+
/// [`on_share_acknowledgement`]: ShareAccounting::on_share_acknowledgement
59+
/// [`SubmitSharesSuccess`]: mining_sv2::SubmitSharesSuccess
5260
#[derive(Clone, Debug)]
5361
pub struct ShareAccounting {
5462
last_share_sequence_number: u32,
@@ -76,20 +84,30 @@ impl ShareAccounting {
7684
}
7785
}
7886

79-
/// Updates the accounting state with a newly accepted share.
87+
/// Updates acceptance accounting based on a [`SubmitSharesSuccess`] message from the
88+
/// upstream server.
8089
///
81-
/// - Increments share count and total work.
82-
/// - Updates last share sequence number.
83-
/// - Records share hash to detect duplicates.
84-
pub fn update_share_accounting(
90+
/// This should be called by the application layer when it receives upstream confirmation
91+
/// that shares were accepted. It is intentionally **not** called from [`validate_share`] —
92+
/// local validation only tracks the share for duplicate detection (via
93+
/// [`track_validated_share`]).
94+
pub fn on_share_acknowledgement(
8595
&mut self,
86-
share_work: f64,
87-
share_sequence_number: u32,
88-
share_hash: Hash,
96+
new_submits_accepted_count: u32,
97+
new_shares_sum: f64,
8998
) {
99+
self.shares_accepted += new_submits_accepted_count;
100+
self.share_work_sum += new_shares_sum;
101+
}
102+
103+
/// Records a share that passed local validation.
104+
///
105+
/// Adds the hash to the seen set for duplicate detection and updates the last sequence
106+
/// number. Called from [`validate_share`] — does **not** count the share as accepted.
107+
/// Acceptance accounting is deferred to [`on_share_acknowledgement`], which should be
108+
/// called when the upstream server confirms via [`SubmitSharesSuccess`].
109+
pub fn track_validated_share(&mut self, share_sequence_number: u32, share_hash: Hash) {
90110
self.last_share_sequence_number = share_sequence_number;
91-
self.shares_accepted += 1;
92-
self.share_work_sum += share_work;
93111
self.seen_shares.insert(share_hash);
94112
}
95113

sv2/channels-sv2/src/client/standard.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,17 @@ impl<'a> StandardChannel<'a> {
168168
&self.share_accounting
169169
}
170170

171+
/// Updates share accounting based on a [`SubmitSharesSuccess`] message from the
172+
/// upstream server. Delegates to [`ShareAccounting::on_share_acknowledgement`].
173+
pub fn on_share_acknowledgement(
174+
&mut self,
175+
new_submits_accepted_count: u32,
176+
new_shares_sum: f64,
177+
) {
178+
self.share_accounting
179+
.on_share_acknowledgement(new_submits_accepted_count, new_shares_sum);
180+
}
181+
171182
/// Handles a new group channel job by converting it into a standard job
172183
/// and activating it in this channel's context.
173184
///
@@ -336,11 +347,8 @@ impl<'a> StandardChannel<'a> {
336347

337348
// check if a block was found
338349
if network_target.is_met_by(share_hash) {
339-
self.share_accounting.update_share_accounting(
340-
job_target.difficulty_float(),
341-
share.sequence_number,
342-
share_hash.to_raw_hash(),
343-
);
350+
self.share_accounting
351+
.track_validated_share(share.sequence_number, share_hash.to_raw_hash());
344352
return Ok(ShareValidationResult::BlockFound(share_hash.to_raw_hash()));
345353
}
346354

@@ -353,11 +361,8 @@ impl<'a> StandardChannel<'a> {
353361
return Err(ShareValidationError::DuplicateShare);
354362
}
355363

356-
self.share_accounting.update_share_accounting(
357-
job_target.difficulty_float(),
358-
share.sequence_number,
359-
share_hash.to_raw_hash(),
360-
);
364+
self.share_accounting
365+
.track_validated_share(share.sequence_number, share_hash.to_raw_hash());
361366

362367
// update the best diff
363368
self.share_accounting.update_best_diff(share_hash_as_diff);

0 commit comments

Comments
 (0)