-
Notifications
You must be signed in to change notification settings - Fork 127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rust docs for protocols::v2::codec-sv2
#1040
Merged
GitGab19
merged 16 commits into
stratum-mining:main
from
rrybarczyk:2024-07-rust-docs-protocols-codec-sv2
Oct 14, 2024
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
cdbbb0f
Add codec unencrypted and encrypted ex
rrybarczyk 4d9017f
codec doc cmts
rrybarczyk 7b912e6
Add codec README
rrybarczyk 602e8b8
add docs for signature_message.rs
Shourya742 d4917df
add docs for aed_cipher.rs
Shourya742 9980ee5
add docs for cipher_state.rs
Shourya742 30a94c0
add docs for error.rs
Shourya742 b1ecb9e
add docs for lib.rs
Shourya742 ec41fcd
add docs for handshake.rs
Shourya742 579ad8d
add docs for initiator.rs
Shourya742 82e2814
add docs for responder.rs
Shourya742 443726c
add readme to noise_sv2 crate
Shourya742 e03e33a
add noise handshake example
Shourya742 a3b4369
add noise specific code coverage badge
Shourya742 ba3dbdd
removed doc comments with general comments
Shourya742 7356d57
Merge branch 'main' into 2024-07-rust-docs-protocols-codec-sv2
rrybarczyk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# `codec_sv2` | ||
|
||
[![crates.io](https://img.shields.io/crates/v/codec_sv2.svg)](https://crates.io/crates/codec_sv2) | ||
[![docs.rs](https://docs.rs/codec_sv2/badge.svg)](https://docs.rs/codec_sv2) | ||
[![rustc+](https://img.shields.io/badge/rustc-1.75.0%2B-lightgrey.svg)](https://blog.rust-lang.org/2023/12/28/Rust-1.75.0.html) | ||
[![license](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](https://github.com/stratum-mining/stratum/blob/main/LICENSE.md) | ||
[![codecov](https://codecov.io/gh/stratum-mining/stratum/branch/main/graph/badge.svg?flag=codec_sv2-coverage)](https://codecov.io/gh/stratum-mining/stratum) | ||
|
||
rrybarczyk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
`codec_sv2` provides the message encoding and decoding functionality for the Stratum V2 (Sv2) | ||
protocol, handling secure communication between Sv2 roles. This crate abstracts the complexity of | ||
message encoding/decoding with optional Noise protocol support, ensuring both regular and encrypted | ||
messages can be serialized, transmitted, and decoded consistently and reliably. | ||
|
||
## Main Components | ||
|
||
- **Encoder**: Encodes Sv2 messages with or without Noise protocol support. | ||
- **Decoder**: Decodes Sv2 messages with or without Noise protocol support. | ||
- **Handshake State**: Manages the current Noise protocol handshake state of the codec. | ||
|
||
|
||
## Usage | ||
|
||
To include this crate in your project, run: | ||
|
||
```bash | ||
cargo add codec_sv2 | ||
``` | ||
|
||
This crate can be built with the following feature flags: | ||
|
||
- `noise_sv2`: Enables support for Noise protocol encryption and decryption. | ||
- `with_buffer_pool`: Enables buffer pooling for more efficient memory management. | ||
- `with_serde`: builds [`binary_sv2`](https://crates.io/crates/binary_sv2) and | ||
[`buffer_sv2`](https://crates.io/crates/buffer_sv2) crates with `serde`-based encoding and | ||
decoding. Note that this feature flag is only used for the Message Generator, and deprecated | ||
for any other kind of usage. It will likely be fully deprecated in the future. | ||
|
||
### Examples | ||
|
||
This crate provides two examples demonstrating how to encode and decode Sv2 frames: | ||
|
||
1. **[Unencrypted Example](https://github.com/stratum-mining/stratum/blob/main/protocols/v2/codec-sv2/examples/unencrypted.rs)**: | ||
Encode and decode standard Sv2 frames, detailing the message serialization and transmission | ||
process for unencrypted communications. | ||
|
||
2. **[Encrypted Example](https://github.com/stratum-mining/stratum/blob/main/protocols/v2/codec-sv2/examples/encrypted.rs)**: | ||
Encode and decode Sv2 frames with Noise protocol encryption, detailing the entire encryption | ||
handshake and transport phase and serialization and transmission process for encrypted | ||
communications. | ||
plebhash marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
// # Using Sv2 Codec with Noise Encryption | ||
// | ||
// This example demonstrates how to use the `codec-sv2` crate to encode and decode Sv2 frames | ||
// with Noise protocol encryption over a TCP connection. It showcases how to: | ||
// | ||
// * Perform a Noise handshake between the sender and receiver. | ||
// * Create an arbitrary custom message type (`CustomMessage`) for encryption/encoding and | ||
// decryption/decoding. | ||
// * Encode the message into an encrypted Sv2 frame using Noise. | ||
// * Send the encrypted frame over a TCP connection. | ||
// * Decode the encrypted Sv2 frame on the receiving side after completing the Noise handshake. | ||
// | ||
// ## Run | ||
// | ||
// ``` | ||
// cargo run --example encrypted --features noise_sv2 | ||
// ``` | ||
|
||
use binary_sv2::{binary_codec_sv2, Deserialize, Serialize}; | ||
#[cfg(feature = "noise_sv2")] | ||
use codec_sv2::{ | ||
Error, HandshakeRole, Initiator, NoiseEncoder, Responder, StandardEitherFrame, | ||
StandardNoiseDecoder, StandardSv2Frame, State, Sv2Frame, | ||
}; | ||
#[cfg(feature = "noise_sv2")] | ||
use const_sv2::{ | ||
INITIATOR_EXPECTED_HANDSHAKE_MESSAGE_SIZE, RESPONDER_EXPECTED_HANDSHAKE_MESSAGE_SIZE, | ||
}; | ||
#[cfg(feature = "noise_sv2")] | ||
use key_utils::{Secp256k1PublicKey, Secp256k1SecretKey}; | ||
use std::convert::TryInto; | ||
#[cfg(feature = "noise_sv2")] | ||
use std::{ | ||
io::{Read, Write}, | ||
net::{TcpListener, TcpStream}, | ||
}; | ||
|
||
// Arbitrary message type. | ||
// Supported Sv2 message types are listed in the [Sv2 Spec Message | ||
// Types](https://github.com/stratum-mining/sv2-spec/blob/main/08-Message-Types.md). | ||
#[cfg(feature = "noise_sv2")] | ||
const CUSTOM_MSG_TYPE: u8 = 0xff; | ||
|
||
// Emulate a TCP connection | ||
#[cfg(feature = "noise_sv2")] | ||
const TCP_ADDR: &str = "127.0.0.1:3333"; | ||
|
||
#[cfg(feature = "noise_sv2")] | ||
const AUTHORITY_PUBLIC_K: &str = "9auqWEzQDVyd2oe1JVGFLMLHZtCo2FFqZwtKA5gd9xbuEu7PH72"; | ||
#[cfg(feature = "noise_sv2")] | ||
const AUTHORITY_PRIVATE_K: &str = "mkDLTBBRxdBv998612qipDYoTK3YUrqLe8uWw7gu3iXbSrn2n"; | ||
#[cfg(feature = "noise_sv2")] | ||
const CERT_VALIDITY: std::time::Duration = std::time::Duration::from_secs(3600); | ||
|
||
// Example message type. | ||
// In practice, all Sv2 messages are defined in the following crates: | ||
// * `common_messages_sv2` | ||
// * `mining_sv2` | ||
// * `job_declaration_sv2` | ||
// * `template_distribution_sv2` | ||
#[derive(Debug, Serialize, Deserialize, Clone)] | ||
pub struct CustomMessage { | ||
nonce: u16, | ||
} | ||
|
||
#[cfg(feature = "noise_sv2")] | ||
fn main() { | ||
// Start a receiving listener | ||
let listener_receiver = TcpListener::bind(TCP_ADDR).expect("Failed to bind TCP listener"); | ||
|
||
// Start a sender stream | ||
let mut stream_sender: TcpStream = | ||
TcpStream::connect(TCP_ADDR).expect("Failed to connect to TCP stream"); | ||
|
||
// Start a receiver stream | ||
let mut stream_receiver: TcpStream = listener_receiver | ||
.incoming() | ||
.next() | ||
.expect("Failed to accept incoming TCP stream") | ||
.expect("Failed to connect to incoming TCP stream"); | ||
|
||
// Handshake | ||
let authority_public_k: Secp256k1PublicKey = AUTHORITY_PUBLIC_K | ||
.to_string() | ||
.try_into() | ||
.expect("Failed to convert receiver public key to Secp256k1PublicKey"); | ||
|
||
let authority_private_k: Secp256k1SecretKey = AUTHORITY_PRIVATE_K | ||
.to_string() | ||
.try_into() | ||
.expect("Failed to convert receiver private key to Secp256k1PublicKey"); | ||
|
||
let initiator = Initiator::from_raw_k(authority_public_k.into_bytes()) | ||
.expect("Failed to create initiator role from raw pub key"); | ||
|
||
let responder = Responder::from_authority_kp( | ||
&authority_public_k.into_bytes(), | ||
&authority_private_k.into_bytes(), | ||
CERT_VALIDITY, | ||
) | ||
.expect("Failed to initialize responder from pub/key pair and/or cert"); | ||
|
||
let mut sender_state = State::initialized(HandshakeRole::Initiator(initiator)); | ||
let mut receiver_state = State::initialized(HandshakeRole::Responder(responder)); | ||
|
||
let first_message = sender_state | ||
.step_0() | ||
.expect("Initiator failed first step of handshake"); | ||
let first_message: [u8; RESPONDER_EXPECTED_HANDSHAKE_MESSAGE_SIZE] = first_message | ||
.get_payload_when_handshaking() | ||
.try_into() | ||
.expect("Handshake remote invlaid message"); | ||
|
||
let (second_message, receiver_state) = receiver_state | ||
.step_1(first_message) | ||
.expect("Responder failed second step of handshake"); | ||
let second_message: [u8; INITIATOR_EXPECTED_HANDSHAKE_MESSAGE_SIZE] = second_message | ||
.get_payload_when_handshaking() | ||
.try_into() | ||
.expect("Handshake remote invlaid message"); | ||
|
||
let sender_state = sender_state | ||
.step_2(second_message) | ||
.expect("Initiator failed third step of handshake"); | ||
|
||
let mut sender_state = match sender_state { | ||
State::Transport(c) => State::with_transport_mode(c), | ||
_ => panic!("todo"), | ||
}; | ||
|
||
let mut receiver_state = match receiver_state { | ||
State::Transport(c) => State::with_transport_mode(c), | ||
_ => panic!("todo"), | ||
}; | ||
|
||
// Create a message | ||
let nonce = 1337; | ||
let msg = CustomMessage { nonce }; | ||
let msg_type = CUSTOM_MSG_TYPE; | ||
// Unique identifier of the extension describing the protocol message, as defined by Sv2 Framing | ||
let extension_type = 0; | ||
// This message is intended for the receiver, so set to false | ||
let channel_msg = false; | ||
|
||
let frame = StandardEitherFrame::<CustomMessage>::Sv2( | ||
Sv2Frame::from_message(msg, msg_type, extension_type, channel_msg) | ||
.expect("Failed to create the frame"), | ||
); | ||
|
||
let mut encoder = NoiseEncoder::<CustomMessage>::new(); | ||
let encoded_frame = encoder | ||
.encode(frame, &mut sender_state) | ||
.expect("Failed to encode the frame"); | ||
|
||
// Send the encoded frame | ||
stream_sender | ||
.write_all(encoded_frame.as_slice()) | ||
.expect("Failed to send the encoded frame"); | ||
|
||
// Initialize the decoder | ||
let mut decoder = StandardNoiseDecoder::<CustomMessage>::new(); | ||
|
||
let mut decoded_frame; | ||
|
||
// Continuously read the frame from the TCP stream into the decoder buffer until the full | ||
// message is received. | ||
// | ||
// Note: The length of the payload is defined in a header field. Every call to `next_frame` | ||
// will return a `MissingBytes` error, until the full payload is received. | ||
loop { | ||
let decoder_buf = decoder.writable(); | ||
|
||
// Read the frame header into the decoder buffer | ||
stream_receiver | ||
.read_exact(decoder_buf) | ||
.expect("Failed to read the encoded frame header"); | ||
|
||
let result = decoder.next_frame(&mut receiver_state); | ||
match result { | ||
Ok(frame) => { | ||
let frame: StandardSv2Frame<CustomMessage> = frame | ||
.try_into() | ||
.expect("Failed to decode frame into Sv2Frame"); | ||
decoded_frame = frame; | ||
break; | ||
} | ||
Err(Error::MissingBytes(_)) => {} | ||
Err(_) => panic!("Failed to decode the frame"), | ||
} | ||
} | ||
|
||
// Parse the decoded frame header and payload | ||
let decoded_frame_header = decoded_frame | ||
.get_header() | ||
.expect("Failed to get the frame header"); | ||
|
||
let decoded_msg: CustomMessage = binary_sv2::from_bytes(decoded_frame.payload()) | ||
.expect("Failed to extract the message from the payload"); | ||
|
||
// Assert that the decoded message is as expected | ||
assert_eq!(decoded_frame_header.msg_type(), CUSTOM_MSG_TYPE); | ||
assert_eq!(decoded_msg.nonce, nonce); | ||
} | ||
|
||
#[cfg(not(feature = "noise_sv2"))] | ||
fn main() { | ||
eprintln!("Noise feature not enabled. Skipping example."); | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Shourya742 is this right? Looks like it is showing 0%, but it should be 10.61% I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nah, there's some issue with how the individual crate coverage is being reported. I'm still looking into it. We can keep the badge for now, since the issue won’t change the fact that it points to the correct coverage number once it's fixed. But if you'd prefer, we can remove it for now and then re-add it once its fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assuming the URL will remain the same, let's save one extra PR and just leave it here