diff --git a/.github/workflows/clippy-lint.yaml b/.github/workflows/clippy-lint.yaml index fa2c45fac4..b8d4cba361 100644 --- a/.github/workflows/clippy-lint.yaml +++ b/.github/workflows/clippy-lint.yaml @@ -2,11 +2,9 @@ on: push: branches: - main - - dev pull_request: branches: - main - - dev name: Clippy Lint diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 5c20325c1b..c8e18d346d 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -18,9 +18,11 @@ name: Test Coverage on: push: - branches: [ main, dev ] + branches: + - main pull_request: - branches: [ main, dev ] + branches: + - main jobs: tarpaulin-test: diff --git a/.github/workflows/fmt.yaml b/.github/workflows/fmt.yaml index b0b3ecc185..181fc6088f 100644 --- a/.github/workflows/fmt.yaml +++ b/.github/workflows/fmt.yaml @@ -2,11 +2,9 @@ on: push: branches: - main - - dev pull_request: branches: - main - - dev name: Rustfmt diff --git a/.github/workflows/lockfiles.yaml b/.github/workflows/lockfiles.yaml index e87f016a7e..91131bfb98 100644 --- a/.github/workflows/lockfiles.yaml +++ b/.github/workflows/lockfiles.yaml @@ -1,14 +1,12 @@ name: Lockfiles -# Trigger the workflow on push or pull request events for the dev and main branches +# Trigger the workflow on push or pull request events for the main branch on: push: branches: - - dev - main pull_request: branches: - - dev - main jobs: diff --git a/.github/workflows/mg.yaml b/.github/workflows/mg.yaml index d22303f4fd..0088ad021f 100644 --- a/.github/workflows/mg.yaml +++ b/.github/workflows/mg.yaml @@ -4,9 +4,11 @@ name: MG Test on: push: - branches: [ main, dev ] + branches: + - main pull_request: - branches: [ main, dev ] + branches: + - main jobs: bad-pool-config-test: diff --git a/.github/workflows/rust-msrv.yaml b/.github/workflows/rust-msrv.yaml index 3384e4a2ff..62ba418b15 100644 --- a/.github/workflows/rust-msrv.yaml +++ b/.github/workflows/rust-msrv.yaml @@ -2,11 +2,9 @@ on: push: branches: - main - - dev pull_request: branches: - main - - dev name: MSRV 1.75 Check diff --git a/.github/workflows/semver-check.yaml b/.github/workflows/semver-check.yaml index 9a2a45ad3b..6543b473d3 100644 --- a/.github/workflows/semver-check.yaml +++ b/.github/workflows/semver-check.yaml @@ -4,11 +4,9 @@ on: push: branches: - "main" - - "dev" pull_request: branches: - "main" - - "dev" jobs: semver-check: diff --git a/.github/workflows/sv2-header-check.yaml b/.github/workflows/sv2-header-check.yaml index 62f5231fdf..b1b069a05c 100644 --- a/.github/workflows/sv2-header-check.yaml +++ b/.github/workflows/sv2-header-check.yaml @@ -1,8 +1,10 @@ on: push: - branches: [ main, dev ] + branches: + - main pull_request: - branches: [ main, dev ] + branches: + - main # Check sv2.h file is up to date with commit name: sv2.h Header Check diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 5b669ebc0b..6665a902f0 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -2,11 +2,9 @@ on: push: branches: - main - - dev pull_request: branches: - main - - dev name: Test, Prop Tests, Example Tests diff --git a/protocols/v2/const-sv2/README.md b/protocols/v2/const-sv2/README.md new file mode 100644 index 0000000000..1c919b895c --- /dev/null +++ b/protocols/v2/const-sv2/README.md @@ -0,0 +1,21 @@ +# const_sv2 + +[![crates.io](https://img.shields.io/crates/v/const_sv2.svg)](https://crates.io/crates/const_sv2) +[![docs.rs](https://docs.rs/const_sv2/badge.svg)](https://docs.rs/const_sv2) + +`const_sv2` is a Rust crate that provides essential constants for the SV2 (Stratum V2) protocol. These constants are crucial for message framing, encryption, and protocol-specific identifiers across various SV2 components, including Mining, Job Declaration, and Template Distribution protocols. + +## Main Components + +- **Protocol Constants**: Define key protocol discriminants, message types, and sizes for the SV2 binary protocol. +- **Encryption Support**: Includes constants for encryption using ChaChaPoly and ElligatorSwift encoding. +- **Channel Bits**: Defines whether specific messages are associated with a channel, simplifying protocol handling. +- **Modular**: Supports a `no_std` environment, enabling use in embedded systems or environments without a standard library. + +## Usage + +To use this crate, add it to your `Cargo.toml`: + +```toml +[dependencies] +const_sv2 = "2.0.0" diff --git a/protocols/v2/const-sv2/src/lib.rs b/protocols/v2/const-sv2/src/lib.rs index c9cda82770..2e248a6334 100644 --- a/protocols/v2/const-sv2/src/lib.rs +++ b/protocols/v2/const-sv2/src/lib.rs @@ -1,34 +1,123 @@ -//! Central repository for all the sv2 constants +//! This crate provides all constants used in the SV2 protocol. +//! These constants are essential for message framing, encryption, and +//! protocol-specific identifiers across various SV2 components, including +//! Mining, Job Declaration, and Template Distribution protocols. +//! +//! It also includes definitions for key encryption settings and message types, +//! ensuring consistency and accuracy when working with SV2's binary protocol. +//! These constants are used throughout the system to ensure that SV2 +//! messages are formatted and interpreted correctly. +//! +//! ### Discriminants for Stratum V2 (sub)protocols +//! Discriminants are unique identifiers used to distinguish between different +//! Stratum V2 (sub)protocols. Each protocol within the SV2 ecosystem has a +//! specific discriminant value that indicates its type, enabling the correct +//! interpretation and handling of messages. These discriminants ensure that +//! messages are processed by the appropriate protocol handlers, +//! thereby facilitating seamless communication across different components of +//! the SV2 architecture. More info can be found [on Chapter 03 of the Stratum V2 specs](https://github.com/stratum-mining/sv2-spec/blob/main/03-Protocol-Overview.md#3-protocol-overview). + +//! +//! ### Message Types +//! Message types in the SV2 protocol define the specific operations and data +//! exchanges between participants. Each type corresponds to a distinct action +//! or information transfer, facilitating communication in various contexts such +//! as mining operations, job declarations, and template distribution. Properly +//! identifying and handling these message types is crucial for maintaining +//! protocol compliance and ensuring seamless interactions within the SV2 +//! ecosystem. The message types are categorized into common types and those +//! specific to each subprotocol, providing clarity on their intended use and +//! interaction patterns. +//! +//! ### Channel Bits +//! The `channel bits` indicate whether a message is associated with a specific +//! channel. If the most significant bit of the `extension_type` (referred to as +//! `channel_msg`) is set, the message is related to a channel and includes a +//! `channel_id`. In this case, the first 4 bytes of the payload represent the +//! `channel_id` the message is destined for. + #![cfg_attr(feature = "no_std", no_std)] +/// Identifier for the extension_type field in the SV2 frame, indicating no +/// extensions. pub const EXTENSION_TYPE_NO_EXTENSION: u16 = 0; +/// Size of the SV2 frame header in bytes. pub const SV2_FRAME_HEADER_SIZE: usize = 6; + +// It's not used anywhere. +// Refactoring: deprecate it. pub const SV2_FRAME_HEADER_LEN_OFFSET: usize = 3; + +// It's not used anywhere. +// Refactoring: deprecate it. pub const SV2_FRAME_HEADER_LEN_END: usize = 3; + +/// Maximum size of an SV2 frame chunk in bytes. pub const SV2_FRAME_CHUNK_SIZE: usize = 65535; -// For now only CHACHA and AES are supported both have a MAC of 16 bytes +/// Size of the MAC for supported AEAD encryption algorithm (ChaChaPoly). pub const AEAD_MAC_LEN: usize = 16; +/// Size of the encrypted SV2 frame header, including the MAC. +// Refactoring: declared in sv2-ffi, and imported in framing_sv2/src/header.rs +// header.rs is then imported into codec_sv2 just for this constant pub const ENCRYPTED_SV2_FRAME_HEADER_SIZE: usize = SV2_FRAME_HEADER_SIZE + AEAD_MAC_LEN; + +/// Size of the Noise protocol frame header in bytes. +// Refactoring: declared in sv2-ffi, and imported in framing_sv2/src/header.rs +// header.rs is then imported into codec_sv2 just for this constant pub const NOISE_FRAME_HEADER_SIZE: usize = 2; + +// Refactoring: declared in sv2-ffi, and imported in framing_sv2/src/header.rs +// header.rs is then imported into codec_sv2 just for this constant, and the +// const there is not even used pub const NOISE_FRAME_HEADER_LEN_OFFSET: usize = 0; + +// It's not used anywhere. +// Refactoring: deprecate it. pub const NOISE_FRAME_MAX_SIZE: usize = u16::MAX as usize; +/// Size in bytes of the encoded elliptic curve point using ElligatorSwift +/// encoding. This encoding produces a 64-byte representation of the +/// X-coordinate of a secp256k1 curve point. pub const ELLSWIFT_ENCODING_SIZE: usize = 64; + +// Refactoring: the alias could be created where it's imported, or we could just +// use one name everywhere pub const RESPONDER_EXPECTED_HANDSHAKE_MESSAGE_SIZE: usize = ELLSWIFT_ENCODING_SIZE; + +// This is the same as AEAD_MAC_LEN. +// Refactoring: deprecate it. pub const MAC: usize = 16; + +/// Size in bytes of the encrypted ElligatorSwift encoded data, which includes +/// the original ElligatorSwift encoded data and a MAC for integrity +/// verification. pub const ENCRYPTED_ELLSWIFT_ENCODING_SIZE: usize = ELLSWIFT_ENCODING_SIZE + MAC; + +/// Size in bytes of the SIGNATURE_NOISE_MESSAGE, which contains information and +/// a signature for the handshake initiator, formatted according to the Noise +/// Protocol specifications. pub const SIGNATURE_NOISE_MESSAGE_SIZE: usize = 74; + +/// Size in bytes of the encrypted signature noise message, which includes the +/// SIGNATURE_NOISE_MESSAGE and a MAC for integrity verification. pub const ENCRYPTED_SIGNATURE_NOISE_MESSAGE_SIZE: usize = SIGNATURE_NOISE_MESSAGE_SIZE + MAC; + +/// Size in bytes of the handshake message expected by the initiator, +/// encompassing: +/// - ElligatorSwift encoded public key +/// - Encrypted ElligatorSwift encoding +/// - Encrypted SIGNATURE_NOISE_MESSAGE pub const INITIATOR_EXPECTED_HANDSHAKE_MESSAGE_SIZE: usize = ELLSWIFT_ENCODING_SIZE + ENCRYPTED_ELLSWIFT_ENCODING_SIZE + ENCRYPTED_SIGNATURE_NOISE_MESSAGE_SIZE; -/// If protocolName is less than or equal to 32 bytes in length, use protocolName with zero bytes -/// appended to make 32 bytes. Otherwise, apply HASH to it. For name = -/// "Noise_NX_Secp256k1+EllSwift_ChaChaPoly_SHA256", we need the hash. +/// If protocolName is less than or equal to 32 bytes in length, use +/// protocolName with zero bytes appended to make 32 bytes. Otherwise, apply +/// HASH to it. For name = "Noise_NX_Secp256k1+EllSwift_ChaChaPoly_SHA256", we +/// need the hash. More info can be found [at this link](https://github.com/stratum-mining/sv2-spec/blob/main/04-Protocol-Security.md#451-handshake-act-1-nx-handshake-part-1---e). pub const NOISE_HASHED_PROTOCOL_NAME_CHACHA: [u8; 32] = [ 46, 180, 120, 129, 32, 142, 158, 238, 31, 102, 159, 103, 198, 110, 231, 14, 169, 234, 136, 9, 13, 80, 63, 232, 48, 220, 75, 200, 62, 41, 191, 16, @@ -36,71 +125,85 @@ pub const NOISE_HASHED_PROTOCOL_NAME_CHACHA: [u8; 32] = [ // len = 1 // 47,53,45,41 = AESG +// We're dropping support for AESG. +// Refactoring: deprecate it. pub const NOISE_SUPPORTED_CIPHERS_MESSAGE: [u8; 5] = [1, 0x47, 0x53, 0x45, 0x41]; +// Discriminants for distinct Stratum V2 (sub)protocols. More info at https://github.com/stratum- +// mining/sv2-spec/blob/main/03-Protocol-Overview.md#3-protocol-overview pub const SV2_MINING_PROTOCOL_DISCRIMINANT: u8 = 0; pub const SV2_JOB_DECLARATION_PROTOCOL_DISCRIMINANT: u8 = 1; +// Refactoring: rename this into SV2_TEMPLATE_DISTRIBUTION_PROTOCOL_DISCRIMINANT pub const SV2_TEMPLATE_DISTR_PROTOCOL_DISCRIMINANT: u8 = 2; -// COMMON MESSAGES TYPES +// Common message types used across all Stratum V2 (sub)protocols. pub const MESSAGE_TYPE_SETUP_CONNECTION: u8 = 0x0; pub const MESSAGE_TYPE_SETUP_CONNECTION_SUCCESS: u8 = 0x1; pub const MESSAGE_TYPE_SETUP_CONNECTION_ERROR: u8 = 0x2; pub const MESSAGE_TYPE_CHANNEL_ENDPOINT_CHANGED: u8 = 0x3; -// TEMPLATE DISTRIBUTION PROTOCOL MESSAGES TYPES -pub const MESSAGE_TYPE_COINBASE_OUTPUT_DATA_SIZE: u8 = 0x70; -pub const MESSAGE_TYPE_NEW_TEMPLATE: u8 = 0x71; -pub const MESSAGE_TYPE_SET_NEW_PREV_HASH: u8 = 0x72; -pub const MESSAGE_TYPE_REQUEST_TRANSACTION_DATA: u8 = 0x73; -pub const MESSAGE_TYPE_REQUEST_TRANSACTION_DATA_SUCCESS: u8 = 0x74; -pub const MESSAGE_TYPE_REQUEST_TRANSACTION_DATA_ERROR: u8 = 0x75; -pub const MESSAGE_TYPE_SUBMIT_SOLUTION: u8 = 0x76; -// JOB DECLARATION PROTOCOL MESSAGES TYPES + +// Mining Protocol message types. +pub const MESSAGE_TYPE_OPEN_STANDARD_MINING_CHANNEL: u8 = 0x10; +pub const MESSAGE_TYPE_OPEN_STANDARD_MINING_CHANNEL_SUCCESS: u8 = 0x11; +pub const MESSAGE_TYPE_OPEN_MINING_CHANNEL_ERROR: u8 = 0x12; +pub const MESSAGE_TYPE_OPEN_EXTENDED_MINING_CHANNEL: u8 = 0x13; +// Refactoring: fix typo with MESSAGE_TYPE_OPEN_EXTENDED_MINING_CHANNEL_SUCCESS +pub const MESSAGE_TYPE_OPEN_EXTENDED_MINING_CHANNEL_SUCCES: u8 = 0x14; +pub const MESSAGE_TYPE_NEW_MINING_JOB: u8 = 0x15; +pub const MESSAGE_TYPE_UPDATE_CHANNEL: u8 = 0x16; +pub const MESSAGE_TYPE_UPDATE_CHANNEL_ERROR: u8 = 0x17; +pub const MESSAGE_TYPE_CLOSE_CHANNEL: u8 = 0x18; +pub const MESSAGE_TYPE_SET_EXTRANONCE_PREFIX: u8 = 0x19; +pub const MESSAGE_TYPE_SUBMIT_SHARES_STANDARD: u8 = 0x1a; +pub const MESSAGE_TYPE_SUBMIT_SHARES_EXTENDED: u8 = 0x1b; +pub const MESSAGE_TYPE_SUBMIT_SHARES_SUCCESS: u8 = 0x1c; +pub const MESSAGE_TYPE_SUBMIT_SHARES_ERROR: u8 = 0x1d; +pub const MESSAGE_TYPE_NEW_EXTENDED_MINING_JOB: u8 = 0x1f; +pub const MESSAGE_TYPE_MINING_SET_NEW_PREV_HASH: u8 = 0x20; +pub const MESSAGE_TYPE_SET_TARGET: u8 = 0x21; +pub const MESSAGE_TYPE_SET_CUSTOM_MINING_JOB: u8 = 0x22; +pub const MESSAGE_TYPE_SET_CUSTOM_MINING_JOB_SUCCESS: u8 = 0x23; +pub const MESSAGE_TYPE_SET_CUSTOM_MINING_JOB_ERROR: u8 = 0x24; + +// Refactoring: we need to move this to 0x04 and shift SETGROUPCHANNEL to 0x25 +// (we are not specs compliant now) +pub const MESSAGE_TYPE_RECONNECT: u8 = 0x25; +pub const MESSAGE_TYPE_SET_GROUP_CHANNEL: u8 = 0x26; + +// Job Declaration Protocol message types. pub const MESSAGE_TYPE_ALLOCATE_MINING_JOB_TOKEN: u8 = 0x50; pub const MESSAGE_TYPE_ALLOCATE_MINING_JOB_TOKEN_SUCCESS: u8 = 0x51; -pub const MESSAGE_TYPE_DECLARE_MINING_JOB: u8 = 0x57; -pub const MESSAGE_TYPE_DECLARE_MINING_JOB_SUCCESS: u8 = 0x58; -pub const MESSAGE_TYPE_DECLARE_MINING_JOB_ERROR: u8 = 0x59; pub const MESSAGE_TYPE_IDENTIFY_TRANSACTIONS: u8 = 0x53; pub const MESSAGE_TYPE_IDENTIFY_TRANSACTIONS_SUCCESS: u8 = 0x54; pub const MESSAGE_TYPE_PROVIDE_MISSING_TRANSACTIONS: u8 = 0x55; pub const MESSAGE_TYPE_PROVIDE_MISSING_TRANSACTIONS_SUCCESS: u8 = 0x56; +pub const MESSAGE_TYPE_DECLARE_MINING_JOB: u8 = 0x57; +pub const MESSAGE_TYPE_DECLARE_MINING_JOB_SUCCESS: u8 = 0x58; +pub const MESSAGE_TYPE_DECLARE_MINING_JOB_ERROR: u8 = 0x59; pub const MESSAGE_TYPE_SUBMIT_SOLUTION_JD: u8 = 0x60; -// MINING PROTOCOL MESSAGES TYPES -pub const MESSAGE_TYPE_CLOSE_CHANNEL: u8 = 0x18; -/// This has been cahnged before was 0x1e it can be that old Sv2 implementation still use the old -/// one but this means that old impl are not following Sv2 spec -pub const MESSAGE_TYPE_NEW_EXTENDED_MINING_JOB: u8 = 0x1f; -pub const MESSAGE_TYPE_NEW_MINING_JOB: u8 = 0x15; -pub const MESSAGE_TYPE_OPEN_EXTENDED_MINING_CHANNEL: u8 = 0x13; -pub const MESSAGE_TYPE_OPEN_EXTENDED_MINING_CHANNEL_SUCCES: u8 = 0x14; -// in the spec page 21 is defined OpenMiningChannelError valid for both extended and standard -// messages but in the spec page 40 are defined two different message types for -// OpenStandardMiningChannelError and OpenExtendedMiningChannelError -pub const MESSAGE_TYPE_OPEN_MINING_CHANNEL_ERROR: u8 = 0x12; -pub const MESSAGE_TYPE_OPEN_STANDARD_MINING_CHANNEL: u8 = 0x10; -pub const MESSAGE_TYPE_OPEN_STANDARD_MINING_CHANNEL_SUCCESS: u8 = 0x11; -pub const MESSAGE_TYPE_RECONNECT: u8 = 0x25; -pub const MESSAGE_TYPE_SET_CUSTOM_MINING_JOB: u8 = 0x22; -pub const MESSAGE_TYPE_SET_CUSTOM_MINING_JOB_ERROR: u8 = 0x24; -pub const MESSAGE_TYPE_SET_CUSTOM_MINING_JOB_SUCCESS: u8 = 0x23; -pub const MESSAGE_TYPE_SET_EXTRANONCE_PREFIX: u8 = 0x19; -pub const MESSAGE_TYPE_SET_GROUP_CHANNEL: u8 = 0x26; -pub const MESSAGE_TYPE_MINING_SET_NEW_PREV_HASH: u8 = 0x20; -pub const MESSAGE_TYPE_SET_TARGET: u8 = 0x21; -pub const MESSAGE_TYPE_SUBMIT_SHARES_ERROR: u8 = 0x1d; -pub const MESSAGE_TYPE_SUBMIT_SHARES_EXTENDED: u8 = 0x1b; -pub const MESSAGE_TYPE_SUBMIT_SHARES_STANDARD: u8 = 0x1a; -pub const MESSAGE_TYPE_SUBMIT_SHARES_SUCCESS: u8 = 0x1c; -pub const MESSAGE_TYPE_UPDATE_CHANNEL: u8 = 0x16; -pub const MESSAGE_TYPE_UPDATE_CHANNEL_ERROR: u8 = 0x17; -// COMMON MESSAGES CHANNEL BIT +// Template Distribution Protocol message types. +pub const MESSAGE_TYPE_COINBASE_OUTPUT_DATA_SIZE: u8 = 0x70; +pub const MESSAGE_TYPE_NEW_TEMPLATE: u8 = 0x71; +pub const MESSAGE_TYPE_SET_NEW_PREV_HASH: u8 = 0x72; +pub const MESSAGE_TYPE_REQUEST_TRANSACTION_DATA: u8 = 0x73; +pub const MESSAGE_TYPE_REQUEST_TRANSACTION_DATA_SUCCESS: u8 = 0x74; +pub const MESSAGE_TYPE_REQUEST_TRANSACTION_DATA_ERROR: u8 = 0x75; +pub const MESSAGE_TYPE_SUBMIT_SOLUTION: u8 = 0x76; + +// The `channel bits` indicate whether a message is associated with a specific +// channel. If the most significant bit of the `extension_type` (referred to as +// `channel_msg`) is set, the message is related to a channel and includes a +// `channel_id`. In this case, the first 4 bytes of the payload represent the +// `channel_id` the message is destined for. For the Job Declaration and +// Template Distribution protocols, the `channel_msg` bit is always unset. + pub const CHANNEL_BIT_SETUP_CONNECTION: bool = false; pub const CHANNEL_BIT_SETUP_CONNECTION_SUCCESS: bool = false; pub const CHANNEL_BIT_SETUP_CONNECTION_ERROR: bool = false; pub const CHANNEL_BIT_CHANNEL_ENDPOINT_CHANGED: bool = true; -// TEMPLATE DISTRIBUTION PROTOCOL MESSAGES CHANNEL BIT + +// For the Template Distribution protocol, the channel bit is always unset. pub const CHANNEL_BIT_COINBASE_OUTPUT_DATA_SIZE: bool = false; pub const CHANNEL_BIT_NEW_TEMPLATE: bool = false; pub const CHANNEL_BIT_SET_NEW_PREV_HASH: bool = false; @@ -108,7 +211,9 @@ pub const CHANNEL_BIT_REQUEST_TRANSACTION_DATA: bool = false; 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; -// JOB DECLARATION PROTOCOL MESSAGES CHANNEL BIT + +// In the Job Declaration protocol, the `channel_msg` bit is always unset, +// except for `SUBMIT_SOLUTION_JD`, which requires a specific channel reference. pub const CHANNEL_BIT_ALLOCATE_MINING_JOB_TOKEN: bool = false; pub const CHANNEL_BIT_ALLOCATE_MINING_JOB_TOKEN_SUCCESS: bool = false; pub const CHANNEL_BIT_DECLARE_MINING_JOB: bool = false; @@ -119,15 +224,13 @@ pub const CHANNEL_BIT_IDENTIFY_TRANSACTIONS_SUCCESS: 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_SUBMIT_SOLUTION_JD: bool = true; -// MINING PROTOCOL MESSAGES CHANNEL BIT + +// Channel bits in the Mining protocol vary depending on the message. pub const CHANNEL_BIT_CLOSE_CHANNEL: bool = true; pub const CHANNEL_BIT_NEW_EXTENDED_MINING_JOB: bool = true; pub const CHANNEL_BIT_NEW_MINING_JOB: bool = true; pub const CHANNEL_BIT_OPEN_EXTENDED_MINING_CHANNEL: bool = false; pub const CHANNEL_BIT_OPEN_EXTENDED_MINING_CHANNEL_SUCCES: bool = false; -// in the spec page 21 is defined OpenMiningChannelError valid for both extended and standard -// messages but in the spec page 40 are defined two different message types for -// OpenStandardMiningChannelError and OpenExtendedMiningChannelError pub const CHANNEL_BIT_OPEN_MINING_CHANNEL_ERROR: bool = false; pub const CHANNEL_BIT_OPEN_STANDARD_MINING_CHANNEL: bool = false; pub const CHANNEL_BIT_OPEN_STANDARD_MINING_CHANNEL_SUCCESS: bool = false; diff --git a/protocols/v2/sv2-ffi/sv2.h b/protocols/v2/sv2-ffi/sv2.h index 8ea99dd691..534c12e6e5 100644 --- a/protocols/v2/sv2-ffi/sv2.h +++ b/protocols/v2/sv2-ffi/sv2.h @@ -4,36 +4,59 @@ #include #include +/// Identifier for the extension_type field in the SV2 frame, indicating no +/// extensions. static const uint16_t EXTENSION_TYPE_NO_EXTENSION = 0; +/// Size of the SV2 frame header in bytes. static const uintptr_t SV2_FRAME_HEADER_SIZE = 6; static const uintptr_t SV2_FRAME_HEADER_LEN_OFFSET = 3; static const uintptr_t SV2_FRAME_HEADER_LEN_END = 3; +/// Maximum size of an SV2 frame chunk in bytes. static const uintptr_t SV2_FRAME_CHUNK_SIZE = 65535; +/// Size of the MAC for supported AEAD encryption algorithm (ChaChaPoly). static const uintptr_t AEAD_MAC_LEN = 16; +/// Size of the encrypted SV2 frame header, including the MAC. static const uintptr_t ENCRYPTED_SV2_FRAME_HEADER_SIZE = (SV2_FRAME_HEADER_SIZE + AEAD_MAC_LEN); +/// Size of the Noise protocol frame header in bytes. static const uintptr_t NOISE_FRAME_HEADER_SIZE = 2; static const uintptr_t NOISE_FRAME_HEADER_LEN_OFFSET = 0; +/// Size in bytes of the encoded elliptic curve point using ElligatorSwift +/// encoding. This encoding produces a 64-byte representation of the +/// X-coordinate of a secp256k1 curve point. static const uintptr_t ELLSWIFT_ENCODING_SIZE = 64; static const uintptr_t RESPONDER_EXPECTED_HANDSHAKE_MESSAGE_SIZE = ELLSWIFT_ENCODING_SIZE; static const uintptr_t MAC = 16; +/// Size in bytes of the encrypted ElligatorSwift encoded data, which includes +/// the original ElligatorSwift encoded data and a MAC for integrity +/// verification. static const uintptr_t ENCRYPTED_ELLSWIFT_ENCODING_SIZE = (ELLSWIFT_ENCODING_SIZE + MAC); +/// Size in bytes of the SIGNATURE_NOISE_MESSAGE, which contains information and +/// a signature for the handshake initiator, formatted according to the Noise +/// Protocol specifications. static const uintptr_t SIGNATURE_NOISE_MESSAGE_SIZE = 74; +/// Size in bytes of the encrypted signature noise message, which includes the +/// SIGNATURE_NOISE_MESSAGE and a MAC for integrity verification. static const uintptr_t ENCRYPTED_SIGNATURE_NOISE_MESSAGE_SIZE = (SIGNATURE_NOISE_MESSAGE_SIZE + MAC); +/// Size in bytes of the handshake message expected by the initiator, +/// encompassing: +/// - ElligatorSwift encoded public key +/// - Encrypted ElligatorSwift encoding +/// - Encrypted SIGNATURE_NOISE_MESSAGE static const uintptr_t INITIATOR_EXPECTED_HANDSHAKE_MESSAGE_SIZE = ((ELLSWIFT_ENCODING_SIZE + ENCRYPTED_ELLSWIFT_ENCODING_SIZE) + ENCRYPTED_SIGNATURE_NOISE_MESSAGE_SIZE); static const uint8_t SV2_MINING_PROTOCOL_DISCRIMINANT = 0; @@ -50,85 +73,83 @@ static const uint8_t MESSAGE_TYPE_SETUP_CONNECTION_ERROR = 2; static const uint8_t MESSAGE_TYPE_CHANNEL_ENDPOINT_CHANGED = 3; -static const uint8_t MESSAGE_TYPE_COINBASE_OUTPUT_DATA_SIZE = 112; +static const uint8_t MESSAGE_TYPE_OPEN_STANDARD_MINING_CHANNEL = 16; -static const uint8_t MESSAGE_TYPE_NEW_TEMPLATE = 113; +static const uint8_t MESSAGE_TYPE_OPEN_STANDARD_MINING_CHANNEL_SUCCESS = 17; -static const uint8_t MESSAGE_TYPE_SET_NEW_PREV_HASH = 114; +static const uint8_t MESSAGE_TYPE_OPEN_MINING_CHANNEL_ERROR = 18; -static const uint8_t MESSAGE_TYPE_REQUEST_TRANSACTION_DATA = 115; +static const uint8_t MESSAGE_TYPE_OPEN_EXTENDED_MINING_CHANNEL = 19; -static const uint8_t MESSAGE_TYPE_REQUEST_TRANSACTION_DATA_SUCCESS = 116; +static const uint8_t MESSAGE_TYPE_OPEN_EXTENDED_MINING_CHANNEL_SUCCES = 20; -static const uint8_t MESSAGE_TYPE_REQUEST_TRANSACTION_DATA_ERROR = 117; +static const uint8_t MESSAGE_TYPE_NEW_MINING_JOB = 21; -static const uint8_t MESSAGE_TYPE_SUBMIT_SOLUTION = 118; +static const uint8_t MESSAGE_TYPE_UPDATE_CHANNEL = 22; -static const uint8_t MESSAGE_TYPE_ALLOCATE_MINING_JOB_TOKEN = 80; +static const uint8_t MESSAGE_TYPE_UPDATE_CHANNEL_ERROR = 23; -static const uint8_t MESSAGE_TYPE_ALLOCATE_MINING_JOB_TOKEN_SUCCESS = 81; +static const uint8_t MESSAGE_TYPE_CLOSE_CHANNEL = 24; -static const uint8_t MESSAGE_TYPE_DECLARE_MINING_JOB = 87; +static const uint8_t MESSAGE_TYPE_SET_EXTRANONCE_PREFIX = 25; -static const uint8_t MESSAGE_TYPE_DECLARE_MINING_JOB_SUCCESS = 88; +static const uint8_t MESSAGE_TYPE_SUBMIT_SHARES_STANDARD = 26; -static const uint8_t MESSAGE_TYPE_DECLARE_MINING_JOB_ERROR = 89; +static const uint8_t MESSAGE_TYPE_SUBMIT_SHARES_EXTENDED = 27; -static const uint8_t MESSAGE_TYPE_IDENTIFY_TRANSACTIONS = 83; +static const uint8_t MESSAGE_TYPE_SUBMIT_SHARES_SUCCESS = 28; -static const uint8_t MESSAGE_TYPE_IDENTIFY_TRANSACTIONS_SUCCESS = 84; +static const uint8_t MESSAGE_TYPE_SUBMIT_SHARES_ERROR = 29; -static const uint8_t MESSAGE_TYPE_PROVIDE_MISSING_TRANSACTIONS = 85; +static const uint8_t MESSAGE_TYPE_NEW_EXTENDED_MINING_JOB = 31; -static const uint8_t MESSAGE_TYPE_PROVIDE_MISSING_TRANSACTIONS_SUCCESS = 86; +static const uint8_t MESSAGE_TYPE_MINING_SET_NEW_PREV_HASH = 32; -static const uint8_t MESSAGE_TYPE_SUBMIT_SOLUTION_JD = 96; +static const uint8_t MESSAGE_TYPE_SET_TARGET = 33; -static const uint8_t MESSAGE_TYPE_CLOSE_CHANNEL = 24; +static const uint8_t MESSAGE_TYPE_SET_CUSTOM_MINING_JOB = 34; -/// This has been cahnged before was 0x1e it can be that old Sv2 implementation still use the old -/// one but this means that old impl are not following Sv2 spec -static const uint8_t MESSAGE_TYPE_NEW_EXTENDED_MINING_JOB = 31; +static const uint8_t MESSAGE_TYPE_SET_CUSTOM_MINING_JOB_SUCCESS = 35; -static const uint8_t MESSAGE_TYPE_NEW_MINING_JOB = 21; +static const uint8_t MESSAGE_TYPE_SET_CUSTOM_MINING_JOB_ERROR = 36; -static const uint8_t MESSAGE_TYPE_OPEN_EXTENDED_MINING_CHANNEL = 19; +static const uint8_t MESSAGE_TYPE_RECONNECT = 37; -static const uint8_t MESSAGE_TYPE_OPEN_EXTENDED_MINING_CHANNEL_SUCCES = 20; +static const uint8_t MESSAGE_TYPE_SET_GROUP_CHANNEL = 38; -static const uint8_t MESSAGE_TYPE_OPEN_MINING_CHANNEL_ERROR = 18; +static const uint8_t MESSAGE_TYPE_ALLOCATE_MINING_JOB_TOKEN = 80; -static const uint8_t MESSAGE_TYPE_OPEN_STANDARD_MINING_CHANNEL = 16; +static const uint8_t MESSAGE_TYPE_ALLOCATE_MINING_JOB_TOKEN_SUCCESS = 81; -static const uint8_t MESSAGE_TYPE_OPEN_STANDARD_MINING_CHANNEL_SUCCESS = 17; +static const uint8_t MESSAGE_TYPE_IDENTIFY_TRANSACTIONS = 83; -static const uint8_t MESSAGE_TYPE_RECONNECT = 37; +static const uint8_t MESSAGE_TYPE_IDENTIFY_TRANSACTIONS_SUCCESS = 84; -static const uint8_t MESSAGE_TYPE_SET_CUSTOM_MINING_JOB = 34; +static const uint8_t MESSAGE_TYPE_PROVIDE_MISSING_TRANSACTIONS = 85; -static const uint8_t MESSAGE_TYPE_SET_CUSTOM_MINING_JOB_ERROR = 36; +static const uint8_t MESSAGE_TYPE_PROVIDE_MISSING_TRANSACTIONS_SUCCESS = 86; -static const uint8_t MESSAGE_TYPE_SET_CUSTOM_MINING_JOB_SUCCESS = 35; +static const uint8_t MESSAGE_TYPE_DECLARE_MINING_JOB = 87; -static const uint8_t MESSAGE_TYPE_SET_EXTRANONCE_PREFIX = 25; +static const uint8_t MESSAGE_TYPE_DECLARE_MINING_JOB_SUCCESS = 88; -static const uint8_t MESSAGE_TYPE_SET_GROUP_CHANNEL = 38; +static const uint8_t MESSAGE_TYPE_DECLARE_MINING_JOB_ERROR = 89; -static const uint8_t MESSAGE_TYPE_MINING_SET_NEW_PREV_HASH = 32; +static const uint8_t MESSAGE_TYPE_SUBMIT_SOLUTION_JD = 96; -static const uint8_t MESSAGE_TYPE_SET_TARGET = 33; +static const uint8_t MESSAGE_TYPE_COINBASE_OUTPUT_DATA_SIZE = 112; -static const uint8_t MESSAGE_TYPE_SUBMIT_SHARES_ERROR = 29; +static const uint8_t MESSAGE_TYPE_NEW_TEMPLATE = 113; -static const uint8_t MESSAGE_TYPE_SUBMIT_SHARES_EXTENDED = 27; +static const uint8_t MESSAGE_TYPE_SET_NEW_PREV_HASH = 114; -static const uint8_t MESSAGE_TYPE_SUBMIT_SHARES_STANDARD = 26; +static const uint8_t MESSAGE_TYPE_REQUEST_TRANSACTION_DATA = 115; -static const uint8_t MESSAGE_TYPE_SUBMIT_SHARES_SUCCESS = 28; +static const uint8_t MESSAGE_TYPE_REQUEST_TRANSACTION_DATA_SUCCESS = 116; -static const uint8_t MESSAGE_TYPE_UPDATE_CHANNEL = 22; +static const uint8_t MESSAGE_TYPE_REQUEST_TRANSACTION_DATA_ERROR = 117; -static const uint8_t MESSAGE_TYPE_UPDATE_CHANNEL_ERROR = 23; +static const uint8_t MESSAGE_TYPE_SUBMIT_SOLUTION = 118; static const bool CHANNEL_BIT_SETUP_CONNECTION = false; diff --git a/roles/pool/src/lib/mining_pool/mod.rs b/roles/pool/src/lib/mining_pool/mod.rs index ac756bf8ac..008975a5a7 100644 --- a/roles/pool/src/lib/mining_pool/mod.rs +++ b/roles/pool/src/lib/mining_pool/mod.rs @@ -402,7 +402,7 @@ impl Pool { debug!("New connection from {}", address); let (receiver, sender): (Receiver, Sender) = - network_helpers::plain_connection_tokio::PlainConnection::new(stream).await; + network_helpers_sv2::plain_connection_tokio::PlainConnection::new(stream).await; handle_result!( status_tx, diff --git a/roles/test-utils/mining-device/README.md b/roles/test-utils/mining-device/README.md index 4065c2c30b..03ad7241a5 100644 --- a/roles/test-utils/mining-device/README.md +++ b/roles/test-utils/mining-device/README.md @@ -3,19 +3,58 @@ Header only sv2 cpu miner. ``` -Usage: mining-device [OPTIONS] --address-pool +Usage: mining_device [OPTIONS] --address-pool Options: - -p, --pubkey-pool Pool pub key, when left empty the pool certificate is not checked - -i, --id-device Sometimes used by the pool to identify the device - -a, --address-pool Address of the pool in this format ip:port or domain:port - --handicap This value is used to slow down the cpu miner, it represents the number of micro-seconds that are awaited between hashes [default: 0] - --id-user User id, used when a new channel is opened, it can be used by the pool to identify the miner - -h, --help Print help - -V, --version Print version + -p, --pubkey-pool + Pool pub key, when left empty the pool certificate is not checked + -i, --id-device + Sometimes used by the pool to identify the device + -a, --address-pool + Address of the pool in this format ip:port or domain:port + --handicap + This value is used to slow down the cpu miner, it represents the number of micro-seconds that are awaited between hashes [default: 0] + --id-user + User id, used when a new channel is opened, it can be used by the pool to identify the miner + --nominal-hashrate-multiplier + This floating point number is used to modify the advertised nominal hashrate when opening a channel with the upstream. + If 0.0 < nominal_hashrate_multiplier < 1.0, the CPU miner will advertise a nominal hashrate that is smaller than its real capacity. + If nominal_hashrate_multiplier > 1.0, the CPU miner will advertise a nominal hashrate that is bigger than its real capacity. + If empty, the CPU miner will simply advertise its real capacity. + -h, --help + Print help + -V, --version + Print version ``` Usage example: ``` cargo run --release -- --address-pool 127.0.0.1:20000 --id-device device_id::SOLO::bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh ``` + +## handicap + +CPU mining could damage the system due to excessive heat. + +The `--handicap` parameter should be used as a safety mechanism to slow down the hashrate in order to preserve hardware. + +## nominal hashrate multiplier + +Let's imagine that: +- the upstream wants to receive shares every ~100s (on average) +- the CPU miner nominal hashrate is 1k H/s + +Maybe we want to do a test where we don't want to wait ~100s before a share is submitted by the CPU miner. + +In that case, we need the CPU miner to advertise a smaller hashrate, which will force the upstream to set a lower +difficulty target. + +The `--nominal-hashrate-multiplier` can be used to advertise a custom nominal hashrate. + +In the scenario described above, we could launch the CPU miner with `--nominal-hashrate-multiplier 0.01`. + +The CPU miner would advertise 0.01k H/s, which would cause the upstream to set the difficulty target such that the CPU miner would find a share within ~1s. + +This feature can also be used to advertise a bigger nominal hashrate by using values above `1.0`. + +That can also be useful for testing difficulty adjustment algorithms on Sv2 upstreams. \ No newline at end of file diff --git a/roles/test-utils/mining-device/src/lib/mod.rs b/roles/test-utils/mining-device/src/lib/mod.rs index abfa1d9691..af4404c58c 100644 --- a/roles/test-utils/mining-device/src/lib/mod.rs +++ b/roles/test-utils/mining-device/src/lib/mod.rs @@ -8,6 +8,7 @@ use std::{ atomic::{AtomicBool, Ordering}, Arc, }, + thread::available_parallelism, time::Duration, }; use tokio::net::TcpStream; @@ -42,6 +43,7 @@ pub async fn connect( device_id: Option, user_id: Option, handicap: u32, + nominal_hashrate_multiplier: Option, ) { let address = address .clone() @@ -77,7 +79,16 @@ pub async fn connect( .await .unwrap(); info!("Pool noise connection established at {}", address); - Device::start(receiver, sender, address, device_id, user_id, handicap).await + Device::start( + receiver, + sender, + address, + device_id, + user_id, + handicap, + nominal_hashrate_multiplier, + ) + .await } pub type Message = MiningDeviceMessages<'static>; @@ -193,13 +204,21 @@ pub struct Device { notify_changes_to_mining_thread: NewWorkNotifier, } -fn open_channel(device_id: Option) -> OpenStandardMiningChannel<'static> { +fn open_channel( + device_id: Option, + nominal_hashrate_multiplier: Option, + handicap: u32, +) -> OpenStandardMiningChannel<'static> { let user_identity = device_id.unwrap_or_default().try_into().unwrap(); let id: u32 = 10; info!("Measuring CPU hashrate"); - let p = std::thread::available_parallelism().unwrap().get() as u32 - 3; - let nominal_hash_rate = measure_hashrate(5) as f32 * p as f32; - info!("Pc hashrate is {}", nominal_hash_rate); + let measured_hashrate = measure_hashrate(5, handicap) as f32; + info!("Measured CPU hashrate is {}", measured_hashrate); + let nominal_hash_rate = match nominal_hashrate_multiplier { + Some(m) => measured_hashrate * m, + None => measured_hashrate, + }; + info!("MINING DEVICE: send open channel with request id {}", id); OpenStandardMiningChannel { request_id: id.into(), @@ -217,6 +236,7 @@ impl Device { device_id: Option, user_id: Option, handicap: u32, + nominal_hashrate_multiplier: Option, ) { let setup_connection_handler = Arc::new(Mutex::new(SetupConnectionHandler::new())); SetupConnectionHandler::setup( @@ -244,8 +264,9 @@ impl Device { sender: notify_changes_to_mining_thread, }, }; - let open_channel = - MiningDeviceMessages::Mining(Mining::OpenStandardMiningChannel(open_channel(user_id))); + let open_channel = MiningDeviceMessages::Mining(Mining::OpenStandardMiningChannel( + open_channel(user_id, nominal_hashrate_multiplier, handicap), + )); let frame: StdFrame = open_channel.try_into().unwrap(); self_.sender.send(frame.into()).await.unwrap(); let self_mutex = std::sync::Arc::new(Mutex::new(self_)); @@ -597,7 +618,7 @@ impl NextShareOutcome { } // returns hashrate based on how fast the device hashes over the given duration -fn measure_hashrate(duration_secs: u64) -> f64 { +fn measure_hashrate(duration_secs: u64, handicap: u32) -> f64 { let mut rng = thread_rng(); let prev_hash: [u8; 32] = generate_random_32_byte_array().to_vec().try_into().unwrap(); let prev_hash = Hash::from_inner(prev_hash); @@ -619,7 +640,7 @@ fn measure_hashrate(duration_secs: u64) -> f64 { let start_time = Instant::now(); let mut hashes: u64 = 0; let duration = Duration::from_secs(duration_secs); - let mut miner = Miner::new(0); + let mut miner = Miner::new(handicap); // We put the target to 0 we are only interested in how many hashes per unit of time we can do // and do not want to be botherd by messages about valid shares found. miner.new_target(vec![0_u8; 32]); @@ -631,7 +652,12 @@ fn measure_hashrate(duration_secs: u64) -> f64 { } let elapsed_secs = start_time.elapsed().as_secs_f64(); - hashes as f64 / elapsed_secs + let hashrate_single_thread = hashes as f64 / elapsed_secs; + + // we just measured for a single thread, need to multiply by the available parallelism + let p = available_parallelism().unwrap().get(); + + hashrate_single_thread * p as f64 } fn generate_random_32_byte_array() -> [u8; 32] { let mut rng = thread_rng(); @@ -648,11 +674,7 @@ fn start_mining_threads( tokio::task::spawn(async move { let mut killers: Vec> = vec![]; loop { - let available_parallelism = u32::max( - 2, - std::thread::available_parallelism().unwrap().get() as u32, - ); - let p = available_parallelism - 1; + let p = available_parallelism().unwrap().get() as u32; let unit = u32::MAX / p; while have_new_job.recv().await.is_ok() { while let Some(killer) = killers.pop() { diff --git a/roles/test-utils/mining-device/src/main.rs b/roles/test-utils/mining-device/src/main.rs index 02a19c12f0..74bff1b3f2 100644 --- a/roles/test-utils/mining-device/src/main.rs +++ b/roles/test-utils/mining-device/src/main.rs @@ -39,6 +39,14 @@ struct Args { help = "User id, used when a new channel is opened, it can be used by the pool to identify the miner" )] id_user: Option, + #[arg( + long, + help = "This floating point number is used to modify the advertised nominal hashrate when opening a channel with the upstream.\ + \nIf 0.0 < nominal_hashrate_multiplier < 1.0, the CPU miner will advertise a nominal hashrate that is smaller than its real capacity.\ + \nIf nominal_hashrate_multiplier > 1.0, the CPU miner will advertise a nominal hashrate that is bigger than its real capacity.\ + \nIf empty, the CPU miner will simply advertise its real capacity." + )] + nominal_hashrate_multiplier: Option, } #[tokio::main(flavor = "current_thread")] @@ -52,6 +60,7 @@ async fn main() { args.id_device, args.id_user, args.handicap, + args.nominal_hashrate_multiplier, ) .await; } diff --git a/roles/tests-integration/tests/common/mod.rs b/roles/tests-integration/tests/common/mod.rs index 5d37b39d5e..4f5c980093 100644 --- a/roles/tests-integration/tests/common/mod.rs +++ b/roles/tests-integration/tests/common/mod.rs @@ -66,10 +66,11 @@ pub struct TemplateProvider { impl TemplateProvider { pub fn start(port: u16) -> Self { - let path_name = format!("/tmp/.template-provider-{}", port); - let temp_dir = PathBuf::from(&path_name); + let temp_dir = PathBuf::from("/tmp/.template-provider"); let mut conf = Conf::default(); + let staticdir = format!(".bitcoin-{}", port); + conf.staticdir = Some(temp_dir.join(staticdir)); let port = format!("-sv2port={}", port); conf.args.extend(vec![ "-txindex=1", @@ -80,7 +81,6 @@ impl TemplateProvider { "-sv2feedelta=1000", "-loglevel=sv2:trace", ]); - conf.staticdir = Some(temp_dir.join(".bitcoin")); let os = env::consts::OS; let arch = env::consts::ARCH;