Skip to content

Binary sv2 cleanup#2200

Merged
GitGab19 merged 16 commits into
stratum-mining:mainfrom
Shourya742:2026-06-16-binary-sv2-cleanup
Jul 2, 2026
Merged

Binary sv2 cleanup#2200
GitGab19 merged 16 commits into
stratum-mining:mainfrom
Shourya742:2026-06-16-binary-sv2-cleanup

Conversation

@Shourya742

@Shourya742 Shourya742 commented Jun 16, 2026

Copy link
Copy Markdown
Member

closes: #2171 #2173 #2175 #2176 #2177 #2178 #2179 #2213 #2214 #2215 #2216

Apart from the issues mentioned above, this PR introduces several additional improvements:

  1. Removes a significant amount of redundant code.
  2. Expands the API surface. In particular, conversions for copy types are now lossless, while conversions for variable-sized types are performed through TryFrom. (More details can be found in the companion sv2-apps PR. I'm open to suggestions for alternative APIs these additions are primarily based on my experience working with the sv2-apps codebase.)
  3. Introduces a dedicated MAC type, bringing us into full compliance with the specification.
  4. Replaces a large number of handwritten From implementations with MBE macros, significantly reducing duplication.
  5. Fixes issues in the streaming APIs. Previously, certain code paths could lead to unbounded reads, creating potential attack vectors. Reads are now properly bounded, and recursion-related bounds have been removed from the writer implementation.
  6. Removes U32Ref and U8Owned, as they do not provide any meaningful structural advantages.
  7. Adds iterator support for Seq types, allowing users to iterate over elements and perform in-place modifications more ergonomically.

companion stratum-mining/sv2-apps#576

@Shourya742

Copy link
Copy Markdown
Member Author

Awesome, the test are passing, atleast I have not broken any invariants. Need to improve API ergonomics and should be good to go.

@Shourya742 Shourya742 force-pushed the 2026-06-16-binary-sv2-cleanup branch 4 times, most recently from 4a79675 to 07b0f31 Compare June 22, 2026 15:28
@Shourya742 Shourya742 marked this pull request as ready for review June 22, 2026 15:39
@Shourya742 Shourya742 requested review from GitGab19 and plebhash June 22, 2026 15:39
@Shourya742

Copy link
Copy Markdown
Member Author

I will open issues for all the extra points mentioned in the description.

@GitGab19

Copy link
Copy Markdown
Member

My clanker found this:

High: the new bounded from_reader can still block/read until EOF on an invalid B032 length. [decodable.rs (line 70)](/tmp/stratum-pr2200/sv2/binary-sv2/src/codec/decodable.rs:70) treats every ReadError as “need one more byte”, but [inner.rs (line 109)](/tmp/stratum-pr2200/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs:109) also returns ReadError when a variable-sized type declares more than its max, e.g. B032 header 33. For messages with B032 fields, a peer can send an oversized length and keep the socket open; the loop keeps reading instead of rejecting the message. I’d split “insufficient bytes” from “declared size exceeds max”, or make from_reader retry only on the former.

@Shourya742 Shourya742 force-pushed the 2026-06-16-binary-sv2-cleanup branch 2 times, most recently from a1f2b6a to fe3c973 Compare June 24, 2026 17:10
@GitGab19

Copy link
Copy Markdown
Member

@Shourya742 just a suggestion about the point raised by the clanker:

Yes. The core fix is to stop using ReadError for two different meanings:

  1. “I need more bytes to know the size”
  2. “The size is known and invalid”

Right now both flow through ReadError, and decodable.rs treats every ReadError as recoverable by reading one more byte.

The best fix is in Inner::expected_length / expected_length_for_reader:

// pseudo-shape
if payload_len <= MAXSIZE {
    Ok(payload_len + HEADERSIZE)
} else {
    Err(Error::ValueExceedsMaxSize(
        ISFIXED,
        SIZE,
        HEADERSIZE,
        MAXSIZE,
        data.to_vec(),
        payload_len,
    ))
}

For the reader path:

if expected_length <= MAXSIZE {
    Ok(expected_length)
} else {
    Err(Error::ValueExceedsMaxSize(
        ISFIXED,
        SIZE,
        HEADERSIZE,
        MAXSIZE,
        header.to_vec(),
        expected_length,
    ))
}

Then Decodable::from_reader can keep retrying only on true partial-read errors:

Err(Error::OutOfBound | Error::ReadError(_, _)) => read_one_more_byte()
Err(error) => return Err(error)

With that change, B032 input [33] would return immediately because the header is complete and declares a payload larger than 32. It would no longer read/block waiting for 33 bytes.

A slightly cleaner design would add a dedicated error like Error::InvalidDeclaredLength { declared, max }, because ValueExceedsMaxSize carries a value vector and is a bit awkward for “length header is invalid”. But if the PR wants a small surgical fix, returning ValueExceedsMaxSize from the known-invalid length paths is enough.

Comment thread sv2/binary-sv2/src/codec/impls.rs
Comment thread sv2/binary-sv2/src/lib.rs
@Shourya742 Shourya742 force-pushed the 2026-06-16-binary-sv2-cleanup branch from 5580dc6 to 772a173 Compare June 26, 2026 17:16
@Shourya742

Copy link
Copy Markdown
Member Author

Loupe tests are in, and all are passing, I will link the commits to corresponding loupe issue for reference.

@Shourya742

Copy link
Copy Markdown
Member Author

@GitGab19 the commits might be a bit different now, I rewrote the history with test and your suggestions.

@Shourya742 Shourya742 force-pushed the 2026-06-16-binary-sv2-cleanup branch 2 times, most recently from 3c25807 to 224a7de Compare June 29, 2026 05:58
Shourya742 added a commit to Shourya742/stratum that referenced this pull request Jun 30, 2026
issue: stratum-mining#2214, in binary_sv2 we had try_from, from impls
from all primitive types to Encodable and decodable variants which were very repetitive. This commit
adds macros to remove repetitions and make the file more sane to look at.

Another note; We are removing try_from from Encodable to primitive, more info here:
stratum-mining#2200 (comment)
@Shourya742 Shourya742 force-pushed the 2026-06-16-binary-sv2-cleanup branch from 224a7de to 16e8677 Compare June 30, 2026 08:15
Comment on lines +418 to +443
impl<const SIZE: usize> Inner<'_, true, SIZE, 0, 0> {
/// Returns the payload bytes as an array reference.
pub fn as_array(&self) -> &[u8; SIZE] {
self.as_bytes()
.try_into()
.expect("fixed-size SV2 byte wrapper must always match SIZE")
}

/// Copies the payload bytes into an array.
pub fn to_array(&self) -> [u8; SIZE] {
*self.as_array()
}

/// Consumes the value and returns the payload bytes as an array.
pub fn into_array(self) -> [u8; SIZE] {
match self {
Inner::Ref(data) => data
.try_into()
.expect("fixed-size SV2 byte wrapper must always match SIZE"),
Inner::Owned(data) => data
.try_into()
.expect("fixed-size SV2 byte wrapper must always match SIZE"),
}
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked gpt-5.3-codex whether these .expects were infallible:

Short answer: conditionally, not absolutely.

  • They are infallible if the invariant holds: for Inner<'_, true, SIZE, 0, 0>, payload length is always exactly SIZE.
  • With current API, that invariant is usually enforced by checked constructors (TryFrom, from_bytes_, from_reader_), so in normal flow these expects should not fire.
  • But Inner is a pub enum, so callers can still construct invalid fixed values directly (e.g. Inner::Owned(vec![0; SIZE - 1])), and then as_array() / into_array() will panic.

So: infallible by convention, not by type-level encapsulation.

not a blocker for me, but I thought I'd bring it up anyways

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We gonna be removing Inner, thats the end goal.

Shourya742 added 16 commits July 1, 2026 22:41
issue: stratum-mining#2214, in binary_sv2 we had try_from, from impls
from all primitive types to Encodable and decodable variants which were very repetitive. This commit
adds macros to remove repetitions and make the file more sane to look at.

Another note; We are removing try_from from Encodable to primitive, more info here:
stratum-mining#2200 (comment)
…rait

This is more of a clean up commit, where we remove repetitive hex conv
code in primitive's display trait implementation.
This solves this issue: stratum-mining#2171

There are other variants of same issue which says that we were using unchecked variant which would
bypass the length invariant, and at the same time, this is not something
we would require, so we are removing them
…ad of using unchecked conversions

* In this commit we remove the unchecked variant from_bytes_ and to_slice_
across the crate, as we no longer use them and no point in using unsafe
methods.

* Decode now returns an error, previous this was heavly utilizing the
unchecked meaning without any constraints to the addition. This
now forces us to return error in case if some variants are not met.
Part of removal of U32AsRef: stratum-mining#2215,
we don't really need this.
Completes the issue: stratum-mining#2215, removes
u8Owned and uses u8 instead
Here, we remove error type variants across the crate which are not
really needed.
The implementation was also handling conversion incorrectly
Solves issue: stratum-mining#2175,

via this we are fully adhering to specs and supporting all primitives
mentioned
…olicy

We are not doing unbounded reads with with_reader
This solves:
1. stratum-mining#2173 -> Here we fix the size of FIXED
primitive to be equal to SIZE and not one.
2. stratum-mining#2177 -> make sure header is appended
to the writer.
…esn't leak internal implementation

This solves issue:

1. stratum-mining#2176 -> Making sure the vector
conversion doesn't break length invariant.
2. stratum-mining#2179 -> Making sure sv2 primitives
doesn't leak internal representation
3. stratum-mining#2216 -> Adding iterators to seq
primitives.

This commit also updates sv2_to_sv1 to not use list type internal representation
…y_sv2

This solves the issue: stratum-mining#2213, here we
introduce new custom API, and deprecate old rigid API's
@Shourya742 Shourya742 force-pushed the 2026-06-16-binary-sv2-cleanup branch from 16e8677 to 7d89f23 Compare July 1, 2026 17:11
@GitGab19 GitGab19 merged commit 7fa5aa1 into stratum-mining:main Jul 2, 2026
14 checks passed
@Shourya742 Shourya742 deleted the 2026-06-16-binary-sv2-cleanup branch July 2, 2026 10:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment