diff --git a/Cargo.lock b/Cargo.lock index ce59fd8f98..eae89f6c4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -286,7 +286,7 @@ dependencies = [ [[package]] name = "channels_sv2" -version = "5.0.0" +version = "6.0.0" dependencies = [ "binary_sv2", "bitcoin", @@ -338,7 +338,7 @@ dependencies = [ [[package]] name = "common_messages_sv2" -version = "7.1.0" +version = "7.2.0" dependencies = [ "binary_sv2", "quickcheck", @@ -606,7 +606,7 @@ checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] name = "handlers_sv2" -version = "0.3.0" +version = "0.4.0" dependencies = [ "binary_sv2", "common_messages_sv2", @@ -734,7 +734,7 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "job_declaration_sv2" -version = "7.0.0" +version = "7.1.0" dependencies = [ "binary_sv2", ] @@ -788,7 +788,7 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mining_sv2" -version = "9.0.0" +version = "10.0.0" dependencies = [ "binary_sv2", "quickcheck", @@ -866,7 +866,7 @@ dependencies = [ [[package]] name = "parsers_sv2" -version = "0.3.0" +version = "0.4.0" dependencies = [ "binary_sv2", "codec_sv2", @@ -1235,7 +1235,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stratum-core" -version = "0.3.0" +version = "0.4.0" dependencies = [ "binary_sv2", "bitcoin", @@ -1257,7 +1257,7 @@ dependencies = [ [[package]] name = "stratum_translation" -version = "0.2.0" +version = "0.3.0" dependencies = [ "binary_sv2", "bitcoin", @@ -1317,7 +1317,7 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "template_distribution_sv2" -version = "5.0.0" +version = "5.1.0" dependencies = [ "binary_sv2", "quickcheck", diff --git a/stratum-core/Cargo.toml b/stratum-core/Cargo.toml index 2d0984b4a4..c491ac440a 100644 --- a/stratum-core/Cargo.toml +++ b/stratum-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stratum-core" -version = "0.3.0" +version = "0.4.0" authors = ["The Stratum V2 Developers"] edition = "2021" readme = "README.md" @@ -18,15 +18,15 @@ codec_sv2 = { path = "../sv2/codec-sv2", version = "^5.0.0", features = ["noise_ extensions_sv2 = { path = "../sv2/extensions-sv2", version = "^0.1.0" } 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.3.0" } -handlers_sv2 = { path = "../sv2/handlers-sv2", version = "^0.3.0" } -channels_sv2 = { path = "../sv2/channels-sv2", version = "^5.0.0" } -common_messages_sv2 = { path = "../sv2/subprotocols/common-messages", version = "^7.0.0" } -mining_sv2 = { path = "../sv2/subprotocols/mining", version = "^9.0.0" } -template_distribution_sv2 = { path = "../sv2/subprotocols/template-distribution", version = "^5.0.0" } -job_declaration_sv2 = { path = "../sv2/subprotocols/job-declaration", version = "^7.0.0" } +parsers_sv2 = { path = "../sv2/parsers-sv2", version = "^0.4.0" } +handlers_sv2 = { path = "../sv2/handlers-sv2", version = "^0.4.0" } +channels_sv2 = { path = "../sv2/channels-sv2", version = "^6.0.0" } +common_messages_sv2 = { path = "../sv2/subprotocols/common-messages", version = "^7.2.0" } +mining_sv2 = { path = "../sv2/subprotocols/mining", version = "^10.0.0" } +template_distribution_sv2 = { path = "../sv2/subprotocols/template-distribution", version = "^5.1.0" } +job_declaration_sv2 = { path = "../sv2/subprotocols/job-declaration", version = "^7.1.0" } sv1_api = { path = "../sv1", version = "^4.0.0", optional = true } -stratum_translation = { path = "stratum-translation", version = "^0.2.0", optional = true } +stratum_translation = { path = "stratum-translation", version = "^0.3.0", optional = true } bitcoin = { workspace = true } [features] diff --git a/stratum-core/stratum-translation/Cargo.toml b/stratum-core/stratum-translation/Cargo.toml index 49a0fe891f..c29a4bc7c3 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.2.0" +version = "0.3.0" edition = "2021" license = "MIT OR Apache-2.0" description = "Stratum V1 ↔ Stratum V2 translation utilities for reuse across proxies, apps, and firmware" @@ -11,8 +11,8 @@ path = "src/lib.rs" [dependencies] binary_sv2 = { path = "../../sv2/binary-sv2", version = "^5.0.0" } -mining_sv2 = { path = "../../sv2/subprotocols/mining", version = "^9.0.0" } -channels_sv2 = { path = "../../sv2/channels-sv2", version = "^5.0.0" } +mining_sv2 = { path = "../../sv2/subprotocols/mining", version = "^10.0.0" } +channels_sv2 = { path = "../../sv2/channels-sv2", version = "^6.0.0" } v1 = { path = "../../sv1", package = "sv1_api", version = "^4.0.0" } tracing = { workspace = true } bitcoin = { workspace = true } diff --git a/sv2/channels-sv2/Cargo.toml b/sv2/channels-sv2/Cargo.toml index 1c22cd1e15..8a96f7e34e 100644 --- a/sv2/channels-sv2/Cargo.toml +++ b/sv2/channels-sv2/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "channels_sv2" -version = "5.0.0" +version = "6.0.0" authors = ["The Stratum V2 Developers"] edition = "2021" readme = "README.md" @@ -15,8 +15,8 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] [dependencies] binary_sv2 = { path = "../binary-sv2", version = "^5.0.0" } -mining_sv2 = { path = "../subprotocols/mining", version = "^9.0.0" } -template_distribution_sv2 = { path = "../subprotocols/template-distribution", version = "^5.0.0" } +mining_sv2 = { path = "../subprotocols/mining", version = "^10.0.0" } +template_distribution_sv2 = { path = "../subprotocols/template-distribution", version = "^5.1.0" } tracing = { workspace = true } bitcoin = { workspace = true } primitive-types = { workspace = true } diff --git a/sv2/channels-sv2/src/client/extended.rs b/sv2/channels-sv2/src/client/extended.rs index 382b312b3b..d8eadf2736 100644 --- a/sv2/channels-sv2/src/client/extended.rs +++ b/sv2/channels-sv2/src/client/extended.rs @@ -30,6 +30,10 @@ use bitcoin::{ use mining_sv2::{ NewExtendedMiningJob, SetCustomMiningJob, SetCustomMiningJobSuccess, SetNewPrevHash as SetNewPrevHashMp, SubmitSharesExtended, + ERROR_CODE_SUBMIT_SHARES_BAD_EXTRANONCE_SIZE, ERROR_CODE_SUBMIT_SHARES_DIFFICULTY_TOO_LOW, + ERROR_CODE_SUBMIT_SHARES_DUPLICATE_SHARE, ERROR_CODE_SUBMIT_SHARES_INVALID_JOB_ID, + ERROR_CODE_SUBMIT_SHARES_INVALID_SHARE, ERROR_CODE_SUBMIT_SHARES_STALE_SHARE, + ERROR_CODE_VERSION_ROLLING_NOT_ALLOWED, }; use tracing::debug; @@ -502,7 +506,9 @@ impl<'a> ExtendedChannel<'a> { let is_stale_job = self.stale_jobs.contains_key(&job_id); if is_stale_job { - return Err(ShareValidationError::Stale); + return Err(ShareValidationError::Stale( + ERROR_CODE_SUBMIT_SHARES_STALE_SHARE, + )); } let job = if is_active_job { @@ -510,12 +516,16 @@ impl<'a> ExtendedChannel<'a> { } else if is_past_job { self.past_jobs.get(&job_id).expect("past job must exist") } else { - return Err(ShareValidationError::InvalidJobId); + return Err(ShareValidationError::InvalidJobId( + ERROR_CODE_SUBMIT_SHARES_INVALID_JOB_ID, + )); }; let extranonce_size = share.extranonce.inner_as_ref().len(); if extranonce_size != self.rollable_extranonce_size as usize { - return Err(ShareValidationError::BadExtranonceSize); + return Err(ShareValidationError::BadExtranonceSize( + ERROR_CODE_SUBMIT_SHARES_BAD_EXTRANONCE_SIZE, + )); } let mut full_extranonce = vec![]; @@ -533,7 +543,9 @@ impl<'a> ExtendedChannel<'a> { full_extranonce.as_ref(), &job.0.merkle_path.inner_as_ref(), ) - .ok_or(ShareValidationError::Invalid)? + .ok_or(ShareValidationError::Invalid( + ERROR_CODE_SUBMIT_SHARES_INVALID_SHARE, + ))? .try_into() .expect("merkle root must be 32 bytes"); @@ -551,7 +563,9 @@ impl<'a> ExtendedChannel<'a> { // This is done by checking if the version & 0x1fffe000 == 0 // ref: https://github.com/bitcoin/bips/blob/master/bip-0320.mediawiki if (share.version & 0x1fffe000) != 0 { - return Err(ShareValidationError::VersionRollingNotAllowed); + return Err(ShareValidationError::VersionRollingNotAllowed( + ERROR_CODE_VERSION_ROLLING_NOT_ALLOWED, + )); } } @@ -591,7 +605,9 @@ impl<'a> ExtendedChannel<'a> { .share_accounting .is_share_seen(share_hash.to_raw_hash()) { - return Err(ShareValidationError::DuplicateShare); + return Err(ShareValidationError::DuplicateShare( + ERROR_CODE_SUBMIT_SHARES_DUPLICATE_SHARE, + )); } self.share_accounting .track_validated_share(share.sequence_number, share_hash.to_raw_hash()); @@ -605,7 +621,9 @@ impl<'a> ExtendedChannel<'a> { .share_accounting .is_share_seen(share_hash.to_raw_hash()) { - return Err(ShareValidationError::DuplicateShare); + return Err(ShareValidationError::DuplicateShare( + ERROR_CODE_SUBMIT_SHARES_DUPLICATE_SHARE, + )); } self.share_accounting @@ -617,7 +635,9 @@ impl<'a> ExtendedChannel<'a> { return Ok(ShareValidationResult::Valid(share_hash.to_raw_hash())); } - Err(ShareValidationError::DoesNotMeetTarget) + Err(ShareValidationError::DoesNotMeetTarget( + ERROR_CODE_SUBMIT_SHARES_DIFFICULTY_TOO_LOW, + )) } } @@ -896,7 +916,7 @@ mod tests { let res = channel.validate_share(share_valid_block); assert!(matches!( res.unwrap_err(), - ShareValidationError::DuplicateShare + ShareValidationError::DuplicateShare(_) )); assert_eq!(channel.get_share_accounting().get_blocks_found(), 1); } @@ -992,7 +1012,7 @@ mod tests { assert!(matches!( res.unwrap_err(), - ShareValidationError::DoesNotMeetTarget + ShareValidationError::DoesNotMeetTarget(_) )); } @@ -1103,7 +1123,7 @@ mod tests { assert!(matches!( res.unwrap_err(), - ShareValidationError::DuplicateShare + ShareValidationError::DuplicateShare(_) )); } } diff --git a/sv2/channels-sv2/src/client/share_accounting.rs b/sv2/channels-sv2/src/client/share_accounting.rs index e0579dd376..3c3d76428c 100644 --- a/sv2/channels-sv2/src/client/share_accounting.rs +++ b/sv2/channels-sv2/src/client/share_accounting.rs @@ -21,25 +21,31 @@ pub enum ShareValidationResult { /// Possible errors encountered during share validation. /// +/// Variants carrying `&'static str` are intended to be used as `error_code` values in +/// [`SubmitSharesError`](mining_sv2::SubmitSharesError). +/// +/// Variants without `&'static str` SHOULD lead to a client disconnection or application +/// shutdown. +/// /// - `Invalid`: The share is malformed or not valid. /// - `Stale`: The share refers to an outdated job or block tip. /// - `InvalidJobId`: The job ID referenced by the share is not recognized. /// - `DoesNotMeetTarget`: The share does not meet the required target difficulty. /// - `VersionRollingNotAllowed`: Version rolling is not permitted for this channel/job. /// - `DuplicateShare`: The share has already been submitted (detected by hash). -/// - `NoChainTip`: The chain tip is unknown or unavailable. /// - `BadExtranonceSize`: The share extranonce size is different from the channel's rollable /// extranonce size. +/// - `NoChainTip`: The chain tip is unknown or unavailable. #[derive(Debug)] pub enum ShareValidationError { - Invalid, - Stale, - InvalidJobId, - DoesNotMeetTarget, - VersionRollingNotAllowed, - DuplicateShare, + Invalid(&'static str), + Stale(&'static str), + InvalidJobId(&'static str), + DoesNotMeetTarget(&'static str), + VersionRollingNotAllowed(&'static str), + DuplicateShare(&'static str), + BadExtranonceSize(&'static str), NoChainTip, - BadExtranonceSize, } /// Tracks share validation and acceptance state for a specific channel (Extended or Standard). diff --git a/sv2/channels-sv2/src/client/standard.rs b/sv2/channels-sv2/src/client/standard.rs index c46c97ef1a..a40c01dd2d 100644 --- a/sv2/channels-sv2/src/client/standard.rs +++ b/sv2/channels-sv2/src/client/standard.rs @@ -26,6 +26,8 @@ use bitcoin::{ }; use mining_sv2::{ NewExtendedMiningJob, NewMiningJob, SetNewPrevHash as SetNewPrevHashMp, SubmitSharesStandard, + ERROR_CODE_SUBMIT_SHARES_DIFFICULTY_TOO_LOW, ERROR_CODE_SUBMIT_SHARES_DUPLICATE_SHARE, + ERROR_CODE_SUBMIT_SHARES_INVALID_JOB_ID, ERROR_CODE_SUBMIT_SHARES_STALE_SHARE, }; use tracing::debug; @@ -310,7 +312,9 @@ impl<'a> StandardChannel<'a> { let is_stale_job = self.stale_jobs.contains_key(&job_id); if is_stale_job { - return Err(ShareValidationError::Stale); + return Err(ShareValidationError::Stale( + ERROR_CODE_SUBMIT_SHARES_STALE_SHARE, + )); } let job = if is_active_job { @@ -318,7 +322,9 @@ impl<'a> StandardChannel<'a> { } else if is_past_job { self.past_jobs.get(&job_id).expect("past job must exist") } else { - return Err(ShareValidationError::InvalidJobId); + return Err(ShareValidationError::InvalidJobId( + ERROR_CODE_SUBMIT_SHARES_INVALID_JOB_ID, + )); }; let merkle_root: [u8; 32] = job @@ -372,7 +378,9 @@ impl<'a> StandardChannel<'a> { .share_accounting .is_share_seen(share_hash.to_raw_hash()) { - return Err(ShareValidationError::DuplicateShare); + return Err(ShareValidationError::DuplicateShare( + ERROR_CODE_SUBMIT_SHARES_DUPLICATE_SHARE, + )); } self.share_accounting .track_validated_share(share.sequence_number, share_hash.to_raw_hash()); @@ -386,7 +394,9 @@ impl<'a> StandardChannel<'a> { .share_accounting .is_share_seen(share_hash.to_raw_hash()) { - return Err(ShareValidationError::DuplicateShare); + return Err(ShareValidationError::DuplicateShare( + ERROR_CODE_SUBMIT_SHARES_DUPLICATE_SHARE, + )); } self.share_accounting @@ -398,7 +408,9 @@ impl<'a> StandardChannel<'a> { return Ok(ShareValidationResult::Valid(share_hash.to_raw_hash())); } - Err(ShareValidationError::DoesNotMeetTarget) + Err(ShareValidationError::DoesNotMeetTarget( + ERROR_CODE_SUBMIT_SHARES_DIFFICULTY_TOO_LOW, + )) } } @@ -604,7 +616,7 @@ mod tests { let res = channel.validate_share(share_valid_block); assert!(matches!( res.unwrap_err(), - ShareValidationError::DuplicateShare + ShareValidationError::DuplicateShare(_) )); assert_eq!(channel.get_share_accounting().get_blocks_found(), 1); } @@ -681,7 +693,7 @@ mod tests { assert!(matches!( res.unwrap_err(), - ShareValidationError::DoesNotMeetTarget + ShareValidationError::DoesNotMeetTarget(_) )); } diff --git a/sv2/channels-sv2/src/server/error.rs b/sv2/channels-sv2/src/server/error.rs index 74922711ef..a32b7aaffc 100644 --- a/sv2/channels-sv2/src/server/error.rs +++ b/sv2/channels-sv2/src/server/error.rs @@ -2,10 +2,20 @@ use crate::server::jobs::error::JobFactoryError; +/// Errors that can occur while operating an extended channel on the server side. +/// +/// Variants carrying `&'static str` are intended to be used as `error_code` values in protocol +/// error messages (for example +/// [`OpenExtendedMiningChannelError`](mining_sv2::OpenExtendedMiningChannelError) and +/// [`UpdateChannelError`](mining_sv2::UpdateChannelError)). +/// +/// Variants without `&'static str` SHOULD lead to a client disconnection or application +/// shutdown. #[derive(Debug)] pub enum ExtendedChannelError { + OpenChannelInvalidNominalHashrate(&'static str), + UpdateChannelInvalidNominalHashrate(&'static str), JobFactoryError(JobFactoryError), - InvalidNominalHashrate, ChainTipNotSet, TemplateIdNotFound, JobIdNotFound, @@ -24,10 +34,20 @@ pub enum GroupChannelError { ScriptSigSizeTooLarge, } +/// Errors that can occur while operating a standard channel on the server side. +/// +/// Variants carrying `&'static str` are intended to be used as `error_code` values in protocol +/// error messages (for example +/// [`OpenStandardMiningChannelError`](mining_sv2::OpenStandardMiningChannelError) and +/// [`UpdateChannelError`](mining_sv2::UpdateChannelError)). +/// +/// Variants without `&'static str` SHOULD lead to a client disconnection or application +/// shutdown. #[derive(Debug)] pub enum StandardChannelError { + OpenChannelInvalidNominalHashrate(&'static str), + UpdateChannelInvalidNominalHashrate(&'static str), TemplateIdNotFound, - InvalidNominalHashrate, ExtranoncePrefixTooLarge, JobFactoryError(JobFactoryError), ChainTipNotSet, diff --git a/sv2/channels-sv2/src/server/extended.rs b/sv2/channels-sv2/src/server/extended.rs index 899278935b..fda1edc3e9 100644 --- a/sv2/channels-sv2/src/server/extended.rs +++ b/sv2/channels-sv2/src/server/extended.rs @@ -57,7 +57,14 @@ use bitcoin::{ transaction::TxOut, CompactTarget, Target, }; -use mining_sv2::{SetCustomMiningJob, SubmitSharesExtended}; +use mining_sv2::{ + SetCustomMiningJob, SubmitSharesExtended, + ERROR_CODE_OPEN_MINING_CHANNEL_INVALID_NOMINAL_HASHRATE, + ERROR_CODE_SUBMIT_SHARES_BAD_EXTRANONCE_SIZE, ERROR_CODE_SUBMIT_SHARES_DIFFICULTY_TOO_LOW, + ERROR_CODE_SUBMIT_SHARES_DUPLICATE_SHARE, ERROR_CODE_SUBMIT_SHARES_INVALID_JOB_ID, + ERROR_CODE_SUBMIT_SHARES_INVALID_SHARE, ERROR_CODE_SUBMIT_SHARES_STALE_SHARE, + ERROR_CODE_UPDATE_CHANNEL_INVALID_NOMINAL_HASHRATE, ERROR_CODE_VERSION_ROLLING_NOT_ALLOWED, +}; use std::{collections::HashMap, convert::TryInto, marker::PhantomData}; use template_distribution_sv2::{NewTemplate, SetNewPrevHash as SetNewPrevHashTdp}; use tracing::debug; @@ -205,7 +212,9 @@ where match hash_rate_to_target(nominal_hashrate.into(), expected_share_per_minute.into()) { Ok(target) => target, Err(_) => { - return Err(ExtendedChannelError::InvalidNominalHashrate); + return Err(ExtendedChannelError::OpenChannelInvalidNominalHashrate( + ERROR_CODE_OPEN_MINING_CHANNEL_INVALID_NOMINAL_HASHRATE, + )); } }; @@ -352,7 +361,7 @@ where /// If the recomputed target is easier than the effective `requested_max_target`, /// the target is clamped to `requested_max_target`. /// - /// Returns [`ExtendedChannelError::InvalidNominalHashrate`] when + /// Returns [`ExtendedChannelError::UpdateChannelInvalidNominalHashrate`] when /// `new_nominal_hashrate` cannot be converted into a valid target. /// /// This can be used in two scenarios: @@ -372,7 +381,9 @@ where ) { Ok(target) => target, Err(_) => { - return Err(ExtendedChannelError::InvalidNominalHashrate); + return Err(ExtendedChannelError::UpdateChannelInvalidNominalHashrate( + ERROR_CODE_UPDATE_CHANNEL_INVALID_NOMINAL_HASHRATE, + )); } }; @@ -647,12 +658,16 @@ where let is_stale_job = self.job_store.get_stale_job(job_id).is_some(); if is_stale_job { - return Err(ShareValidationError::Stale); + return Err(ShareValidationError::Stale( + ERROR_CODE_SUBMIT_SHARES_STALE_SHARE, + )); } // if job_id is not active, past or stale, return error if !is_active_job && !is_past_job && !is_stale_job { - return Err(ShareValidationError::InvalidJobId); + return Err(ShareValidationError::InvalidJobId( + ERROR_CODE_SUBMIT_SHARES_INVALID_JOB_ID, + )); }; let job = if is_active_job { @@ -676,7 +691,9 @@ where let extranonce_size = share.extranonce.inner_as_ref().len(); if extranonce_size != self.rollable_extranonce_size as usize { - return Err(ShareValidationError::BadExtranonceSize); + return Err(ShareValidationError::BadExtranonceSize( + ERROR_CODE_SUBMIT_SHARES_BAD_EXTRANONCE_SIZE, + )); } let extranonce_prefix = job.get_extranonce_prefix(); @@ -695,7 +712,9 @@ where full_extranonce.as_ref(), &job.get_merkle_path().inner_as_ref(), ) - .ok_or(ShareValidationError::Invalid)? + .ok_or(ShareValidationError::Invalid( + ERROR_CODE_SUBMIT_SHARES_INVALID_SHARE, + ))? .try_into() .expect("merkle root must be 32 bytes"); @@ -713,7 +732,9 @@ where // This is done by checking if the version & 0x1fffe000 == 0 // ref: https://github.com/bitcoin/bips/blob/master/bip-0320.mediawiki if (share.version & 0x1fffe000) != 0 { - return Err(ShareValidationError::VersionRollingNotAllowed); + return Err(ShareValidationError::VersionRollingNotAllowed( + ERROR_CODE_VERSION_ROLLING_NOT_ALLOWED, + )); } } @@ -752,7 +773,9 @@ where .share_accounting .is_share_seen(share_hash.to_raw_hash()) { - return Err(ShareValidationError::DuplicateShare); + return Err(ShareValidationError::DuplicateShare( + ERROR_CODE_SUBMIT_SHARES_DUPLICATE_SHARE, + )); } self.share_accounting.update_share_accounting( job_target.difficulty_float(), @@ -792,7 +815,9 @@ where .share_accounting .is_share_seen(share_hash.to_raw_hash()) { - return Err(ShareValidationError::DuplicateShare); + return Err(ShareValidationError::DuplicateShare( + ERROR_CODE_SUBMIT_SHARES_DUPLICATE_SHARE, + )); } self.share_accounting.update_share_accounting( @@ -806,7 +831,9 @@ where Ok(ShareValidationResult::Valid(share_hash.to_raw_hash())) } else { - Err(ShareValidationError::DoesNotMeetTarget) + Err(ShareValidationError::DoesNotMeetTarget( + ERROR_CODE_SUBMIT_SHARES_DIFFICULTY_TOO_LOW, + )) } } } @@ -1293,7 +1320,7 @@ mod tests { let res = channel.validate_share(share_valid_block); assert!(matches!( res.unwrap_err(), - ShareValidationError::DuplicateShare + ShareValidationError::DuplicateShare(_) )); assert_eq!(channel.get_share_accounting().get_blocks_found(), 1); } @@ -1406,7 +1433,7 @@ mod tests { assert!(matches!( res.unwrap_err(), - ShareValidationError::DoesNotMeetTarget + ShareValidationError::DoesNotMeetTarget(_) )); } @@ -1535,7 +1562,7 @@ mod tests { let res = channel.validate_share(repeated_share); // assert duplicate share is rejected - assert!(matches!(res, Err(ShareValidationError::DuplicateShare))); + assert!(matches!(res, Err(ShareValidationError::DuplicateShare(_)))); } #[test] @@ -1641,7 +1668,7 @@ mod tests { assert!(result.is_err()); assert!(matches!( result, - Err(ExtendedChannelError::InvalidNominalHashrate) + Err(ExtendedChannelError::UpdateChannelInvalidNominalHashrate(_)) )); // Create a not so permissive max_target so we can test a target that exceeds it diff --git a/sv2/channels-sv2/src/server/share_accounting.rs b/sv2/channels-sv2/src/server/share_accounting.rs index a7dd53e49d..32c04eb9a4 100644 --- a/sv2/channels-sv2/src/server/share_accounting.rs +++ b/sv2/channels-sv2/src/server/share_accounting.rs @@ -42,26 +42,32 @@ pub enum ShareValidationResult { } /// The error variants that can occur during share validation. +/// +/// Variants carrying `&'static str` are intended to be used as `error_code` values in +/// [`SubmitSharesError`](mining_sv2::SubmitSharesError). +/// +/// Variants without `&'static str` SHOULD lead to a client disconnection or application +/// shutdown. #[derive(Debug)] pub enum ShareValidationError { /// The share is invalid for unspecified reasons. - Invalid, + Invalid(&'static str), /// The share is stale due to chain tip changes. - Stale, + Stale(&'static str), /// The submitted job ID does not refer to any known job for this channel. - InvalidJobId, + InvalidJobId(&'static str), /// The share does not meet the required target difficulty. - DoesNotMeetTarget, + DoesNotMeetTarget(&'static str), /// The submitted share attempts version rolling when not allowed. - VersionRollingNotAllowed, + VersionRollingNotAllowed(&'static str), /// The share is a duplicate of a previously accepted share. - DuplicateShare, + DuplicateShare(&'static str), + /// The share extranonce size is different from the channel's rollable extranonce size. + BadExtranonceSize(&'static str), /// The coinbase transaction was invalid or malformed. InvalidCoinbase, /// No chain tip is set for the channel (required for share validation). NoChainTip, - /// The share extranonce size is different from the channel's rollable extranonce size. - BadExtranonceSize, } /// The state of share validation in the context of some specific channel (either Extended or diff --git a/sv2/channels-sv2/src/server/standard.rs b/sv2/channels-sv2/src/server/standard.rs index 0e07f7e426..73e37c57d2 100644 --- a/sv2/channels-sv2/src/server/standard.rs +++ b/sv2/channels-sv2/src/server/standard.rs @@ -57,7 +57,12 @@ use bitcoin::{ transaction::{OutPoint, Transaction, TxIn, TxOut, Version as TxVersion}, CompactTarget, Sequence, Target, }; -use mining_sv2::SubmitSharesStandard; +use mining_sv2::{ + SubmitSharesStandard, ERROR_CODE_OPEN_MINING_CHANNEL_INVALID_NOMINAL_HASHRATE, + ERROR_CODE_SUBMIT_SHARES_DIFFICULTY_TOO_LOW, ERROR_CODE_SUBMIT_SHARES_DUPLICATE_SHARE, + ERROR_CODE_SUBMIT_SHARES_INVALID_JOB_ID, ERROR_CODE_SUBMIT_SHARES_STALE_SHARE, + ERROR_CODE_UPDATE_CHANNEL_INVALID_NOMINAL_HASHRATE, +}; use std::{collections::HashMap, convert::TryInto, marker::PhantomData}; use template_distribution_sv2::{NewTemplate, SetNewPrevHash}; use tracing::debug; @@ -192,7 +197,9 @@ where match hash_rate_to_target(nominal_hashrate.into(), expected_share_per_minute.into()) { Ok(target_u256) => target_u256, Err(_) => { - return Err(StandardChannelError::InvalidNominalHashrate); + return Err(StandardChannelError::OpenChannelInvalidNominalHashrate( + ERROR_CODE_OPEN_MINING_CHANNEL_INVALID_NOMINAL_HASHRATE, + )); } }; @@ -301,7 +308,7 @@ where /// If the recomputed target is easier than the effective `requested_max_target`, /// the target is clamped to `requested_max_target`. /// - /// Returns [`StandardChannelError::InvalidNominalHashrate`] when + /// Returns [`StandardChannelError::UpdateChannelInvalidNominalHashrate`] when /// `nominal_hashrate` cannot be converted into a valid target. /// /// This can be used in two scenarios: @@ -321,7 +328,9 @@ where ) { Ok(target) => target, Err(_) => { - return Err(StandardChannelError::InvalidNominalHashrate); + return Err(StandardChannelError::UpdateChannelInvalidNominalHashrate( + ERROR_CODE_UPDATE_CHANNEL_INVALID_NOMINAL_HASHRATE, + )); } }; @@ -569,12 +578,16 @@ where let is_stale_job = self.job_store.get_stale_job(job_id).is_some(); if is_stale_job { - return Err(ShareValidationError::Stale); + return Err(ShareValidationError::Stale( + ERROR_CODE_SUBMIT_SHARES_STALE_SHARE, + )); } // if job_id is not active, past or stale, return error if !is_active_job && !is_past_job && !is_stale_job { - return Err(ShareValidationError::InvalidJobId); + return Err(ShareValidationError::InvalidJobId( + ERROR_CODE_SUBMIT_SHARES_INVALID_JOB_ID, + )); } let job = if is_active_job { @@ -644,7 +657,9 @@ where .share_accounting .is_share_seen(share_hash.to_raw_hash()) { - return Err(ShareValidationError::DuplicateShare); + return Err(ShareValidationError::DuplicateShare( + ERROR_CODE_SUBMIT_SHARES_DUPLICATE_SHARE, + )); } self.share_accounting.update_share_accounting( job_target.difficulty_float(), @@ -695,7 +710,9 @@ where .share_accounting .is_share_seen(share_hash.to_raw_hash()) { - return Err(ShareValidationError::DuplicateShare); + return Err(ShareValidationError::DuplicateShare( + ERROR_CODE_SUBMIT_SHARES_DUPLICATE_SHARE, + )); } self.share_accounting.update_share_accounting( @@ -709,7 +726,9 @@ where Ok(ShareValidationResult::Valid(share_hash.to_raw_hash())) } else { - Err(ShareValidationError::DoesNotMeetTarget) + Err(ShareValidationError::DoesNotMeetTarget( + ERROR_CODE_SUBMIT_SHARES_DIFFICULTY_TOO_LOW, + )) } } } @@ -1083,7 +1102,7 @@ mod tests { let res = standard_channel.validate_share(share_valid_block); assert!(matches!( res.unwrap_err(), - ShareValidationError::DuplicateShare + ShareValidationError::DuplicateShare(_) )); assert_eq!( standard_channel.get_share_accounting().get_blocks_found(), @@ -1196,7 +1215,7 @@ mod tests { assert!(matches!( res.unwrap_err(), - ShareValidationError::DoesNotMeetTarget + ShareValidationError::DoesNotMeetTarget(_) )); } @@ -1401,7 +1420,7 @@ mod tests { assert!(result.is_err()); assert!(matches!( result, - Err(StandardChannelError::InvalidNominalHashrate) + Err(StandardChannelError::UpdateChannelInvalidNominalHashrate(_)) )); // Create a not so permissive max_target so we can test a target that exceeds it diff --git a/sv2/handlers-sv2/Cargo.toml b/sv2/handlers-sv2/Cargo.toml index 6a9e534582..0e618c7af1 100644 --- a/sv2/handlers-sv2/Cargo.toml +++ b/sv2/handlers-sv2/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "handlers_sv2" -version = "0.3.0" +version = "0.4.0" authors = ["The Stratum V2 Developers"] edition = "2021" readme = "README.md" @@ -12,12 +12,12 @@ homepage = "https://stratumprotocol.org" keywords = ["stratum", "mining", "bitcoin", "protocol"] [dependencies] -parsers_sv2 = { path = "../parsers-sv2", version = "^0.3.0"} +parsers_sv2 = { path = "../parsers-sv2", version = "^0.4.0"} binary_sv2 = { path = "../binary-sv2", version = "^5.0.0" } -common_messages_sv2 = { path = "../subprotocols/common-messages", version = "^7.0.0" } +common_messages_sv2 = { path = "../subprotocols/common-messages", version = "^7.2.0" } framing_sv2 = { path = "../framing-sv2", version = "^6.0.0" } -mining_sv2 = { path = "../subprotocols/mining", version = "^9.0.0" } -template_distribution_sv2 = { path = "../subprotocols/template-distribution", version = "^5.0.0" } -job_declaration_sv2 = { path = "../subprotocols/job-declaration", version = "^7.0.0" } +mining_sv2 = { path = "../subprotocols/mining", version = "^10.0.0" } +template_distribution_sv2 = { path = "../subprotocols/template-distribution", version = "^5.1.0" } +job_declaration_sv2 = { path = "../subprotocols/job-declaration", version = "^7.1.0" } extensions_sv2 = { path = "../extensions-sv2", version = "^0.1.0" } trait-variant = { workspace = true } diff --git a/sv2/parsers-sv2/Cargo.toml b/sv2/parsers-sv2/Cargo.toml index 3760ce2d72..e852925529 100644 --- a/sv2/parsers-sv2/Cargo.toml +++ b/sv2/parsers-sv2/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parsers_sv2" -version = "0.3.0" +version = "0.4.0" authors = ["The Stratum V2 Developers"] edition = "2021" readme = "README.md" @@ -14,10 +14,10 @@ keywords = ["stratum", "mining", "bitcoin", "protocol"] [dependencies] binary_sv2 = { path = "../binary-sv2", version = "^5.0.0" } framing_sv2 = { path = "../framing-sv2", version = "^6.0.0" } -common_messages_sv2 = { path = "../subprotocols/common-messages", version = "^7.0.0" } -mining_sv2 = { path = "../subprotocols/mining", version = "^9.0.0" } -template_distribution_sv2 = { path = "../subprotocols/template-distribution", version = "^5.0.0" } -job_declaration_sv2 = { path = "../subprotocols/job-declaration", version = "^7.0.0" } +common_messages_sv2 = { path = "../subprotocols/common-messages", version = "^7.2.0" } +mining_sv2 = { path = "../subprotocols/mining", version = "^10.0.0" } +template_distribution_sv2 = { path = "../subprotocols/template-distribution", version = "^5.1.0" } +job_declaration_sv2 = { path = "../subprotocols/job-declaration", version = "^7.1.0" } extensions_sv2 = { path = "../extensions-sv2", version = "^0.1.0" } [dev-dependencies] diff --git a/sv2/subprotocols/common-messages/Cargo.toml b/sv2/subprotocols/common-messages/Cargo.toml index 45ce89b278..8af02131d6 100644 --- a/sv2/subprotocols/common-messages/Cargo.toml +++ b/sv2/subprotocols/common-messages/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "common_messages_sv2" -version = "7.1.0" +version = "7.2.0" authors = ["The Stratum V2 Developers"] edition = "2021" readme = "README.md" diff --git a/sv2/subprotocols/common-messages/src/lib.rs b/sv2/subprotocols/common-messages/src/lib.rs index 5ec330c27f..a63615d196 100644 --- a/sv2/subprotocols/common-messages/src/lib.rs +++ b/sv2/subprotocols/common-messages/src/lib.rs @@ -60,6 +60,12 @@ pub const CHANNEL_BIT_SETUP_CONNECTION_ERROR: bool = false; pub const CHANNEL_BIT_CHANNEL_ENDPOINT_CHANGED: bool = true; pub const CHANNEL_BIT_RECONNECT: bool = false; +// Commonly used SetupConnectionError error_code values. +pub const ERROR_CODE_SETUP_CONNECTION_UNSUPPORTED_FEATURE_FLAGS: &str = "unsupported-feature-flags"; +pub const ERROR_CODE_SETUP_CONNECTION_UNSUPPORTED_PROTOCOL: &str = "unsupported-protocol"; +pub const ERROR_CODE_SETUP_CONNECTION_MISSING_DECLARE_TX_DATA_FLAG: &str = + "missing-declare-tx-data-flag"; + #[cfg(feature = "prop_test")] impl ChannelEndpointChanged { pub fn from_gen(g: &mut Gen) -> Self { diff --git a/sv2/subprotocols/common-messages/src/setup_connection.rs b/sv2/subprotocols/common-messages/src/setup_connection.rs index 53663fdea5..463b262e4b 100644 --- a/sv2/subprotocols/common-messages/src/setup_connection.rs +++ b/sv2/subprotocols/common-messages/src/setup_connection.rs @@ -256,7 +256,7 @@ pub struct SetupConnectionError<'decoder> { /// Possible error codes: /// - unsupported-feature-flags /// - unsupported-protocol - /// - protocol-version-mismatch + /// - missing-declare-tx-data-flag pub error_code: Str0255<'decoder>, } diff --git a/sv2/subprotocols/job-declaration/Cargo.toml b/sv2/subprotocols/job-declaration/Cargo.toml index 89b2d3639c..1c326d37b3 100644 --- a/sv2/subprotocols/job-declaration/Cargo.toml +++ b/sv2/subprotocols/job-declaration/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "job_declaration_sv2" -version = "7.0.0" +version = "7.1.0" authors = ["The Stratum V2 Developers"] edition = "2021" readme = "README.md" diff --git a/sv2/subprotocols/job-declaration/src/declare_mining_job.rs b/sv2/subprotocols/job-declaration/src/declare_mining_job.rs index bbf4a2357b..8c1f069d6d 100644 --- a/sv2/subprotocols/job-declaration/src/declare_mining_job.rs +++ b/sv2/subprotocols/job-declaration/src/declare_mining_job.rs @@ -95,7 +95,12 @@ pub struct DeclareMiningJobError<'decoder> { /// Possible values: /// /// - invalid-mining-job-token - /// - invalid-job-param-value-{DeclareMiningJob::field} + /// - missing-txs + /// - invalid-coinbase-tx + /// - invalid-coinbase-tx-input + /// - internal-error + /// - stale-chain-tip + /// - invalid-job pub error_code: Str0255<'decoder>, /// Optional details about the error. pub error_details: B064K<'decoder>, diff --git a/sv2/subprotocols/job-declaration/src/lib.rs b/sv2/subprotocols/job-declaration/src/lib.rs index 2a6d64ab68..c31ea71f60 100644 --- a/sv2/subprotocols/job-declaration/src/lib.rs +++ b/sv2/subprotocols/job-declaration/src/lib.rs @@ -46,3 +46,13 @@ pub const CHANNEL_BIT_DECLARE_MINING_JOB_ERROR: bool = false; pub const CHANNEL_BIT_PROVIDE_MISSING_TRANSACTIONS: bool = false; pub const CHANNEL_BIT_PROVIDE_MISSING_TRANSACTIONS_SUCCESS: bool = false; pub const CHANNEL_BIT_PUSH_SOLUTION: bool = true; + +// Commonly used DeclareMiningJobError error_code values. +pub const ERROR_CODE_DECLARE_MINING_JOB_INVALID_MINING_JOB_TOKEN: &str = "invalid-mining-job-token"; +pub const ERROR_CODE_DECLARE_MINING_JOB_MISSING_TXS: &str = "missing-txs"; +pub const ERROR_CODE_DECLARE_MINING_JOB_INVALID_COINBASE_TX: &str = "invalid-coinbase-tx"; +pub const ERROR_CODE_DECLARE_MINING_JOB_INVALID_COINBASE_TX_INPUT: &str = + "invalid-coinbase-tx-input"; +pub const ERROR_CODE_DECLARE_MINING_JOB_INTERNAL_ERROR: &str = "internal-error"; +pub const ERROR_CODE_DECLARE_MINING_JOB_STALE_CHAIN_TIP: &str = "stale-chain-tip"; +pub const ERROR_CODE_DECLARE_MINING_JOB_INVALID_JOB: &str = "invalid-job"; diff --git a/sv2/subprotocols/mining/Cargo.toml b/sv2/subprotocols/mining/Cargo.toml index 9a409b9b5a..20cb25f925 100644 --- a/sv2/subprotocols/mining/Cargo.toml +++ b/sv2/subprotocols/mining/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mining_sv2" -version = "9.0.0" +version = "10.0.0" authors = ["The Stratum V2 Developers"] edition = "2021" readme = "README.md" @@ -20,4 +20,3 @@ binary_sv2 = { path = "../../binary-sv2", version = "^5.0.0" } [dev-dependencies] quickcheck = { workspace = true } quickcheck_macros = { workspace = true } - diff --git a/sv2/subprotocols/mining/src/lib.rs b/sv2/subprotocols/mining/src/lib.rs index d54b00e9c7..31b3891e74 100644 --- a/sv2/subprotocols/mining/src/lib.rs +++ b/sv2/subprotocols/mining/src/lib.rs @@ -98,5 +98,55 @@ pub const CHANNEL_BIT_SUBMIT_SHARES_ERROR: bool = true; pub const CHANNEL_BIT_SUBMIT_SHARES_EXTENDED: bool = true; pub const CHANNEL_BIT_SUBMIT_SHARES_STANDARD: bool = true; pub const CHANNEL_BIT_SUBMIT_SHARES_SUCCESS: bool = true; + +// Commonly used OpenMiningChannelError error_code values. +pub const ERROR_CODE_OPEN_MINING_CHANNEL_STANDARD_CHANNELS_NOT_SUPPORTED_FOR_CUSTOM_WORK: &str = + "standard-channels-not-supported-for-custom-work"; +pub const ERROR_CODE_OPEN_MINING_CHANNEL_INVALID_USER_IDENTITY: &str = "invalid-user-identity"; +pub const ERROR_CODE_OPEN_MINING_CHANNEL_INVALID_NOMINAL_HASHRATE: &str = + "invalid-nominal-hashrate"; +pub const ERROR_CODE_OPEN_MINING_CHANNEL_MIN_EXTRANONCE_SIZE_TOO_LARGE: &str = + "min-extranonce-size-too-large"; +pub const ERROR_CODE_OPEN_MINING_CHANNEL_MAX_TARGET_OUT_OF_RANGE: &str = "max-target-out-of-range"; +pub const ERROR_CODE_OPEN_MINING_CHANNEL_UNSUPPORTED_MIN_EXTRANONCE_SIZE: &str = + "unsupported-min-extranonce-size"; +pub const ERROR_CODE_OPEN_MINING_CHANNEL_UNKNOWN_USER: &str = "unknown-user"; + +// Commonly used UpdateChannelError error_code values. +pub const ERROR_CODE_UPDATE_CHANNEL_INVALID_NOMINAL_HASHRATE: &str = "invalid-nominal-hashrate"; +pub const ERROR_CODE_UPDATE_CHANNEL_INVALID_CHANNEL_ID: &str = "invalid-channel-id"; + +// Commonly used SubmitSharesError error_code values. +pub const ERROR_CODE_SUBMIT_SHARES_INVALID_CHANNEL_ID: &str = "invalid-channel-id"; +pub const ERROR_CODE_SUBMIT_SHARES_INVALID_SHARE: &str = "invalid-share"; +pub const ERROR_CODE_SUBMIT_SHARES_STALE_SHARE: &str = "stale-share"; +pub const ERROR_CODE_SUBMIT_SHARES_INVALID_JOB_ID: &str = "invalid-job-id"; +pub const ERROR_CODE_SUBMIT_SHARES_DIFFICULTY_TOO_LOW: &str = "difficulty-too-low"; +pub const ERROR_CODE_SUBMIT_SHARES_DUPLICATE_SHARE: &str = "duplicate-share"; +pub const ERROR_CODE_SUBMIT_SHARES_BAD_EXTRANONCE_SIZE: &str = "bad-extranonce-size"; +pub const ERROR_CODE_VERSION_ROLLING_NOT_ALLOWED: &str = "version-rolling-not-allowed"; + +// Commonly used SetCustomMiningJobError error_code values. +pub const ERROR_CODE_SET_CUSTOM_MINING_JOB_JD_NOT_SUPPORTED: &str = "jd-not-supported"; +pub const ERROR_CODE_SET_CUSTOM_MINING_JOB_INVALID_CHANNEL_ID: &str = "invalid-channel-id"; +pub const ERROR_CODE_SET_CUSTOM_MINING_JOB_INVALID_MINING_JOB_TOKEN: &str = + "invalid-mining-job-token"; +pub const ERROR_CODE_SET_CUSTOM_MINING_JOB_JOB_NOT_YET_VALIDATED: &str = "job-not-yet-validated"; +pub const ERROR_CODE_SET_CUSTOM_MINING_JOB_STALE_CHAIN_TIP: &str = "stale-chain-tip"; +pub const ERROR_CODE_SET_CUSTOM_MINING_JOB_INVALID_MIN_NTIME: &str = "invalid-min-ntime"; +pub const ERROR_CODE_SET_CUSTOM_MINING_JOB_INVALID_NBITS: &str = "invalid-nbits"; +pub const ERROR_CODE_SET_CUSTOM_MINING_JOB_INVALID_VERSION: &str = "invalid-version"; +pub const ERROR_CODE_SET_CUSTOM_MINING_JOB_INVALID_COINBASE_TX: &str = "invalid-coinbase-tx"; +pub const ERROR_CODE_SET_CUSTOM_MINING_JOB_INVALID_COINBASE_TX_VERSION: &str = + "invalid-coinbase-tx-version"; +pub const ERROR_CODE_SET_CUSTOM_MINING_JOB_INVALID_COINBASE_PREFIX: &str = + "invalid-coinbase-prefix"; +pub const ERROR_CODE_SET_CUSTOM_MINING_JOB_INVALID_COINBASE_TX_INPUT_N_SEQUENCE: &str = + "invalid-coinbase-tx-input-n-sequence"; +pub const ERROR_CODE_SET_CUSTOM_MINING_JOB_INVALID_COINBASE_TX_OUTPUTS: &str = + "invalid-coinbase-tx-outputs"; +pub const ERROR_CODE_SET_CUSTOM_MINING_JOB_INVALID_COINBASE_TX_LOCKTIME: &str = + "invalid-coinbase-tx-locktime"; +pub const ERROR_CODE_SET_CUSTOM_MINING_JOB_INVALID_MERKLE_PATH: &str = "invalid-merkle-path"; pub const CHANNEL_BIT_UPDATE_CHANNEL: bool = true; pub const CHANNEL_BIT_UPDATE_CHANNEL_ERROR: bool = true; diff --git a/sv2/subprotocols/mining/src/open_channel.rs b/sv2/subprotocols/mining/src/open_channel.rs index a9a03d2e1f..0f5d7d97f6 100644 --- a/sv2/subprotocols/mining/src/open_channel.rs +++ b/sv2/subprotocols/mining/src/open_channel.rs @@ -225,8 +225,13 @@ pub struct OpenMiningChannelError<'decoder> { /// /// Possible error codes: /// - /// - ‘unknown-user’ - /// - ‘max-target-out-of-range’ + /// - standard-channels-not-supported-for-custom-work + /// - invalid-user-identity + /// - invalid-nominal-hashrate + /// - min-extranonce-size-too-large + /// - max-target-out-of-range + /// - unsupported-min-extranonce-size + /// - unknown-user pub error_code: Str0255<'decoder>, } @@ -245,13 +250,16 @@ impl OpenMiningChannelError<'_> { pub fn new_max_target_out_of_range(request_id: u32) -> Self { Self { request_id, - error_code: "max-target-out-of-range".to_string().try_into().unwrap(), + error_code: crate::ERROR_CODE_OPEN_MINING_CHANNEL_MAX_TARGET_OUT_OF_RANGE + .to_string() + .try_into() + .unwrap(), } } pub fn unsupported_extranonce_size(request_id: u32) -> Self { Self { request_id, - error_code: "unsupported-min-extranonce-size" + error_code: crate::ERROR_CODE_OPEN_MINING_CHANNEL_UNSUPPORTED_MIN_EXTRANONCE_SIZE .to_string() .try_into() .unwrap(), @@ -260,7 +268,10 @@ impl OpenMiningChannelError<'_> { pub fn new_unknown_user(request_id: u32) -> Self { Self { request_id, - error_code: "unknown-user".to_string().try_into().unwrap(), + error_code: crate::ERROR_CODE_OPEN_MINING_CHANNEL_UNKNOWN_USER + .to_string() + .try_into() + .unwrap(), } } } diff --git a/sv2/subprotocols/mining/src/set_custom_mining_job.rs b/sv2/subprotocols/mining/src/set_custom_mining_job.rs index c0cc8ed667..cc3d635b6d 100644 --- a/sv2/subprotocols/mining/src/set_custom_mining_job.rs +++ b/sv2/subprotocols/mining/src/set_custom_mining_job.rs @@ -108,7 +108,18 @@ pub struct SetCustomMiningJobError<'decoder> { /// Possible errors: /// - invalid-channel-id /// - invalid-mining-job-token - /// - invalid-job-param-value-{field_name} + /// - job-not-yet-validated + /// - stale-chain-tip + /// - invalid-min-ntime + /// - invalid-nbits + /// - invalid-version + /// - invalid-coinbase-tx + /// - invalid-coinbase-tx-version + /// - invalid-coinbase-prefix + /// - invalid-coinbase-tx-input-n-sequence + /// - invalid-coinbase-tx-outputs + /// - invalid-coinbase-tx-locktime + /// - invalid-merkle-path pub error_code: Str0255<'decoder>, } diff --git a/sv2/subprotocols/mining/src/submit_shares.rs b/sv2/subprotocols/mining/src/submit_shares.rs index e11eb02a17..d11ee34d3c 100644 --- a/sv2/subprotocols/mining/src/submit_shares.rs +++ b/sv2/subprotocols/mining/src/submit_shares.rs @@ -128,9 +128,13 @@ pub struct SubmitSharesError<'decoder> { /// Possible error codes: /// /// - invalid-channel-id + /// - invalid-share /// - stale-share /// - difficulty-too-low /// - invalid-job-id + /// - duplicate-share + /// - bad-extranonce-size + /// - version-rolling-not-allowed pub error_code: Str0255<'decoder>, } @@ -145,18 +149,3 @@ impl fmt::Display for SubmitSharesError<'_> { ) } } - -impl SubmitSharesError<'_> { - pub fn invalid_channel_error_code() -> &'static str { - "invalid-channel-id" - } - pub fn stale_share_error_code() -> &'static str { - "stale-share" - } - pub fn difficulty_too_low_error_code() -> &'static str { - "difficulty-too-low" - } - pub fn invalid_job_id_error_code() -> &'static str { - "invalid-job-id" - } -} diff --git a/sv2/subprotocols/mining/src/update_channel.rs b/sv2/subprotocols/mining/src/update_channel.rs index 294a5c39f1..d52e27b8b7 100644 --- a/sv2/subprotocols/mining/src/update_channel.rs +++ b/sv2/subprotocols/mining/src/update_channel.rs @@ -52,7 +52,7 @@ pub struct UpdateChannelError<'decoder> { /// Reason for channel update error. /// /// Possible error codes: - /// - max-target-out-of-range + /// - invalid-nominal-hashrate /// - invalid-channel-id pub error_code: Str0255<'decoder>, } diff --git a/sv2/subprotocols/template-distribution/Cargo.toml b/sv2/subprotocols/template-distribution/Cargo.toml index 561d69eb6d..d571066bbd 100644 --- a/sv2/subprotocols/template-distribution/Cargo.toml +++ b/sv2/subprotocols/template-distribution/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "template_distribution_sv2" -version = "5.0.0" +version = "5.1.0" authors = ["The Stratum V2 Developers"] edition = "2021" readme = "README.md" diff --git a/sv2/subprotocols/template-distribution/src/lib.rs b/sv2/subprotocols/template-distribution/src/lib.rs index 4def9ce5ac..c6809c2009 100644 --- a/sv2/subprotocols/template-distribution/src/lib.rs +++ b/sv2/subprotocols/template-distribution/src/lib.rs @@ -56,6 +56,10 @@ pub const CHANNEL_BIT_REQUEST_TRANSACTION_DATA_SUCCESS: bool = false; pub const CHANNEL_BIT_REQUEST_TRANSACTION_DATA_ERROR: bool = false; pub const CHANNEL_BIT_SUBMIT_SOLUTION: bool = false; +// Commonly used RequestTransactionDataError error_code values. +pub const ERROR_CODE_REQUEST_TRANSACTION_DATA_STALE_TEMPLATE_ID: &str = "stale-template-id"; +pub const ERROR_CODE_REQUEST_TRANSACTION_DATA_TEMPLATE_ID_NOT_FOUND: &str = "template-id-not-found"; + #[cfg(feature = "prop_test")] impl NewTemplate<'static> { pub fn from_gen(g: &mut Gen) -> Self { diff --git a/sv2/subprotocols/template-distribution/src/request_transaction_data.rs b/sv2/subprotocols/template-distribution/src/request_transaction_data.rs index e5f394743c..4681607420 100644 --- a/sv2/subprotocols/template-distribution/src/request_transaction_data.rs +++ b/sv2/subprotocols/template-distribution/src/request_transaction_data.rs @@ -88,6 +88,7 @@ pub struct RequestTransactionDataError<'decoder> { /// Reason why no transaction data has been provided. /// /// Possible error codes: + /// - stale-template-id /// - template-id-not-found pub error_code: Str0255<'decoder>, }