From 444c5ff1b70c99cfed24797d1d4838d0ed365f64 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 19 Jun 2026 14:30:13 +0530 Subject: [PATCH 01/20] Add macros instead of handwritten impls issue: https://github.com/stratum-mining/stratum/issues/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. --- sv2/binary-sv2/src/codec/impls.rs | 992 +++++++----------------------- 1 file changed, 206 insertions(+), 786 deletions(-) diff --git a/sv2/binary-sv2/src/codec/impls.rs b/sv2/binary-sv2/src/codec/impls.rs index 1e98b1bc29..bf0bdad965 100644 --- a/sv2/binary-sv2/src/codec/impls.rs +++ b/sv2/binary-sv2/src/codec/impls.rs @@ -9,789 +9,209 @@ use crate::{ Error, }; use alloc::vec::Vec; -use core::convert::{TryFrom, TryInto}; - -// IMPL GET MARKER FOR PRIMITIVES -impl GetMarker for bool { - fn get_marker() -> FieldMarker { - FieldMarker::Primitive(PrimitiveMarker::Bool) - } -} -impl GetMarker for u8 { - fn get_marker() -> FieldMarker { - FieldMarker::Primitive(PrimitiveMarker::U8) - } -} -impl GetMarker for u16 { - fn get_marker() -> FieldMarker { - FieldMarker::Primitive(PrimitiveMarker::U16) - } -} -impl GetMarker for U24 { - fn get_marker() -> FieldMarker { - FieldMarker::Primitive(PrimitiveMarker::U24) - } -} -impl GetMarker for u32 { - fn get_marker() -> FieldMarker { - FieldMarker::Primitive(PrimitiveMarker::U32) - } -} -impl GetMarker for f32 { - fn get_marker() -> FieldMarker { - FieldMarker::Primitive(PrimitiveMarker::F32) - } -} -impl GetMarker for u64 { - fn get_marker() -> FieldMarker { - FieldMarker::Primitive(PrimitiveMarker::U64) - } -} -impl GetMarker for U256<'_> { - fn get_marker() -> FieldMarker { - FieldMarker::Primitive(PrimitiveMarker::U256) - } -} -impl GetMarker for Signature<'_> { - fn get_marker() -> FieldMarker { - FieldMarker::Primitive(PrimitiveMarker::Signature) - } -} -impl GetMarker for B032<'_> { - fn get_marker() -> FieldMarker { - FieldMarker::Primitive(PrimitiveMarker::B032) - } -} -impl GetMarker for B0255<'_> { - fn get_marker() -> FieldMarker { - FieldMarker::Primitive(PrimitiveMarker::B0255) - } -} -impl GetMarker for B064K<'_> { - fn get_marker() -> FieldMarker { - FieldMarker::Primitive(PrimitiveMarker::B064K) - } -} -impl GetMarker for B016M<'_> { - fn get_marker() -> FieldMarker { - FieldMarker::Primitive(PrimitiveMarker::B016M) - } -} -impl GetMarker for U32AsRef<'_> { - fn get_marker() -> FieldMarker { - FieldMarker::Primitive(PrimitiveMarker::U32AsRef) - } -} - -// IMPL DECODABLE FOR PRIMITIVES - -impl<'a> Decodable<'a> for u8 { - fn get_structure(_: &[u8]) -> Result, Error> { - Ok(vec![PrimitiveMarker::U8.into()]) - } - - fn from_decoded_fields(mut data: Vec>) -> Result { - data.pop().ok_or(Error::NoDecodableFieldPassed)?.try_into() - } -} -impl<'a> Decodable<'a> for u16 { - fn get_structure(_: &[u8]) -> Result, Error> { - Ok(vec![PrimitiveMarker::U16.into()]) - } - - fn from_decoded_fields(mut data: Vec>) -> Result { - data.pop().ok_or(Error::NoDecodableFieldPassed)?.try_into() - } -} -impl<'a> Decodable<'a> for u32 { - fn get_structure(_: &[u8]) -> Result, Error> { - Ok(vec![PrimitiveMarker::U32.into()]) - } - - fn from_decoded_fields(mut data: Vec>) -> Result { - data.pop().ok_or(Error::NoDecodableFieldPassed)?.try_into() - } -} -impl<'a> Decodable<'a> for f32 { - fn get_structure(_: &[u8]) -> Result, Error> { - Ok(vec![PrimitiveMarker::F32.into()]) - } - - fn from_decoded_fields(mut data: Vec>) -> Result { - data.pop().ok_or(Error::NoDecodableFieldPassed)?.try_into() - } -} -impl<'a> Decodable<'a> for u64 { - fn get_structure(_: &[u8]) -> Result, Error> { - Ok(vec![PrimitiveMarker::U64.into()]) - } - - fn from_decoded_fields(mut data: Vec>) -> Result { - data.pop().ok_or(Error::NoDecodableFieldPassed)?.try_into() - } -} -impl<'a> Decodable<'a> for bool { - fn get_structure(_: &[u8]) -> Result, Error> { - Ok(vec![PrimitiveMarker::Bool.into()]) - } - - fn from_decoded_fields(mut data: Vec>) -> Result { - data.pop().ok_or(Error::NoDecodableFieldPassed)?.try_into() - } -} -impl<'a> Decodable<'a> for U24 { - fn get_structure(_: &[u8]) -> Result, Error> { - Ok(vec![PrimitiveMarker::U24.into()]) - } - - fn from_decoded_fields(mut data: Vec>) -> Result { - data.pop().ok_or(Error::NoDecodableFieldPassed)?.try_into() - } -} -impl<'a> Decodable<'a> for U256<'a> { - fn get_structure(_: &[u8]) -> Result, Error> { - Ok(vec![PrimitiveMarker::U256.into()]) - } - - fn from_decoded_fields(mut data: Vec>) -> Result { - data.pop().ok_or(Error::NoDecodableFieldPassed)?.try_into() - } -} -impl<'a> Decodable<'a> for Signature<'a> { - fn get_structure(_: &[u8]) -> Result, Error> { - Ok(vec![PrimitiveMarker::Signature.into()]) - } - - fn from_decoded_fields(mut data: Vec>) -> Result { - data.pop().ok_or(Error::NoDecodableFieldPassed)?.try_into() - } -} -impl<'a> Decodable<'a> for B032<'a> { - fn get_structure(_: &[u8]) -> Result, Error> { - Ok(vec![PrimitiveMarker::B032.into()]) - } - - fn from_decoded_fields(mut data: Vec>) -> Result { - data.pop().ok_or(Error::NoDecodableFieldPassed)?.try_into() - } -} -impl<'a> Decodable<'a> for B0255<'a> { - fn get_structure(_: &[u8]) -> Result, Error> { - Ok(vec![PrimitiveMarker::B0255.into()]) - } - - fn from_decoded_fields(mut data: Vec>) -> Result { - data.pop().ok_or(Error::NoDecodableFieldPassed)?.try_into() - } -} -impl<'a> Decodable<'a> for B064K<'a> { - fn get_structure(_: &[u8]) -> Result, Error> { - Ok(vec![PrimitiveMarker::B064K.into()]) - } - - fn from_decoded_fields(mut data: Vec>) -> Result { - data.pop().ok_or(Error::NoDecodableFieldPassed)?.try_into() - } -} -impl<'a> Decodable<'a> for B016M<'a> { - fn get_structure(_: &[u8]) -> Result, Error> { - Ok(vec![PrimitiveMarker::B016M.into()]) - } - - fn from_decoded_fields(mut data: Vec>) -> Result { - data.pop().ok_or(Error::NoDecodableFieldPassed)?.try_into() - } -} - -impl<'a> Decodable<'a> for U32AsRef<'a> { - fn get_structure(_: &[u8]) -> Result, Error> { - Ok(vec![PrimitiveMarker::U32AsRef.into()]) - } - - fn from_decoded_fields(mut data: Vec>) -> Result { - data.pop().ok_or(Error::NoDecodableFieldPassed)?.try_into() - } -} - -// IMPL TRY_FROM PRIMITIVE FOR PRIMITIVEs - -impl<'a> TryFrom> for u8 { - type Error = Error; - - fn try_from(value: DecodablePrimitive<'a>) -> Result { - match value { - DecodablePrimitive::U8(val) => Ok(val), - _ => Err(Error::PrimitiveConversionError), - } - } -} -impl<'a> TryFrom> for u16 { - type Error = Error; - - fn try_from(value: DecodablePrimitive<'a>) -> Result { - match value { - DecodablePrimitive::U16(val) => Ok(val), - _ => Err(Error::PrimitiveConversionError), - } - } -} -impl<'a> TryFrom> for u32 { - type Error = Error; - - fn try_from(value: DecodablePrimitive<'a>) -> Result { - match value { - DecodablePrimitive::U32(val) => Ok(val), - _ => Err(Error::PrimitiveConversionError), - } - } -} -impl<'a> TryFrom> for f32 { - type Error = Error; - - fn try_from(value: DecodablePrimitive<'a>) -> Result { - match value { - DecodablePrimitive::F32(val) => Ok(val), - _ => Err(Error::PrimitiveConversionError), - } - } -} -impl<'a> TryFrom> for u64 { - type Error = Error; - - fn try_from(value: DecodablePrimitive<'a>) -> Result { - match value { - DecodablePrimitive::U64(val) => Ok(val), - _ => Err(Error::PrimitiveConversionError), - } - } -} -impl<'a> TryFrom> for bool { - type Error = Error; - - fn try_from(value: DecodablePrimitive<'a>) -> Result { - match value { - DecodablePrimitive::Bool(val) => Ok(val), - _ => Err(Error::PrimitiveConversionError), - } - } -} - -impl<'a> TryFrom> for U24 { - type Error = Error; - - fn try_from(value: DecodablePrimitive<'a>) -> Result { - match value { - DecodablePrimitive::U24(val) => Ok(val), - _ => Err(Error::PrimitiveConversionError), - } - } -} -impl<'a> TryFrom> for U256<'a> { - type Error = Error; - - fn try_from(value: DecodablePrimitive<'a>) -> Result { - match value { - DecodablePrimitive::U256(val) => Ok(val), - _ => Err(Error::PrimitiveConversionError), - } - } -} -impl<'a> TryFrom> for Signature<'a> { - type Error = Error; - - fn try_from(value: DecodablePrimitive<'a>) -> Result { - match value { - DecodablePrimitive::Signature(val) => Ok(val), - _ => Err(Error::PrimitiveConversionError), - } - } -} -impl<'a> TryFrom> for B032<'a> { - type Error = Error; - - fn try_from(value: DecodablePrimitive<'a>) -> Result { - match value { - DecodablePrimitive::B032(val) => Ok(val), - _ => Err(Error::PrimitiveConversionError), - } - } -} -impl<'a> TryFrom> for B0255<'a> { - type Error = Error; - - fn try_from(value: DecodablePrimitive<'a>) -> Result { - match value { - DecodablePrimitive::B0255(val) => Ok(val), - _ => Err(Error::PrimitiveConversionError), - } - } -} -impl<'a> TryFrom> for B064K<'a> { - type Error = Error; - - fn try_from(value: DecodablePrimitive<'a>) -> Result { - match value { - DecodablePrimitive::B064K(val) => Ok(val), - _ => Err(Error::PrimitiveConversionError), - } - } -} -impl<'a> TryFrom> for B016M<'a> { - type Error = Error; - - fn try_from(value: DecodablePrimitive<'a>) -> Result { - match value { - DecodablePrimitive::B016M(val) => Ok(val), - _ => Err(Error::PrimitiveConversionError), - } - } -} -impl<'a> TryFrom> for U32AsRef<'a> { - type Error = Error; - - fn try_from(value: DecodablePrimitive<'a>) -> Result { - match value { - DecodablePrimitive::U32AsRef(val) => Ok(val), - _ => Err(Error::PrimitiveConversionError), - } - } -} - -// IMPL TRY_FROM DECODEC FIELD FOR PRIMITIVES - -impl<'a> TryFrom> for u8 { - type Error = Error; - - fn try_from(value: DecodableField<'a>) -> Result { - match value { - DecodableField::Primitive(p) => p.try_into(), - _ => Err(Error::DecodableConversionError), - } - } -} -impl<'a> TryFrom> for u16 { - type Error = Error; - - fn try_from(value: DecodableField<'a>) -> Result { - match value { - DecodableField::Primitive(p) => p.try_into(), - _ => Err(Error::DecodableConversionError), - } - } -} -impl<'a> TryFrom> for u32 { - type Error = Error; - - fn try_from(value: DecodableField<'a>) -> Result { - match value { - DecodableField::Primitive(p) => p.try_into(), - _ => Err(Error::DecodableConversionError), - } - } -} -impl<'a> TryFrom> for f32 { - type Error = Error; - - fn try_from(value: DecodableField<'a>) -> Result { - match value { - DecodableField::Primitive(p) => p.try_into(), - _ => Err(Error::DecodableConversionError), - } - } -} -impl<'a> TryFrom> for u64 { - type Error = Error; - - fn try_from(value: DecodableField<'a>) -> Result { - match value { - DecodableField::Primitive(p) => p.try_into(), - _ => Err(Error::DecodableConversionError), - } - } -} -impl<'a> TryFrom> for bool { - type Error = Error; - - fn try_from(value: DecodableField<'a>) -> Result { - match value { - DecodableField::Primitive(p) => p.try_into(), - _ => Err(Error::DecodableConversionError), - } - } -} -impl<'a> TryFrom> for U24 { - type Error = Error; - - fn try_from(value: DecodableField<'a>) -> Result { - match value { - DecodableField::Primitive(p) => p.try_into(), - _ => Err(Error::DecodableConversionError), - } - } -} -impl<'a> TryFrom> for U256<'a> { - type Error = Error; - - fn try_from(value: DecodableField<'a>) -> Result { - match value { - DecodableField::Primitive(p) => p.try_into(), - _ => Err(Error::DecodableConversionError), - } - } -} -impl<'a> TryFrom> for Signature<'a> { - type Error = Error; - - fn try_from(value: DecodableField<'a>) -> Result { - match value { - DecodableField::Primitive(p) => p.try_into(), - _ => Err(Error::DecodableConversionError), - } - } -} -impl<'a> TryFrom> for B032<'a> { - type Error = Error; - - fn try_from(value: DecodableField<'a>) -> Result { - match value { - DecodableField::Primitive(p) => p.try_into(), - _ => Err(Error::DecodableConversionError), - } - } -} -impl<'a> TryFrom> for B0255<'a> { - type Error = Error; - - fn try_from(value: DecodableField<'a>) -> Result { - match value { - DecodableField::Primitive(p) => p.try_into(), - _ => Err(Error::DecodableConversionError), - } - } -} -impl<'a> TryFrom> for B064K<'a> { - type Error = Error; - - fn try_from(value: DecodableField<'a>) -> Result { - match value { - DecodableField::Primitive(p) => p.try_into(), - _ => Err(Error::DecodableConversionError), - } - } -} -impl<'a> TryFrom> for B016M<'a> { - type Error = Error; - - fn try_from(value: DecodableField<'a>) -> Result { - match value { - DecodableField::Primitive(p) => p.try_into(), - _ => Err(Error::DecodableConversionError), - } - } -} -impl<'a> TryFrom> for U32AsRef<'a> { - type Error = Error; - - fn try_from(value: DecodableField<'a>) -> Result { - match value { - DecodableField::Primitive(p) => p.try_into(), - _ => Err(Error::DecodableConversionError), - } - } -} - -// IMPL FROM PRIMITIVES FOR ENCODED FIELD - -impl From for EncodableField<'_> { - fn from(v: bool) -> Self { - EncodableField::Primitive(EncodablePrimitive::Bool(v)) - } -} -impl<'a> TryFrom> for bool { - type Error = Error; - - fn try_from(value: EncodableField<'a>) -> Result { - match value { - EncodableField::Primitive(EncodablePrimitive::Bool(v)) => Ok(v), - _ => Err(Error::NonPrimitiveTypeCannotBeEncoded), - } - } -} -impl From for EncodableField<'_> { - fn from(v: u8) -> Self { - EncodableField::Primitive(EncodablePrimitive::U8(v)) - } -} -impl<'a> TryFrom> for u8 { - type Error = Error; - - fn try_from(value: EncodableField<'a>) -> Result { - match value { - EncodableField::Primitive(EncodablePrimitive::U8(v)) => Ok(v), - _ => Err(Error::NonPrimitiveTypeCannotBeEncoded), - } - } -} -impl From for EncodableField<'_> { - fn from(v: u16) -> Self { - EncodableField::Primitive(EncodablePrimitive::U16(v)) - } -} -impl<'a> TryFrom> for u16 { - type Error = Error; - - fn try_from(value: EncodableField<'a>) -> Result { - match value { - EncodableField::Primitive(EncodablePrimitive::U16(v)) => Ok(v), - _ => Err(Error::NonPrimitiveTypeCannotBeEncoded), - } - } -} -impl From for EncodableField<'_> { - fn from(v: U24) -> Self { - EncodableField::Primitive(EncodablePrimitive::U24(v)) - } -} -impl<'a> TryFrom> for U24 { - type Error = Error; - - fn try_from(value: EncodableField<'a>) -> Result { - match value { - EncodableField::Primitive(EncodablePrimitive::U24(v)) => Ok(v), - _ => Err(Error::NonPrimitiveTypeCannotBeEncoded), - } - } -} -impl From for EncodableField<'_> { - fn from(v: u32) -> Self { - EncodableField::Primitive(EncodablePrimitive::U32(v)) - } -} -impl<'a> TryFrom> for u32 { - type Error = Error; - - fn try_from(value: EncodableField<'a>) -> Result { - match value { - EncodableField::Primitive(EncodablePrimitive::U32(v)) => Ok(v), - _ => Err(Error::NonPrimitiveTypeCannotBeEncoded), - } - } -} -impl From for EncodableField<'_> { - fn from(v: f32) -> Self { - EncodableField::Primitive(EncodablePrimitive::F32(v)) - } -} -impl<'a> TryFrom> for f32 { - type Error = Error; - - fn try_from(value: EncodableField<'a>) -> Result { - match value { - EncodableField::Primitive(EncodablePrimitive::F32(v)) => Ok(v), - _ => Err(Error::NonPrimitiveTypeCannotBeEncoded), - } - } -} -impl From for EncodableField<'_> { - fn from(v: u64) -> Self { - EncodableField::Primitive(EncodablePrimitive::U64(v)) - } -} -impl<'a> TryFrom> for u64 { - type Error = Error; - - fn try_from(value: EncodableField<'a>) -> Result { - match value { - EncodableField::Primitive(EncodablePrimitive::U64(v)) => Ok(v), - _ => Err(Error::NonPrimitiveTypeCannotBeEncoded), - } - } -} -impl<'a> From> for EncodableField<'a> { - fn from(v: U256<'a>) -> Self { - EncodableField::Primitive(EncodablePrimitive::U256(v)) - } -} -impl<'a> TryFrom> for U256<'a> { - type Error = Error; - - fn try_from(value: EncodableField<'a>) -> Result { - match value { - EncodableField::Primitive(EncodablePrimitive::U256(v)) => Ok(v), - _ => Err(Error::NonPrimitiveTypeCannotBeEncoded), - } - } -} -impl<'a> From> for EncodableField<'a> { - fn from(v: Signature<'a>) -> Self { - EncodableField::Primitive(EncodablePrimitive::Signature(v)) - } -} -impl<'a> TryFrom> for Signature<'a> { - type Error = Error; - - fn try_from(value: EncodableField<'a>) -> Result { - match value { - EncodableField::Primitive(EncodablePrimitive::Signature(v)) => Ok(v), - _ => Err(Error::NonPrimitiveTypeCannotBeEncoded), - } - } -} -impl<'a> From> for EncodableField<'a> { - fn from(v: B032<'a>) -> Self { - EncodableField::Primitive(EncodablePrimitive::B032(v)) - } -} -impl<'a> From> for EncodableField<'a> { - fn from(v: B0255<'a>) -> Self { - EncodableField::Primitive(EncodablePrimitive::B0255(v)) - } -} -impl<'a> TryFrom> for B032<'a> { - type Error = Error; - - fn try_from(value: EncodableField<'a>) -> Result { - match value { - EncodableField::Primitive(EncodablePrimitive::B032(v)) => Ok(v), - _ => Err(Error::NonPrimitiveTypeCannotBeEncoded), - } - } -} -impl<'a> TryFrom> for B0255<'a> { - type Error = Error; - - fn try_from(value: EncodableField<'a>) -> Result { - match value { - EncodableField::Primitive(EncodablePrimitive::B0255(v)) => Ok(v), - _ => Err(Error::NonPrimitiveTypeCannotBeEncoded), - } - } -} -impl<'a> From> for EncodableField<'a> { - fn from(v: B064K<'a>) -> Self { - EncodableField::Primitive(EncodablePrimitive::B064K(v)) - } -} -impl<'a> TryFrom> for B064K<'a> { - type Error = Error; - - fn try_from(value: EncodableField<'a>) -> Result { - match value { - EncodableField::Primitive(EncodablePrimitive::B064K(v)) => Ok(v), - _ => Err(Error::NonPrimitiveTypeCannotBeEncoded), - } - } -} -impl<'a> From> for EncodableField<'a> { - fn from(v: B016M<'a>) -> Self { - EncodableField::Primitive(EncodablePrimitive::B016M(v)) - } -} -impl<'a> TryFrom> for B016M<'a> { - type Error = Error; - - fn try_from(value: EncodableField<'a>) -> Result { - match value { - EncodableField::Primitive(EncodablePrimitive::B016M(v)) => Ok(v), - _ => Err(Error::NonPrimitiveTypeCannotBeEncoded), - } - } -} -impl<'a> From> for EncodableField<'a> { - fn from(v: U32AsRef<'a>) -> Self { - EncodableField::Primitive(EncodablePrimitive::U32AsRef(v)) - } -} -impl<'a> TryFrom> for U32AsRef<'a> { - type Error = Error; - - fn try_from(value: EncodableField<'a>) -> Result { - match value { - EncodableField::Primitive(EncodablePrimitive::U32AsRef(v)) => Ok(v), - _ => Err(Error::NonPrimitiveTypeCannotBeEncoded), - } - } -} - -// IMPL INTO FIELD MARKER FOR PRIMITIVES -impl From for FieldMarker { - fn from(_: bool) -> Self { - FieldMarker::Primitive(PrimitiveMarker::Bool) - } -} -impl From for FieldMarker { - fn from(_: u8) -> Self { - FieldMarker::Primitive(PrimitiveMarker::U8) - } -} - -impl From for FieldMarker { - fn from(_: u16) -> Self { - FieldMarker::Primitive(PrimitiveMarker::U16) - } -} - -impl From for FieldMarker { - fn from(_: u32) -> Self { - FieldMarker::Primitive(PrimitiveMarker::U32) - } -} - -impl From for FieldMarker { - fn from(_: f32) -> Self { - FieldMarker::Primitive(PrimitiveMarker::F32) - } -} - -impl From for FieldMarker { - fn from(_: u64) -> Self { - FieldMarker::Primitive(PrimitiveMarker::U64) - } -} - -impl From for FieldMarker { - fn from(_: U24) -> Self { - FieldMarker::Primitive(PrimitiveMarker::U24) - } -} - -impl<'a> From> for FieldMarker { - fn from(_: Inner<'a, true, 32, 0, 0>) -> Self { - FieldMarker::Primitive(PrimitiveMarker::U256) - } -} - -impl<'a> From> for FieldMarker { - fn from(_: Inner<'a, true, 64, 0, 0>) -> Self { - FieldMarker::Primitive(PrimitiveMarker::Signature) - } -} - -impl<'a> From> for FieldMarker { - fn from(_: B032<'a>) -> Self { - FieldMarker::Primitive(PrimitiveMarker::B032) - } -} - -impl<'a> From> for FieldMarker { - fn from(_: Inner<'a, false, 1, 1, 255>) -> Self { - FieldMarker::Primitive(PrimitiveMarker::B0255) - } -} - -impl<'a> From> for FieldMarker { - fn from(_: Inner<'a, false, 1, 2, { 2_usize.pow(16) - 1 }>) -> Self { - FieldMarker::Primitive(PrimitiveMarker::B064K) - } -} - -impl<'a> From> for FieldMarker { - fn from(_: Inner<'a, false, 1, 3, { 2_usize.pow(24) - 1 }>) -> Self { - FieldMarker::Primitive(PrimitiveMarker::B016M) - } -} -impl<'a> From> for FieldMarker { - fn from(_: U32AsRef<'a>) -> Self { - FieldMarker::Primitive(PrimitiveMarker::U32AsRef) - } -} +use core::convert::TryInto; + +macro_rules! impl_get_marker { + ($(($ty:ty, $marker:ident)),+ $(,)?) => { + $( + impl GetMarker for $ty { + fn get_marker() -> FieldMarker { + FieldMarker::Primitive(PrimitiveMarker::$marker) + } + } + )+ + }; +} + +macro_rules! impl_decodable { + ($(($ty:ty, $marker:ident)),+ $(,)?) => { + $( + impl<'a> Decodable<'a> for $ty { + fn get_structure(_: &[u8]) -> Result, Error> { + Ok(vec![PrimitiveMarker::$marker.into()]) + } + + fn from_decoded_fields( + mut data: Vec>, + ) -> Result { + data.pop().ok_or(Error::NoDecodableFieldPassed)?.try_into() + } + } + )+ + }; +} + +macro_rules! impl_try_from_decodable_primitive { + ($(($ty:ty, $variant:ident)),+ $(,)?) => { + $( + impl<'a> TryFrom> for $ty { + type Error = Error; + + fn try_from(value: DecodablePrimitive<'a>) -> Result { + match value { + DecodablePrimitive::$variant(val) => Ok(val), + _ => Err(Error::PrimitiveConversionError), + } + } + } + )+ + }; +} + +macro_rules! impl_try_from_decodable_field { + ($($ty:ty),+ $(,)?) => { + $( + impl<'a> TryFrom> for $ty { + type Error = Error; + + fn try_from(value: DecodableField<'a>) -> Result { + match value { + DecodableField::Primitive(p) => p.try_into(), + _ => Err(Error::DecodableConversionError), + } + } + } + )+ + }; +} + +macro_rules! impl_encodable_field_conversion { + ($(($ty:ty, $variant:ident)),+ $(,)?) => { + $( + impl<'a> From<$ty> for EncodableField<'a> { + fn from(v: $ty) -> Self { + EncodableField::Primitive(EncodablePrimitive::$variant(v)) + } + } + )+ + }; +} + +macro_rules! impl_field_marker_from_owned { + ($(($ty:ty, $marker:ident)),+ $(,)?) => { + $( + impl From<$ty> for FieldMarker { + fn from(_: $ty) -> Self { + FieldMarker::Primitive(PrimitiveMarker::$marker) + } + } + )+ + }; +} + +macro_rules! impl_field_marker_from_borrowed { + ($(($ty:ty, $marker:ident)),+ $(,)?) => { + $( + impl<'a> From<$ty> for FieldMarker { + fn from(_: $ty) -> Self { + FieldMarker::Primitive(PrimitiveMarker::$marker) + } + } + )+ + }; +} + +impl_get_marker!( + (bool, Bool), + (u8, U8), + (u16, U16), + (U24, U24), + (u32, U32), + (f32, F32), + (u64, U64), + (U256<'_>, U256), + (Signature<'_>, Signature), + (B032<'_>, B032), + (B0255<'_>, B0255), + (B064K<'_>, B064K), + (B016M<'_>, B016M), + (U32AsRef<'_>, U32AsRef), +); + +impl_decodable!( + (u8, U8), + (u16, U16), + (u32, U32), + (f32, F32), + (u64, U64), + (bool, Bool), + (U24, U24), + (U256<'a>, U256), + (Signature<'a>, Signature), + (B032<'a>, B032), + (B0255<'a>, B0255), + (B064K<'a>, B064K), + (B016M<'a>, B016M), + (U32AsRef<'a>, U32AsRef), +); + +impl_try_from_decodable_primitive!( + (u8, U8), + (u16, U16), + (u32, U32), + (f32, F32), + (u64, U64), + (bool, Bool), + (U24, U24), + (U256<'a>, U256), + (Signature<'a>, Signature), + (B032<'a>, B032), + (B0255<'a>, B0255), + (B064K<'a>, B064K), + (B016M<'a>, B016M), + (U32AsRef<'a>, U32AsRef), +); + +impl_try_from_decodable_field!( + u8, + u16, + u32, + f32, + u64, + bool, + U24, + U256<'a>, + Signature<'a>, + B032<'a>, + B0255<'a>, + B064K<'a>, + B016M<'a>, + U32AsRef<'a>, +); + +impl_encodable_field_conversion!( + (bool, Bool), + (u8, U8), + (u16, U16), + (U24, U24), + (u32, U32), + (f32, F32), + (u64, U64), + (U256<'a>, U256), + (Signature<'a>, Signature), + (B032<'a>, B032), + (B0255<'a>, B0255), + (B064K<'a>, B064K), + (B016M<'a>, B016M), + (U32AsRef<'a>, U32AsRef), +); + +impl_field_marker_from_owned!( + (bool, Bool), + (u8, U8), + (u16, U16), + (u32, U32), + (f32, F32), + (u64, U64), + (U24, U24), +); + +impl_field_marker_from_borrowed!( + (Inner<'a, true, 32, 0, 0>, U256), + (Inner<'a, true, 64, 0, 0>, Signature), + (B032<'a>, B032), + (Inner<'a, false, 1, 1, 255>, B0255), + (Inner<'a, false, 1, 2, { 2_usize.pow(16) - 1 }>, B064K), + (Inner<'a, false, 1, 3, { 2_usize.pow(24) - 1 }>, B016M), + (U32AsRef<'a>, U32AsRef), +); From 8e1626b58afb9e2f7db2c82e22fc4fb6396e08eb Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 19 Jun 2026 14:31:22 +0530 Subject: [PATCH 02/20] Implement conversion from buffer_pool slice to a decodable field Here we add slices as an encodable field in case buffer_sv2 is enabled, not tracked by an issue. --- sv2/binary-sv2/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sv2/binary-sv2/src/lib.rs b/sv2/binary-sv2/src/lib.rs index adac90f726..409f8fcdd0 100644 --- a/sv2/binary-sv2/src/lib.rs +++ b/sv2/binary-sv2/src/lib.rs @@ -342,9 +342,9 @@ impl From> for EncodableField<'_> { } #[cfg(feature = "with_buffer_pool")] -impl From for EncodableField<'_> { - fn from(_v: buffer_sv2::Slice) -> Self { - unreachable!() +impl From for EncodableField<'static> { + fn from(v: buffer_sv2::Slice) -> Self { + EncodableField::Struct(v.as_ref().to_vec().into_iter().map(Into::into).collect()) } } From b84f644754ad891b3757ec0bf570038022f80335 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 19 Jun 2026 14:32:14 +0530 Subject: [PATCH 03/20] Correct the semantics around size_hint for fixed type Size_hint basically provides an idea around how much is expected, initially would just provide the size without checking whether buffer would even satisfy requirements. Now, we make sure buffer len should be adequate otherwise we return read error --- sv2/binary-sv2/src/codec/mod.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/sv2/binary-sv2/src/codec/mod.rs b/sv2/binary-sv2/src/codec/mod.rs index 727d0bb6f4..7d4ec59fa5 100644 --- a/sv2/binary-sv2/src/codec/mod.rs +++ b/sv2/binary-sv2/src/codec/mod.rs @@ -104,12 +104,20 @@ pub trait Fixed { } impl SizeHint for T { - fn size_hint(_data: &[u8], _offset: usize) -> Result { - Ok(Self::SIZE) + fn size_hint(data: &[u8], offset: usize) -> Result { + let available = data + .len() + .checked_sub(offset) + .ok_or(Error::ReadError(data.len(), offset))?; + if available >= Self::SIZE { + Ok(Self::SIZE) + } else { + Err(Error::ReadError(data.len(), offset + Self::SIZE)) + } } - fn size_hint_(&self, _: &[u8], _offset: usize) -> Result { - Ok(Self::SIZE) + fn size_hint_(&self, data: &[u8], offset: usize) -> Result { + Self::size_hint(data, offset) } } From 012536aa31d907d9643b1af8f082f2fe05a4e194 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 19 Jun 2026 14:33:38 +0530 Subject: [PATCH 04/20] improve the ergonomics around sv2 list primitives and make sure it doesn't leak internal implementation This one fixes two of the loupe issues and one of the ergonomics issue: 1. https://github.com/stratum-mining/stratum/issues/2176 -> Making sure the vector conversion doesn't break length invariant. 2. https://github.com/stratum-mining/stratum/issues/2179 -> Making sure sv2 primitives doesn't leak internal representation 3. https://github.com/stratum-mining/stratum/issues/2216 -> Adding iterators to seq primitives. This commit also updates sv2_to_sv1 to not use list type internal representation --- .../stratum-translation/src/sv2_to_sv1.rs | 2 +- .../non_copy_data_types/seq_inner.rs | 82 ++++++++++++++++--- 2 files changed, 71 insertions(+), 13 deletions(-) diff --git a/stratum-core/stratum-translation/src/sv2_to_sv1.rs b/stratum-core/stratum-translation/src/sv2_to_sv1.rs index d7d8d9ed60..12e63d5635 100644 --- a/stratum-core/stratum-translation/src/sv2_to_sv1.rs +++ b/stratum-core/stratum-translation/src/sv2_to_sv1.rs @@ -68,7 +68,7 @@ pub fn build_sv1_notify_from_sv2( let prev_hash = PrevHash(new_prev_hash.prev_hash.clone()); let coin_base1 = new_job.coinbase_tx_prefix.to_vec().into(); let coin_base2 = new_job.coinbase_tx_suffix.to_vec().into(); - let merkle_path = new_job.merkle_path.clone().into_static().0; + let merkle_path = new_job.merkle_path.clone().into_static().into_inner(); let merkle_branch: Vec = merkle_path.into_iter().map(MerkleNode).collect(); let version = HexU32Be(new_job.version); let bits = HexU32Be(new_prev_hash.nbits); diff --git a/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs b/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs index 06c94c4560..4ac4c19a6e 100644 --- a/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs +++ b/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs @@ -50,7 +50,7 @@ use crate::{ datatypes::{Sv2DataType, *}, Error, }; -use core::marker::PhantomData; +use core::{marker::PhantomData, ops::Index, slice}; // TODO add test for that impl<'a, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> @@ -113,7 +113,14 @@ use std::io::Read; /// This structure uses a generic type `T` and a lifetime parameter `'a`. #[derive(Debug, Clone, Eq, PartialEq)] -pub struct Seq0255<'a, T>(pub Vec, PhantomData<&'a T>); +pub struct Seq0255<'a, T>(Vec, PhantomData<&'a T>); + +impl<'a, T> Index for Seq0255<'a, T> { + type Output = T; + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} impl<'a, T: 'a> Seq0255<'a, T> { const HEADERSIZE: usize = 1; @@ -140,6 +147,26 @@ impl<'a, T: 'a> Seq0255<'a, T> { pub fn into_inner(self) -> Vec { self.0 } + + /// Returns the sequence as a slice. + pub fn as_slice(&self) -> &[T] { + &self.0 + } + + /// Iterates over the sequence by reference. + pub fn iter(&self) -> slice::Iter<'_, T> { + self.0.iter() + } + + /// Length of Seq0255 which is a list of bytes up-to 255 len + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns true when the sequence contains no elements. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } } impl GetSize for Seq0255<'_, T> { @@ -156,7 +183,14 @@ impl GetSize for Seq0255<'_, T> { /// [`Seq064K`] represents a sequence with a maximum length of 65535 elements. /// This structure uses a generic type `T` and a lifetime parameter `'a`. #[derive(Debug, Clone, Eq, PartialEq)] -pub struct Seq064K<'a, T>(pub(crate) Vec, PhantomData<&'a T>); +pub struct Seq064K<'a, T>(Vec, PhantomData<&'a T>); + +impl<'a, T> Index for Seq064K<'a, T> { + type Output = T; + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} impl<'a, T: 'a> Seq064K<'a, T> { const HEADERSIZE: usize = 2; @@ -183,6 +217,26 @@ impl<'a, T: 'a> Seq064K<'a, T> { pub fn into_inner(self) -> Vec { self.0 } + + /// Returns the sequence as a slice. + pub fn as_slice(&self) -> &[T] { + &self.0 + } + + /// Iterates over the sequence by reference. + pub fn iter(&self) -> slice::Iter<'_, T> { + self.0.iter() + } + + /// Length of Seq0255 which is a list of bytes up-to 64k len + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns true when the sequence contains no elements. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } } impl GetSize for Seq064K<'_, T> { @@ -228,8 +282,10 @@ macro_rules! impl_codec_for_sequence { T::from_decoded_fields(vec![DecodableField::Primitive(p)]); inner.push(element?) } - // A struct always recursivly call decode until it reach a primitive - DecodableField::Struct(_) => unreachable!(), + DecodableField::Struct(fields) => { + let element = T::from_decoded_fields(fields); + inner.push(element?) + } } } i += 1; @@ -345,15 +401,17 @@ impl_into_encodable_field_for_seq!(B0255<'a>); impl_into_encodable_field_for_seq!(B064K<'a>); impl_into_encodable_field_for_seq!(B016M<'a>); -impl From> for Seq0255<'_, T> { - fn from(v: Vec) -> Self { - Seq0255(v, PhantomData) +impl TryFrom> for Seq0255<'_, T> { + type Error = Error; + fn try_from(value: Vec) -> Result { + Seq0255::new(value) } } -impl From> for Seq064K<'_, T> { - fn from(v: Vec) -> Self { - Seq064K(v, PhantomData) +impl TryFrom> for Seq064K<'_, T> { + type Error = Error; + fn try_from(value: Vec) -> Result { + Seq064K::new(value) } } @@ -423,7 +481,7 @@ impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const /// The lifetime 'a is defined. #[derive(Debug, Clone, Eq, PartialEq)] -pub struct Sv2Option<'a, T>(pub Vec, PhantomData<&'a T>); +pub struct Sv2Option<'a, T>(Vec, PhantomData<&'a T>); // TODO add test for that impl<'a, const SIZE: usize> Sv2Option<'a, super::inner::Inner<'a, true, SIZE, 0, 0>> { From d8358c301ef50bf3068a3823fe9c20257a1cd95d Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 19 Jun 2026 14:34:55 +0530 Subject: [PATCH 05/20] have a helper for binary to hex and remove duplication from display trait This is more of a clean up commit, where we remove repetitive hex conv code in primitive's display trait implementation. --- .../src/datatypes/non_copy_data_types/mod.rs | 155 +++++++----------- 1 file changed, 63 insertions(+), 92 deletions(-) diff --git a/sv2/binary-sv2/src/datatypes/non_copy_data_types/mod.rs b/sv2/binary-sv2/src/datatypes/non_copy_data_types/mod.rs index 1a77b0a08c..7d0be9e57d 100644 --- a/sv2/binary-sv2/src/datatypes/non_copy_data_types/mod.rs +++ b/sv2/binary-sv2/src/datatypes/non_copy_data_types/mod.rs @@ -23,6 +23,7 @@ // protocol data. use alloc::{borrow::ToOwned, fmt, string::String}; +use core::fmt::Write as _; mod inner; mod seq_inner; @@ -58,6 +59,14 @@ pub type B064K<'a> = Inner<'a, false, 1, 2, { u16::MAX as usize }>; /// represented using the `Inner` type with a 3-byte header. pub type B016M<'a> = Inner<'a, false, 1, 3, { 2_usize.pow(24) - 1 }>; +fn bytes_to_hex<'a>(bytes: impl IntoIterator) -> String { + let mut hex = String::new(); + for byte in bytes { + write!(&mut hex, "{byte:02x}").expect("writing to String cannot fail"); + } + hex +} + impl fmt::Display for U32AsRef<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let inner = self.inner_as_ref(); @@ -82,11 +91,7 @@ impl fmt::Display for Sv2Option<'_, u32> { impl B0255<'_> { pub fn as_hex(&self) -> String { - let inner = self - .inner_as_ref() - .iter() - .map(|byte| format!("{byte:02x}")) - .collect::(); + let inner = bytes_to_hex(self.inner_as_ref()); format!("B0255({inner})") } } @@ -96,69 +101,48 @@ impl Str0255<'_> { pub fn as_utf8_or_hex(&self) -> String { match core::str::from_utf8(self.inner_as_ref()) { Ok(s) => alloc::string::String::from(s), - Err(_) => format!( - "0x{}", - self.inner_as_ref() - .iter() - .map(|b| format!("{b:02x}")) - .collect::() - ), + Err(_) => format!("0x{}", bytes_to_hex(self.inner_as_ref())), } } } impl fmt::Display for B064K<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let inner = self - .inner_as_ref() - .iter() - .map(|byte| format!("{byte:02x}")) - .collect::(); + let inner = bytes_to_hex(self.inner_as_ref()); write!(f, "B064K({inner})") } } impl fmt::Display for U256<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let inner = self - .inner_as_ref() - .iter() - .rev() - .map(|byte| format!("{byte:02x}")) - .collect::(); + let inner = bytes_to_hex(self.inner_as_ref().iter().rev()); write!(f, "U256({inner})") } } impl fmt::Display for Seq0255<'_, U256<'_>> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let len = self.0.len(); - let as_hex = |item: &U256<'_>| { - item.inner_as_ref() - .iter() - .rev() - .map(|byte| format!("{byte:02x}")) - .collect::() - }; + let len = self.len(); + let as_hex = |item: &U256<'_>| bytes_to_hex(item.inner_as_ref().iter().rev()); write!(f, "Seq0255 write!(f, "[]"), - 1 => write!(f, "{}]", as_hex(&self.0[0])), - 2 => write!(f, "{}, {}]", as_hex(&self.0[0]), as_hex(&self.0[1])), + 1 => write!(f, "[{}]", as_hex(&self[0])), + 2 => write!(f, "[{}, {}]", as_hex(&self[0]), as_hex(&self[1])), 3 => write!( f, "[{}, {}, {}]", - as_hex(&self.0[0]), - as_hex(&self.0[1]), - as_hex(&self.0[2]) + as_hex(&self[0]), + as_hex(&self[1]), + as_hex(&self[2]) ), _ => write!( f, "[{}, {}, ... , {}, {}]", - as_hex(&self.0[0]), - as_hex(&self.0[1]), - as_hex(&self.0[len - 2]), - as_hex(&self.0[len - 1]) + as_hex(&self[0]), + as_hex(&self[1]), + as_hex(&self[len - 2]), + as_hex(&self[len - 1]) ), } } @@ -166,14 +150,10 @@ impl fmt::Display for Seq0255<'_, U256<'_>> { impl fmt::Display for Seq064K<'_, B016M<'_>> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let len = self.0.len(); + let len = self.len(); let as_hex = |item: &B016M<'_>| { - let hex: String = item - .inner_as_ref() - .iter() - .map(|byte| format!("{byte:02x}")) - .collect(); + let hex = bytes_to_hex(item.inner_as_ref()); if hex.len() > 500 { format!("{}…", &hex[..500], hex.len() - 500) @@ -185,22 +165,22 @@ impl fmt::Display for Seq064K<'_, B016M<'_>> { write!(f, "Seq064K write!(f, "[]"), - 1 => write!(f, "[{}]", as_hex(&self.0[0])), - 2 => write!(f, "[{}, {}]", as_hex(&self.0[0]), as_hex(&self.0[1])), + 1 => write!(f, "[{}]", as_hex(&self[0])), + 2 => write!(f, "[{}, {}]", as_hex(&self[0]), as_hex(&self[1])), 3 => write!( f, "[{}, {}, {}]", - as_hex(&self.0[0]), - as_hex(&self.0[1]), - as_hex(&self.0[2]) + as_hex(&self[0]), + as_hex(&self[1]), + as_hex(&self[2]) ), _ => write!( f, "[{}, {}, … , {}, {}]", - as_hex(&self.0[0]), - as_hex(&self.0[1]), - as_hex(&self.0[len - 2]), - as_hex(&self.0[len - 1]) + as_hex(&self[0]), + as_hex(&self[1]), + as_hex(&self[len - 2]), + as_hex(&self[len - 1]) ), } } @@ -208,32 +188,27 @@ impl fmt::Display for Seq064K<'_, B016M<'_>> { impl fmt::Display for Seq064K<'_, U256<'_>> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let len = self.0.len(); - let as_hex = |item: &U256<'_>| { - item.inner_as_ref() - .iter() - .map(|byte| format!("{byte:02x}")) - .collect::() - }; + let len = self.len(); + let as_hex = |item: &U256<'_>| bytes_to_hex(item.inner_as_ref().iter().rev()); write!(f, "Seq064K write!(f, "[]"), - 1 => write!(f, "[{}]", as_hex(&self.0[0])), - 2 => write!(f, "[{}, {}]", as_hex(&self.0[0]), as_hex(&self.0[1])), + 1 => write!(f, "[{}]", as_hex(&self[0])), + 2 => write!(f, "[{}, {}]", as_hex(&self[0]), as_hex(&self[1])), 3 => write!( f, "[{}, {}, {}]", - as_hex(&self.0[0]), - as_hex(&self.0[1]), - as_hex(&self.0[2]) + as_hex(&self[0]), + as_hex(&self[1]), + as_hex(&self[2]) ), _ => write!( f, "[{}, {}, ... , {}, {}]", - as_hex(&self.0[0]), - as_hex(&self.0[1]), - as_hex(&self.0[len - 2]), - as_hex(&self.0[len - 1]) + as_hex(&self[0]), + as_hex(&self[1]), + as_hex(&self[len - 2]), + as_hex(&self[len - 1]) ), } } @@ -241,40 +216,40 @@ impl fmt::Display for Seq064K<'_, U256<'_>> { impl fmt::Display for Seq064K<'_, u16> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let len = self.0.len(); + let len = self.len(); write!(f, "Seq064K write!(f, "[]"), - 1 => write!(f, "[{}]", self.0[0]), - 2 => write!(f, "[{}, {}]", self.0[0], self.0[1]), - 3 => write!(f, "[{}, {}, {}]", self.0[0], self.0[1], self.0[2]), + 1 => write!(f, "[{}]", self[0]), + 2 => write!(f, "[{}, {}]", self[0], self[1]), + 3 => write!(f, "[{}, {}, {}]", self[0], self[1], self[2]), _ => write!( f, "[{}, {}, ... , {}, {}]", - self.0[0], - self.0[1], - self.0[len - 2], - self.0[len - 1] + self[0], + self[1], + self[len - 2], + self[len - 1] ), } } } impl fmt::Display for Seq064K<'_, u32> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let len = self.0.len(); + let len = self.len(); write!(f, "Seq064K write!(f, "[]"), - 1 => write!(f, "[{}]", self.0[0]), - 2 => write!(f, "[{}, {}]", self.0[0], self.0[1]), - 3 => write!(f, "[{}, {}, {}]", self.0[0], self.0[1], self.0[2]), + 1 => write!(f, "[{}]", self[0]), + 2 => write!(f, "[{}, {}]", self[0], self[1]), + 3 => write!(f, "[{}, {}, {}]", self[0], self[1], self[2]), _ => write!( f, "[{}, {}, ... , {}, {}]", - self.0[0], - self.0[1], - self.0[len - 2], - self.0[len - 1] + self[0], + self[1], + self[len - 2], + self[len - 1] ), } } @@ -282,11 +257,7 @@ impl fmt::Display for Seq064K<'_, u32> { impl fmt::Display for B032<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let item = self - .inner_as_ref() - .iter() - .map(|byte| format!("{byte:02x}")) - .collect::(); + let item = bytes_to_hex(self.inner_as_ref()); write!(f, "B032({item})") } } From 0b6c2065bc40b272f249ea1594a33ff382922176 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 19 Jun 2026 14:38:18 +0530 Subject: [PATCH 06/20] remove from_vec and from_vec_unchecked This is also a loupe issue: https://github.com/stratum-mining/stratum/issues/2171 The loupe variant 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 --- .../src/datatypes/copy_data_types.rs | 20 ------------------- sv2/binary-sv2/src/datatypes/mod.rs | 6 ------ .../datatypes/non_copy_data_types/inner.rs | 11 +--------- 3 files changed, 1 insertion(+), 36 deletions(-) diff --git a/sv2/binary-sv2/src/datatypes/copy_data_types.rs b/sv2/binary-sv2/src/datatypes/copy_data_types.rs index 56db4a79f5..5f03a6beeb 100644 --- a/sv2/binary-sv2/src/datatypes/copy_data_types.rs +++ b/sv2/binary-sv2/src/datatypes/copy_data_types.rs @@ -28,8 +28,6 @@ // unsigned integer types, ensuring little-endian byte ordering for serialization and handling both // in-memory buffers and `std::io::Read`/`Write` interfaces when `std` is available. use crate::{codec::Fixed, datatypes::Sv2DataType, Error}; - -use alloc::vec::Vec; use core::convert::{TryFrom, TryInto}; #[cfg(not(feature = "no_std"))] @@ -47,24 +45,14 @@ impl<'a> Sv2DataType<'a> for bool { .first() .map(|x: &u8| x << 7) .map(|x: u8| x >> 7) - // This is an unchecked function is fine to panic .expect("Try to decode a bool from a buffer of len 0") { 0 => false, 1 => true, - // Below panic is impossible value is either 0 or 1 _ => panic!(), } } - fn from_vec_(mut data: Vec) -> Result { - Self::from_bytes_(&mut data) - } - - fn from_vec_unchecked(mut data: Vec) -> Self { - Self::from_bytes_unchecked(&mut data) - } - #[cfg(not(feature = "no_std"))] fn from_reader_(reader: &mut impl Read) -> Result { let mut dst = [0_u8; Self::SIZE]; @@ -122,14 +110,6 @@ macro_rules! impl_sv2_for_unsigned { Self::from_le_bytes(*a) } - fn from_vec_(mut data: Vec) -> Result { - Self::from_bytes_(&mut data) - } - - fn from_vec_unchecked(mut data: Vec) -> Self { - Self::from_bytes_unchecked(&mut data) - } - #[cfg(not(feature = "no_std"))] fn from_reader_(reader: &mut impl Read) -> Result { let mut dst = [0_u8; Self::SIZE]; diff --git a/sv2/binary-sv2/src/datatypes/mod.rs b/sv2/binary-sv2/src/datatypes/mod.rs index 86102ba67f..8c6a94e887 100644 --- a/sv2/binary-sv2/src/datatypes/mod.rs +++ b/sv2/binary-sv2/src/datatypes/mod.rs @@ -70,12 +70,6 @@ pub trait Sv2DataType<'a>: Sized + SizeHint + GetSize + TryInto { /// Constructs an instance from a mutable byte slice without verifying size constraints. fn from_bytes_unchecked(data: &'a mut [u8]) -> Self; - /// Constructs an instance from a vector, checking for the correct size. - fn from_vec_(data: Vec) -> Result; - - /// Constructs an instance from a vector without validating its size. - fn from_vec_unchecked(data: Vec) -> Self; - // Constructs an instance from a reader source, checking for size constraints. #[cfg(not(feature = "no_std"))] fn from_reader_(reader: &mut impl Read) -> Result; diff --git a/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs b/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs index a492b52391..1d91d6180e 100644 --- a/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs +++ b/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs @@ -341,15 +341,6 @@ where } } - fn from_vec_(data: Vec) -> Result { - Self::size_hint(&data, 0)?; - Ok(Self::Owned(data)) - } - - fn from_vec_unchecked(data: Vec) -> Self { - Self::Owned(data) - } - #[cfg(not(feature = "no_std"))] fn from_reader_(mut reader: &mut impl Read) -> Result { let size = Self::expected_length_for_reader(&mut reader)?; @@ -357,7 +348,7 @@ where let mut dst = vec![0; size]; reader.read_exact(&mut dst)?; - Ok(Self::from_vec_unchecked(dst)) + Ok(Self::Owned(dst)) } fn to_slice_unchecked(&'a self, dst: &mut [u8]) { From 173e162e919b73cbc059c3737faa7800d98b3613 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 19 Jun 2026 14:38:55 +0530 Subject: [PATCH 07/20] Correct payload size calculation for fixed types This solves loupe issue: 1. https://github.com/stratum-mining/stratum/issues/2173 -> Here we fix the size of FIXED primitive to be equal to SIZE and not one. 2. https://github.com/stratum-mining/stratum/issues/2177 -> make sure header is appended to the writer. --- sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs b/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs index 1d91d6180e..ddddc0c9fe 100644 --- a/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs +++ b/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs @@ -204,7 +204,7 @@ impl unreachable!(), }; - if expected_length <= (MAXSIZE + HEADERSIZE) { + if expected_length <= MAXSIZE { Ok(expected_length) } else { Err(Error::ReadError(expected_length, MAXSIZE)) @@ -218,7 +218,7 @@ impl data.len(), (Inner::Owned(data), false) => data.len(), - (_, true) => 1, + (_, true) => SIZE, } } @@ -369,6 +369,8 @@ where #[cfg(not(feature = "no_std"))] fn to_writer_(&self, writer: &mut impl Write) -> Result<(), E> { + let header = self.get_header(); + writer.write_all(&header[..HEADERSIZE])?; match self { Inner::Ref(data) => { writer.write_all(data)?; From 5a0d4bb27780e98413d4c2ef8d630f6d68539777 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 19 Jun 2026 14:46:49 +0530 Subject: [PATCH 08/20] Make sure from_reader doesn't read_to_end and follow a conservative policy This solves a loupe issue: https://github.com/stratum-mining/stratum/issues/2178, We are not doing unbounded reads with with_reader --- sv2/binary-sv2/src/codec/decodable.rs | 29 +++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/sv2/binary-sv2/src/codec/decodable.rs b/sv2/binary-sv2/src/codec/decodable.rs index 81d928cf2a..8f6c2ca35b 100644 --- a/sv2/binary-sv2/src/codec/decodable.rs +++ b/sv2/binary-sv2/src/codec/decodable.rs @@ -54,9 +54,34 @@ pub trait Decodable<'a>: Sized { #[cfg(not(feature = "no_std"))] fn from_reader(reader: &mut impl Read) -> Result { let mut data = Vec::new(); - reader.read_to_end(&mut data)?; - let structure = Self::get_structure(&data[..])?; + let structure = loop { + match Self::get_structure(&data[..]) { + Ok(structure) => match structure.size_hint_(&data[..], 0) { + Ok(expected_len) => { + if data.len() < expected_len { + let missing = expected_len - data.len(); + let original_len = data.len(); + data.resize(expected_len, 0); + reader.read_exact(&mut data[original_len..original_len + missing])?; + } + break structure; + } + Err(Error::OutOfBound | Error::ReadError(_, _)) => { + let mut next = [0_u8; 1]; + reader.read_exact(&mut next)?; + data.push(next[0]); + } + Err(error) => return Err(error), + }, + Err(Error::OutOfBound | Error::ReadError(_, _)) => { + let mut next = [0_u8; 1]; + reader.read_exact(&mut next)?; + data.push(next[0]); + } + Err(error) => return Err(error), + } + }; let mut fields = Vec::new(); let mut reader = Cursor::new(data); From 7fc72cbb69df25b049e1776f89d36e9cc4d43dc2 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 19 Jun 2026 14:48:08 +0530 Subject: [PATCH 09/20] decode now returns an error instead of using unchecked conversions 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. This also adds owned variant decode where the decode consume the buffer, instead of just referencing the buffer. --- sv2/binary-sv2/src/codec/decodable.rs | 140 ++++++++++++++---- sv2/binary-sv2/src/datatypes/mod.rs | 8 +- .../common-messages/src/setup_connection.rs | 4 +- 3 files changed, 116 insertions(+), 36 deletions(-) diff --git a/sv2/binary-sv2/src/codec/decodable.rs b/sv2/binary-sv2/src/codec/decodable.rs index 8f6c2ca35b..c2455fedfd 100644 --- a/sv2/binary-sv2/src/codec/decodable.rs +++ b/sv2/binary-sv2/src/codec/decodable.rs @@ -175,9 +175,9 @@ pub enum DecodableField<'a> { } impl SizeHint for PrimitiveMarker { - // PrimitiveMarker needs introspection to return a size hint. This method is not implementable. + // PrimitiveMarker requires a concrete marker instance to determine the size. fn size_hint(_data: &[u8], _offset: usize) -> Result { - unimplemented!() + Err(Error::UnInitializedDecoder) } fn size_hint_(&self, data: &[u8], offset: usize) -> Result { @@ -201,9 +201,9 @@ impl SizeHint for PrimitiveMarker { } impl SizeHint for FieldMarker { - // FieldMarker need introspection to return a size hint. This method is not implementeable + // FieldMarker requires a concrete marker instance to determine the size. fn size_hint(_data: &[u8], _offset: usize) -> Result { - unimplemented!() + Err(Error::UnInitializedDecoder) } fn size_hint_(&self, data: &[u8], offset: usize) -> Result { @@ -221,9 +221,9 @@ impl SizeHint for FieldMarker { } impl SizeHint for Vec { - // FieldMarker need introspection to return a size hint. This method is not implementeable + // The structure must be initialized before its aggregate size can be calculated. fn size_hint(_data: &[u8], _offset: usize) -> Result { - unimplemented!() + Err(Error::UnInitializedDecoder) } fn size_hint_(&self, data: &[u8], offset: usize) -> Result { @@ -271,32 +271,107 @@ impl PrimitiveMarker { // Decodes a primitive value from a byte slice at the given offset, returning the corresponding // `DecodablePrimitive`. The specific decoding logic depends on the type of the primitive (e.g., // `u8`, `u16`, etc.). - fn decode<'a>(&self, data: &'a mut [u8], offset: usize) -> DecodablePrimitive<'a> { + fn decode<'a>( + &self, + data: &'a mut [u8], + offset: usize, + ) -> Result, Error> { match self { - Self::U8 => DecodablePrimitive::U8(u8::from_bytes_unchecked(&mut data[offset..])), - Self::U16 => DecodablePrimitive::U16(u16::from_bytes_unchecked(&mut data[offset..])), - Self::Bool => DecodablePrimitive::Bool(bool::from_bytes_unchecked(&mut data[offset..])), - Self::U24 => DecodablePrimitive::U24(U24::from_bytes_unchecked(&mut data[offset..])), - Self::U256 => DecodablePrimitive::U256(U256::from_bytes_unchecked(&mut data[offset..])), - Self::Signature => { - DecodablePrimitive::Signature(Signature::from_bytes_unchecked(&mut data[offset..])) - } - Self::U32 => DecodablePrimitive::U32(u32::from_bytes_unchecked(&mut data[offset..])), - Self::U32AsRef => { - DecodablePrimitive::U32AsRef(U32AsRef::from_bytes_unchecked(&mut data[offset..])) - } - Self::F32 => DecodablePrimitive::F32(f32::from_bytes_unchecked(&mut data[offset..])), - Self::U64 => DecodablePrimitive::U64(u64::from_bytes_unchecked(&mut data[offset..])), - Self::B032 => DecodablePrimitive::B032(B032::from_bytes_unchecked(&mut data[offset..])), - Self::B0255 => { - DecodablePrimitive::B0255(B0255::from_bytes_unchecked(&mut data[offset..])) - } - Self::B064K => { - DecodablePrimitive::B064K(B064K::from_bytes_unchecked(&mut data[offset..])) - } - Self::B016M => { - DecodablePrimitive::B016M(B016M::from_bytes_unchecked(&mut data[offset..])) - } + Self::U8 => Ok(DecodablePrimitive::U8(u8::from_bytes_( + &mut data[offset..], + )?)), + Self::U16 => Ok(DecodablePrimitive::U16(u16::from_bytes_( + &mut data[offset..], + )?)), + Self::Bool => Ok(DecodablePrimitive::Bool(bool::from_bytes_( + &mut data[offset..], + )?)), + Self::U24 => Ok(DecodablePrimitive::U24(U24::from_bytes_( + &mut data[offset..], + )?)), + Self::U256 => Ok(DecodablePrimitive::U256(U256::from_bytes_( + &mut data[offset..], + )?)), + Self::Signature => Ok(DecodablePrimitive::Signature(Signature::from_bytes_( + &mut data[offset..], + )?)), + Self::U32 => Ok(DecodablePrimitive::U32(u32::from_bytes_( + &mut data[offset..], + )?)), + Self::U32AsRef => Ok(DecodablePrimitive::U32AsRef(U32AsRef::from_bytes_( + &mut data[offset..], + )?)), + Self::F32 => Ok(DecodablePrimitive::F32(f32::from_bytes_( + &mut data[offset..], + )?)), + Self::U64 => Ok(DecodablePrimitive::U64(u64::from_bytes_( + &mut data[offset..], + )?)), + Self::B032 => Ok(DecodablePrimitive::B032(B032::from_bytes_( + &mut data[offset..], + )?)), + Self::B0255 => Ok(DecodablePrimitive::B0255(B0255::from_bytes_( + &mut data[offset..], + )?)), + Self::B064K => Ok(DecodablePrimitive::B064K(B064K::from_bytes_( + &mut data[offset..], + )?)), + Self::B016M => Ok(DecodablePrimitive::B016M(B016M::from_bytes_( + &mut data[offset..], + )?)), + } + } + + fn decode_owned( + &self, + data: &[u8], + offset: usize, + ) -> Result, Error> { + macro_rules! decode_owned_copy { + ($ty:ty, $variant:ident) => {{ + let mut owned = data[offset..].to_vec(); + Ok(DecodablePrimitive::$variant(<$ty>::from_bytes_( + &mut owned, + )?)) + }}; + } + + macro_rules! decode_owned_fixed_inner { + ($ty:ty, $variant:ident) => {{ + let data = &data[offset..]; + let size = <$ty>::size_hint(data, 0)?; + Ok(DecodablePrimitive::$variant(<$ty>::try_from( + data[..size].to_vec(), + )?)) + }}; + } + + macro_rules! decode_owned_variable_inner { + ($ty:ty, $variant:ident, $header_size:expr) => {{ + let data = &data[offset..]; + let size = <$ty>::size_hint(data, 0)?; + let payload = &data[$header_size..size]; + Ok(DecodablePrimitive::$variant(<$ty>::try_from( + payload.to_vec(), + )?)) + }}; + } + + match self { + Self::U8 => decode_owned_copy!(u8, U8), + Self::U16 => decode_owned_copy!(u16, U16), + Self::Bool => decode_owned_copy!(bool, Bool), + Self::U24 => decode_owned_copy!(U24, U24), + Self::U256 => decode_owned_fixed_inner!(U256, U256), + Self::Mac => decode_owned_fixed_inner!(Mac, Mac), + Self::Signature => decode_owned_fixed_inner!(Signature, Signature), + Self::U32 => decode_owned_copy!(u32, U32), + Self::F32 => decode_owned_copy!(f32, F32), + Self::U64 => decode_owned_copy!(u64, U64), + Self::B032 => decode_owned_variable_inner!(B032, B032, 1), + Self::B0255 => decode_owned_variable_inner!(B0255, B0255, 1), + Self::B064K => decode_owned_variable_inner!(B064K, B064K, 2), + Self::B016M => decode_owned_variable_inner!(B016M, B016M, 3), } } @@ -312,6 +387,7 @@ impl PrimitiveMarker { Self::Bool => Ok(DecodablePrimitive::Bool(bool::from_reader_(reader)?)), Self::U24 => Ok(DecodablePrimitive::U24(U24::from_reader_(reader)?)), Self::U256 => Ok(DecodablePrimitive::U256(U256::from_reader_(reader)?)), + Self::Mac => Ok(DecodablePrimitive::Mac(Mac::from_reader_(reader)?)), Self::Signature => Ok(DecodablePrimitive::Signature(Signature::from_reader_( reader, )?)), @@ -357,7 +433,7 @@ impl FieldMarker { // and returns the resulting `DecodableField`. pub(crate) fn decode<'a>(&self, data: &'a mut [u8]) -> Result, Error> { match self { - Self::Primitive(p) => Ok(DecodableField::Primitive(p.decode(data, 0))), + Self::Primitive(p) => Ok(DecodableField::Primitive(p.decode(data, 0)?)), Self::Struct(ps) => { let mut decodeds = Vec::new(); let mut tail = data; diff --git a/sv2/binary-sv2/src/datatypes/mod.rs b/sv2/binary-sv2/src/datatypes/mod.rs index 8c6a94e887..8153063d6b 100644 --- a/sv2/binary-sv2/src/datatypes/mod.rs +++ b/sv2/binary-sv2/src/datatypes/mod.rs @@ -63,8 +63,12 @@ pub trait Sv2DataType<'a>: Sized + SizeHint + GetSize + TryInto { /// This function verifies that the provided byte slice has the correct size according to the /// type's size hint. fn from_bytes_(data: &'a mut [u8]) -> Result { - Self::size_hint(data, 0)?; - Ok(Self::from_bytes_unchecked(data)) + let size = Self::size_hint(data, 0)?; + if size > data.len() { + return Err(Error::ReadError(data.len(), size)); + } + let (head, _) = data.split_at_mut(size); + Ok(Self::from_bytes_unchecked(head)) } /// Constructs an instance from a mutable byte slice without verifying size constraints. diff --git a/sv2/subprotocols/common-messages/src/setup_connection.rs b/sv2/subprotocols/common-messages/src/setup_connection.rs index 463b262e4b..8c052d7d2a 100644 --- a/sv2/subprotocols/common-messages/src/setup_connection.rs +++ b/sv2/subprotocols/common-messages/src/setup_connection.rs @@ -344,8 +344,8 @@ mod test { assert!(result.is_err()); match result { - Err(binary_sv2::Error::OutOfBound) => (), - Err(e) => panic!("Expected OutOfBounds error, got {:?}", e), + Err(binary_sv2::Error::ReadError(0, 1)) => (), + Err(e) => panic!("Expected ReadError(0, 1), got {:?}", e), Ok(_) => panic!("Expected error, got Ok"), } } From 1c370bf217aa2af9d0cb8beca60d5996aa708cf5 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 19 Jun 2026 14:52:58 +0530 Subject: [PATCH 10/20] remove unchecked implementation 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. --- .../src/datatypes/copy_data_types.rs | 38 +++++++++++++------ sv2/binary-sv2/src/datatypes/mod.rs | 30 ++------------- .../datatypes/non_copy_data_types/inner.rs | 17 +++++++-- .../non_copy_data_types/seq_inner.rs | 2 +- 4 files changed, 43 insertions(+), 44 deletions(-) diff --git a/sv2/binary-sv2/src/datatypes/copy_data_types.rs b/sv2/binary-sv2/src/datatypes/copy_data_types.rs index 5f03a6beeb..44d7a995e6 100644 --- a/sv2/binary-sv2/src/datatypes/copy_data_types.rs +++ b/sv2/binary-sv2/src/datatypes/copy_data_types.rs @@ -12,8 +12,8 @@ // // ### `Sv2DataType` // The `Sv2DataType` trait is implemented for these data types, providing methods for encoding and -// decoding operations such as `from_bytes_unchecked`, `from_vec_`, `from_reader_` (if `std` is -// available), and `to_slice_unchecked`. The methods use little-endian byte order for consistency +// decoding operations such as `from_bytes_`, `from_reader_` (if `std` is available), and +// `to_slice`. The methods use little-endian byte order for consistency // across platforms. // // ## Special Types @@ -27,7 +27,11 @@ // The `impl_sv2_for_unsigned` macro streamlines the implementation of the `Sv2DataType` trait for // unsigned integer types, ensuring little-endian byte ordering for serialization and handling both // in-memory buffers and `std::io::Read`/`Write` interfaces when `std` is available. -use crate::{codec::Fixed, datatypes::Sv2DataType, Error}; +use crate::{ + codec::{Fixed, SizeHint}, + datatypes::Sv2DataType, + Error, +}; use core::convert::{TryFrom, TryInto}; #[cfg(not(feature = "no_std"))] @@ -40,8 +44,9 @@ impl Fixed for bool { } impl<'a> Sv2DataType<'a> for bool { - fn from_bytes_unchecked(data: &'a mut [u8]) -> Self { - match data + fn from_bytes_(data: &'a mut [u8]) -> Result { + bool::size_hint(data, 0)?; + let value = match data .first() .map(|x: &u8| x << 7) .map(|x: u8| x >> 7) @@ -50,7 +55,8 @@ impl<'a> Sv2DataType<'a> for bool { 0 => false, 1 => true, _ => panic!(), - } + }; + Ok(value) } #[cfg(not(feature = "no_std"))] @@ -60,11 +66,15 @@ impl<'a> Sv2DataType<'a> for bool { Self::from_bytes_(&mut dst) } - fn to_slice_unchecked(&'a self, dst: &mut [u8]) { + fn to_slice(&'a self, dst: &mut [u8]) -> Result { + if dst.len() < Self::SIZE { + return Err(Error::WriteError(Self::SIZE, dst.len())); + } match self { true => dst[0] = 1, false => dst[0] = 0, }; + Ok(Self::SIZE) } #[cfg(not(feature = "no_std"))] @@ -102,25 +112,29 @@ impl Fixed for u64 { macro_rules! impl_sv2_for_unsigned { ($a:ty) => { impl<'a> Sv2DataType<'a> for $a { - fn from_bytes_unchecked(data: &'a mut [u8]) -> Self { - // unchecked function is fine to panic + fn from_bytes_(data: &'a mut [u8]) -> Result { + Self::size_hint(data, 0)?; let a: &[u8; Self::SIZE] = data[0..Self::SIZE].try_into().expect( "Try to decode a copy data type from a buffer that do not have enough bytes", ); - Self::from_le_bytes(*a) + Ok(Self::from_le_bytes(*a)) } #[cfg(not(feature = "no_std"))] fn from_reader_(reader: &mut impl Read) -> Result { let mut dst = [0_u8; Self::SIZE]; reader.read_exact(&mut dst)?; - Ok(Self::from_bytes_unchecked(&mut dst)) + Ok(Self::from_le_bytes(dst)) } - fn to_slice_unchecked(&'a self, dst: &mut [u8]) { + fn to_slice(&'a self, dst: &mut [u8]) -> Result { + if dst.len() < Self::SIZE { + return Err(Error::WriteError(Self::SIZE, dst.len())); + } let dst = &mut dst[0..Self::SIZE]; let src = self.to_le_bytes(); dst.copy_from_slice(&src); + Ok(Self::SIZE) } #[cfg(not(feature = "no_std"))] diff --git a/sv2/binary-sv2/src/datatypes/mod.rs b/sv2/binary-sv2/src/datatypes/mod.rs index 8153063d6b..200ab20461 100644 --- a/sv2/binary-sv2/src/datatypes/mod.rs +++ b/sv2/binary-sv2/src/datatypes/mod.rs @@ -10,9 +10,7 @@ // - **Deserialize**: Convert byte slices or reader sources into Rust types. // - **Serialize**: Encode Rust types into byte slices or write them to I/O streams. // -// Supports both **checked** and **unchecked** variants for serialization and deserialization. -// Checked functions validate data lengths, while unchecked versions assume size correctness for -// optimized performance. +// Checked functions validate data lengths before decoding or encoding. // // ### Modules // - **`copy_data_types`**: Defines fixed-size types directly copied into or from byte slices, such @@ -55,41 +53,19 @@ use std::io::{Error as E, Read, Write}; /// - Deserialization: Converting byte slices or streams back into the in-memory representation of /// the data. /// -/// This trait includes functions for both checked and unchecked conversions, providing flexibility -/// in situations where error handling can be safely ignored. pub trait Sv2DataType<'a>: Sized + SizeHint + GetSize + TryInto { /// Creates an instance of the type from a mutable byte slice, checking for size constraints. /// /// This function verifies that the provided byte slice has the correct size according to the /// type's size hint. - fn from_bytes_(data: &'a mut [u8]) -> Result { - let size = Self::size_hint(data, 0)?; - if size > data.len() { - return Err(Error::ReadError(data.len(), size)); - } - let (head, _) = data.split_at_mut(size); - Ok(Self::from_bytes_unchecked(head)) - } - - /// Constructs an instance from a mutable byte slice without verifying size constraints. - fn from_bytes_unchecked(data: &'a mut [u8]) -> Self; + fn from_bytes_(data: &'a mut [u8]) -> Result; // Constructs an instance from a reader source, checking for size constraints. #[cfg(not(feature = "no_std"))] fn from_reader_(reader: &mut impl Read) -> Result; /// Serializes the instance to a mutable slice, checking the destination size. - fn to_slice(&'a self, dst: &mut [u8]) -> Result { - if dst.len() >= self.get_size() { - self.to_slice_unchecked(dst); - Ok(self.get_size()) - } else { - Err(Error::WriteError(self.get_size(), dst.len())) - } - } - - /// Serializes the instance to a mutable slice without checking the destination size. - fn to_slice_unchecked(&'a self, dst: &mut [u8]); + fn to_slice(&'a self, dst: &mut [u8]) -> Result; // Serializes the instance to a writer destination, checking for I/O errors. #[cfg(not(feature = "no_std"))] diff --git a/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs b/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs index ddddc0c9fe..7f5a284f92 100644 --- a/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs +++ b/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs @@ -333,11 +333,16 @@ impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const where Self: TryInto, { - fn from_bytes_unchecked(data: &'a mut [u8]) -> Self { + fn from_bytes_(data: &'a mut [u8]) -> Result { + let size = Self::size_hint(data, 0)?; + if size > data.len() { + return Err(Error::ReadError(data.len(), size)); + } + let (head, _) = data.split_at_mut(size); if ISFIXED { - Self::Ref(data) + Ok(Self::Ref(head)) } else { - Self::Ref(&mut data[HEADERSIZE..]) + Ok(Self::Ref(&mut head[HEADERSIZE..])) } } @@ -351,8 +356,11 @@ where Ok(Self::Owned(dst)) } - fn to_slice_unchecked(&'a self, dst: &mut [u8]) { + fn to_slice(&'a self, dst: &mut [u8]) -> Result { let size = self.get_size(); + if dst.len() < size { + return Err(Error::WriteError(size, dst.len())); + } let header = self.get_header(); dst[0..HEADERSIZE].copy_from_slice(&header[..HEADERSIZE]); match self { @@ -365,6 +373,7 @@ where dst[HEADERSIZE..].copy_from_slice(data); } } + Ok(size) } #[cfg(not(feature = "no_std"))] diff --git a/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs b/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs index 4ac4c19a6e..01ccea376f 100644 --- a/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs +++ b/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs @@ -306,7 +306,7 @@ macro_rules! impl_codec_for_sequence { } let (head, t) = tail.split_at_mut(element_size); tail = t; - inner.push(T::from_bytes_unchecked(head)); + inner.push(T::from_bytes_(head)?); } Ok(Self(inner, PhantomData)) } From e1720b337c612da7648ac7e8190b6cdd78e34015 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 20 Jun 2026 10:44:18 +0530 Subject: [PATCH 11/20] Expose better surface API's and remove old redundant API's from binary_sv2 This solves the issue: https://github.com/stratum-mining/stratum/issues/2213, here we introduce new custom API, and deprecate old rigid API's --- .../stratum-translation/src/sv2_to_sv1.rs | 8 +- sv1/src/methods/client_to_server.rs | 8 +- sv1/src/utils.rs | 31 ++-- sv2/binary-sv2/src/codec/decodable.rs | 75 +++++++++ .../datatypes/non_copy_data_types/inner.rs | 158 ++++++++++++------ .../src/datatypes/non_copy_data_types/mod.rs | 39 +++-- .../non_copy_data_types/seq_inner.rs | 116 ++++++------- sv2/binary-sv2/src/lib.rs | 5 + sv2/channels-sv2/src/client/extended.rs | 21 ++- sv2/channels-sv2/src/client/standard.rs | 13 +- .../src/extranonce_manager/allocator.rs | 10 +- sv2/channels-sv2/src/server/extended.rs | 6 +- sv2/channels-sv2/src/server/jobs/extended.rs | 8 +- sv2/channels-sv2/src/server/jobs/factory.rs | 28 ++-- sv2/channels-sv2/src/server/jobs/standard.rs | 2 +- sv2/channels-sv2/src/server/standard.rs | 10 +- sv2/channels-sv2/src/target.rs | 4 +- sv2/codec-sv2/benches/common.rs | 4 +- sv2/parsers-sv2/src/tlv_extensions/mod.rs | 4 +- sv2/subprotocols/mining/src/new_mining_job.rs | 3 +- sv2/subprotocols/mining/src/open_channel.rs | 8 +- 21 files changed, 338 insertions(+), 223 deletions(-) diff --git a/stratum-core/stratum-translation/src/sv2_to_sv1.rs b/stratum-core/stratum-translation/src/sv2_to_sv1.rs index 12e63d5635..4103b38e28 100644 --- a/stratum-core/stratum-translation/src/sv2_to_sv1.rs +++ b/stratum-core/stratum-translation/src/sv2_to_sv1.rs @@ -45,8 +45,8 @@ pub fn build_sv1_notify_from_sv2( clean_jobs: bool, ) -> Result> { let new_job = match try_strip_bip141( - new_job.coinbase_tx_prefix.inner_as_ref(), - new_job.coinbase_tx_suffix.inner_as_ref(), + new_job.coinbase_tx_prefix.as_bytes(), + new_job.coinbase_tx_suffix.as_bytes(), ) .map_err(StratumTranslationError::FailedToTryToStripBip141)? { @@ -66,8 +66,8 @@ pub fn build_sv1_notify_from_sv2( let job_id = new_job.job_id.to_string(); let prev_hash = PrevHash(new_prev_hash.prev_hash.clone()); - let coin_base1 = new_job.coinbase_tx_prefix.to_vec().into(); - let coin_base2 = new_job.coinbase_tx_suffix.to_vec().into(); + let coin_base1 = new_job.coinbase_tx_prefix.to_owned_bytes().into(); + let coin_base2 = new_job.coinbase_tx_suffix.to_owned_bytes().into(); let merkle_path = new_job.merkle_path.clone().into_static().into_inner(); let merkle_branch: Vec = merkle_path.into_iter().map(MerkleNode).collect(); let version = HexU32Be(new_job.version); diff --git a/sv1/src/methods/client_to_server.rs b/sv1/src/methods/client_to_server.rs index 8977a06aed..0d709e8608 100644 --- a/sv1/src/methods/client_to_server.rs +++ b/sv1/src/methods/client_to_server.rs @@ -169,7 +169,7 @@ impl Submit<'_> { impl From> for Message { fn from(submit: Submit) -> Self { - let ex: String = submit.extra_nonce2.0.inner_as_ref().to_lower_hex_string(); + let ex: String = submit.extra_nonce2.0.as_bytes().to_lower_hex_string(); let mut params: Vec = vec![ submit.user_name.into(), submit.job_id.into(), @@ -335,7 +335,7 @@ impl<'a> TryFrom> for Message { fn try_from(subscribe: Subscribe) -> Result { let params = match (subscribe.agent_signature, subscribe.extranonce1) { - (a, Some(b)) => vec![a, b.0.inner_as_ref().to_lower_hex_string()], + (a, Some(b)) => vec![a, b.0.as_bytes().to_lower_hex_string()], (a, None) => vec![a], }; Ok(Message::StandardRequest(StandardRequest { @@ -800,7 +800,7 @@ fn test_subscribe_with_odd_length_extranonce() { assert_eq!(subscribe.agent_signature, "test-agent"); assert!(subscribe.extranonce1.is_some()); let extranonce = subscribe.extranonce1.unwrap(); - assert_eq!(extranonce.0.inner_as_ref(), &[0x0a, 0xbc]); // "0abc" -> [10, 188] + assert_eq!(extranonce.0.as_bytes(), &[0x0a, 0xbc]); // "0abc" -> [10, 188] } #[test] @@ -816,5 +816,5 @@ fn test_subscribe_with_even_length_extranonce() { assert_eq!(subscribe.agent_signature, "test-agent"); assert!(subscribe.extranonce1.is_some()); let extranonce = subscribe.extranonce1.unwrap(); - assert_eq!(extranonce.0.inner_as_ref(), &[0xab, 0xcd]); // "abcd" -> [171, 205] + assert_eq!(extranonce.0.as_bytes(), &[0xab, 0xcd]); // "abcd" -> [171, 205] } diff --git a/sv1/src/utils.rs b/sv1/src/utils.rs index 0dccec5d41..079a2c3399 100644 --- a/sv1/src/utils.rs +++ b/sv1/src/utils.rs @@ -15,16 +15,16 @@ pub struct Extranonce<'a>(pub B032<'a>); impl fmt::Display for Extranonce<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.0.inner_as_ref().to_lower_hex_string()) + f.write_str(&self.0.as_bytes().to_lower_hex_string()) } } impl Extranonce<'_> { pub fn len(&self) -> usize { - self.0.inner_as_ref().len() + self.0.len() } pub fn is_empty(&self) -> bool { - self.0.inner_as_ref().is_empty() + self.0.is_empty() } } @@ -37,7 +37,7 @@ impl<'a> TryFrom> for Extranonce<'a> { impl<'a> From> for Vec { fn from(v: Extranonce<'a>) -> Self { - v.0.to_vec() + v.0.into_bytes() } } @@ -81,7 +81,7 @@ impl<'a> TryFrom<&str> for Extranonce<'a> { impl<'a> From> for String { fn from(bytes: Extranonce<'a>) -> String { - bytes.0.inner_as_ref().to_lower_hex_string() + bytes.0.as_bytes().to_lower_hex_string() } } @@ -197,7 +197,7 @@ impl fmt::Display for PrevHash<'_> { impl<'a> From> for Vec { fn from(p_hash: PrevHash<'a>) -> Self { - p_hash.0.to_vec() + p_hash.0.into_bytes() } } @@ -245,7 +245,7 @@ impl From> for String { fn from(v: PrevHash) -> Self { let mut prev_hash_stratum_cursor = std::io::Cursor::new(Vec::new()); // swap every u32 from little endian to big endian - for chunk in v.0.inner_as_ref().chunks(size_of::()) { + for chunk in v.0.as_bytes().chunks(size_of::()) { let prev_hash_word = LittleEndian::read_u32(chunk); prev_hash_stratum_cursor .write_u32::(prev_hash_word) @@ -258,7 +258,7 @@ impl From> for String { // / Referencing the internal part of hex bytes impl AsRef<[u8]> for PrevHash<'_> { fn as_ref(&self) -> &[u8] { - self.0.inner_as_ref() + self.0.as_bytes() } } @@ -272,7 +272,7 @@ impl<'a> AsRef> for PrevHash<'a> { /// Referencing the internal part of hex bytes impl AsRef<[u8]> for Extranonce<'_> { fn as_ref(&self) -> &[u8] { - self.0.inner_as_ref() + self.0.as_bytes() } } @@ -281,13 +281,13 @@ pub struct MerkleNode<'a>(pub U256<'a>); impl fmt::Display for MerkleNode<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0.inner_as_ref().to_lower_hex_string()) + write!(f, "{}", self.0.as_bytes().to_lower_hex_string()) } } impl MerkleNode<'_> { pub fn is_empty(&self) -> bool { - self.0.inner_as_ref().is_empty() + self.0.is_empty() } } @@ -302,7 +302,7 @@ impl<'a> TryFrom> for MerkleNode<'a> { impl<'a> From> for Vec { fn from(v: MerkleNode<'a>) -> Self { - v.0.to_vec() + v.0.into_bytes() } } @@ -315,7 +315,7 @@ impl<'a> From> for Value { /// Referencing the internal part of hex bytes impl AsRef<[u8]> for MerkleNode<'_> { fn as_ref(&self) -> &[u8] { - self.0.inner_as_ref() + self.0.as_bytes() } } @@ -330,7 +330,7 @@ impl<'a> TryFrom<&str> for MerkleNode<'a> { impl<'a> From> for String { fn from(bytes: MerkleNode<'a>) -> String { - bytes.0.inner_as_ref().to_lower_hex_string() + bytes.0.as_bytes().to_lower_hex_string() } } @@ -432,8 +432,7 @@ mod tests { let value_to_string = back_to_hex_value.as_str().unwrap(); let chunk_size: usize = size_of::(); - let me_chunks = me.clone().0.to_vec(); - let me_chunks = me_chunks.chunks(chunk_size); + let me_chunks = me.0.as_bytes().chunks(chunk_size); for (be_chunk, le_chunk) in bytes.clone().chunks(chunk_size).zip(me_chunks) { let le_chunk = [le_chunk[0], le_chunk[1], le_chunk[2], le_chunk[3]]; let be_chunk = [be_chunk[0], be_chunk[1], be_chunk[2], be_chunk[3]]; diff --git a/sv2/binary-sv2/src/codec/decodable.rs b/sv2/binary-sv2/src/codec/decodable.rs index c2455fedfd..b96a63c6f9 100644 --- a/sv2/binary-sv2/src/codec/decodable.rs +++ b/sv2/binary-sv2/src/codec/decodable.rs @@ -93,6 +93,23 @@ pub trait Decodable<'a>: Sized { } } +pub(crate) fn from_bytes_owned>(data: Vec) -> Result { + let structure = T::get_structure(&data)?; + let mut fields = Vec::new(); + let mut tail = data.as_slice(); + + for field in structure { + let field_size = field.size_hint_(tail, 0)?; + if field_size > tail.len() { + return Err(Error::DecodableConversionError); + } + let (head, t) = tail.split_at(field_size); + tail = t; + fields.push(field.decode_owned(head)?); + } + T::from_decoded_fields(fields) +} + // Primitive data marker. // // Fundamental data types that can be passed to a decoder to define the structure of the type to be @@ -322,6 +339,47 @@ impl PrimitiveMarker { } } + fn decode_owned( + &self, + data: &[u8], + offset: usize, + ) -> Result, Error> { + macro_rules! decode_owned_copy { + ($ty:ty, $variant:ident) => {{ + let mut owned = data[offset..].to_vec(); + Ok(DecodablePrimitive::$variant(<$ty>::from_bytes_( + &mut owned, + )?)) + }}; + } + + macro_rules! decode_owned_inner { + ($ty:ty, $variant:ident) => {{ + let mut owned = data[offset..].to_vec(); + Ok(DecodablePrimitive::$variant( + <$ty>::from_bytes_(&mut owned)?.into_static(), + )) + }}; + } + + match self { + Self::U8 => decode_owned_copy!(u8, U8), + Self::U16 => decode_owned_copy!(u16, U16), + Self::Bool => decode_owned_copy!(bool, Bool), + Self::U24 => decode_owned_copy!(U24, U24), + Self::U256 => decode_owned_inner!(U256, U256), + Self::Signature => decode_owned_inner!(Signature, Signature), + Self::U32 => decode_owned_copy!(u32, U32), + Self::U32AsRef => decode_owned_inner!(U32AsRef, U32AsRef), + Self::F32 => decode_owned_copy!(f32, F32), + Self::U64 => decode_owned_copy!(u64, U64), + Self::B032 => decode_owned_inner!(B032, B032), + Self::B0255 => decode_owned_inner!(B0255, B0255), + Self::B064K => decode_owned_inner!(B064K, B064K), + Self::B016M => decode_owned_inner!(B016M, B016M), + } + } + fn decode_owned( &self, data: &[u8], @@ -448,6 +506,23 @@ impl FieldMarker { } } + pub(crate) fn decode_owned(&self, data: &[u8]) -> Result, Error> { + match self { + Self::Primitive(p) => Ok(DecodableField::Primitive(p.decode_owned(data, 0)?)), + Self::Struct(ps) => { + let mut decodeds = Vec::new(); + let mut tail = data; + for p in ps { + let field_size = p.size_hint_(tail, 0)?; + let (head, t) = tail.split_at(field_size); + tail = t; + decodeds.push(p.decode_owned(head)?); + } + Ok(DecodableField::Struct(decodeds)) + } + } + } + #[allow(clippy::wrong_self_convention)] #[cfg(not(feature = "no_std"))] #[allow(clippy::wrong_self_convention)] diff --git a/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs b/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs index 7f5a284f92..0bc4e74d24 100644 --- a/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs +++ b/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs @@ -21,8 +21,9 @@ // // # Usage // `Inner` offers several methods for data manipulation, including: -// - `to_vec()`: Returns a `Vec`, cloning the slice or owned data. -// - `inner_as_ref()` and `inner_as_mut()`: Provide immutable or mutable access to the data. +// - `as_bytes()` and `as_mut_bytes()`: Provide immutable or mutable access to the data. +// - `to_owned_bytes()` and `into_bytes()`: Return owned payload bytes. +// - `len()` and `is_empty()`: Inspect payload size directly. // - `expected_length(data: &[u8])`: Computes the expected length, validating it against // constraints. // - `get_header()`: Returns the data's header based on `HEADERSIZE`. @@ -73,55 +74,6 @@ pub enum Inner< Owned(Vec), } -impl Inner<'_, true, SIZE, 0, 0> { - // Converts the inner data to a vector, either by cloning the referenced slice or - // returning a clone of the owned vector. - pub fn to_vec(&self) -> Vec { - match self { - Inner::Ref(ref_) => ref_.to_vec(), - Inner::Owned(v) => v.clone(), - } - } - // Returns an immutable reference to the inner data, whether it's a reference or - // an owned vector. - pub fn inner_as_ref(&self) -> &[u8] { - match self { - Inner::Ref(ref_) => ref_, - Inner::Owned(v) => v, - } - } - // Provides a mutable reference to the inner data, allowing modification if the - // data is being referenced. - pub fn inner_as_mut(&mut self) -> &mut [u8] { - match self { - Inner::Ref(ref_) => ref_, - Inner::Owned(v) => v, - } - } -} - -impl - Inner<'_, false, SIZE, HEADERSIZE, MAXSIZE> -{ - // Similar to the fixed-size variant, this method converts the inner data into a vector. - // The data is either cloned from the referenced slice or returned as a clone of the - // owned vector. - pub fn to_vec(&self) -> Vec { - match self { - Inner::Ref(ref_) => ref_[..].to_vec(), - Inner::Owned(v) => v[..].to_vec(), - } - } - // Returns an immutable reference to the inner data for variable-size types, either - // referencing a slice or an owned vector. - pub fn inner_as_ref(&self) -> &[u8] { - match self { - Inner::Ref(ref_) => &ref_[..], - Inner::Owned(v) => &v[..], - } - } -} - impl PartialEq for Inner<'_, ISFIXED, SIZE, HEADERSIZE, MAXSIZE> { @@ -222,6 +174,11 @@ impl bool { + self.len() == 0 + } + // Retrieves the header as a byte vector. If `HEADERSIZE` is zero, an empty vector is // returned. Otherwise, the header is constructed from the length of the data. fn get_header(&self) -> Vec { @@ -395,6 +352,43 @@ where impl Inner<'_, ISFIXED, SIZE, HEADERSIZE, MAXSIZE> { + /// Returns the payload bytes without any SV2 length header. + pub fn as_bytes(&self) -> &[u8] { + match self { + Inner::Ref(data) => data, + Inner::Owned(data) => data, + } + } + + /// Returns the payload bytes mutably without any SV2 length header. + pub fn as_mut_bytes(&mut self) -> &mut [u8] { + match self { + Inner::Ref(data) => data, + Inner::Owned(data) => data, + } + } + + /// Clones the payload bytes into an owned vector. + pub fn to_owned_bytes(&self) -> Vec { + self.as_bytes().to_vec() + } + + /// Consumes the value and returns owned payload bytes. + pub fn into_bytes(self) -> Vec { + match self { + Inner::Ref(data) => data.to_vec(), + Inner::Owned(data) => data, + } + } + + /// Copies the payload bytes into an array of the requested size. + pub fn try_as_array(&self) -> Result<[u8; N], Error> { + self.as_bytes() + .try_into() + .map_err(|_| Error::ReadError(self.as_bytes().len(), N)) + } + + /// Converts the value into an owned static value. pub fn into_static(self) -> Inner<'static, ISFIXED, SIZE, HEADERSIZE, MAXSIZE> { match self { Inner::Ref(data) => { @@ -405,6 +399,37 @@ impl Inner::Owned(data), } } + + /// Alias for [`Inner::into_static`]. + pub fn into_owned(self) -> Inner<'static, ISFIXED, SIZE, HEADERSIZE, MAXSIZE> { + self.into_static() + } +} + +impl 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"), + } + } } impl Clone @@ -426,9 +451,32 @@ impl for Inner<'_, ISFIXED, SIZE, HEADERSIZE, MAXSIZE> { fn as_ref(&self) -> &[u8] { - match self { - Inner::Ref(r) => &r[..], - Inner::Owned(r) => &r[..], - } + self.as_bytes() + } +} + +impl + TryFrom<&[u8]> for Inner<'_, ISFIXED, SIZE, HEADERSIZE, MAXSIZE> +{ + type Error = Error; + + fn try_from(value: &[u8]) -> Result { + value.to_vec().try_into() + } +} + +impl From<[u8; SIZE]> for Inner<'_, true, SIZE, 0, 0> { + fn from(value: [u8; SIZE]) -> Self { + Inner::Owned(value.into()) + } +} + +impl + TryFrom<[u8; N]> for Inner<'_, false, SIZE, HEADERSIZE, MAXSIZE> +{ + type Error = Error; + + fn try_from(value: [u8; N]) -> Result { + value.to_vec().try_into() } } diff --git a/sv2/binary-sv2/src/datatypes/non_copy_data_types/mod.rs b/sv2/binary-sv2/src/datatypes/non_copy_data_types/mod.rs index 7d0be9e57d..feb2649a7e 100644 --- a/sv2/binary-sv2/src/datatypes/non_copy_data_types/mod.rs +++ b/sv2/binary-sv2/src/datatypes/non_copy_data_types/mod.rs @@ -69,7 +69,7 @@ fn bytes_to_hex<'a>(bytes: impl IntoIterator) -> String { impl fmt::Display for U32AsRef<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let inner = self.inner_as_ref(); + let inner = self.as_bytes(); write!( f, "U32AsRef({})", @@ -91,7 +91,7 @@ impl fmt::Display for Sv2Option<'_, u32> { impl B0255<'_> { pub fn as_hex(&self) -> String { - let inner = bytes_to_hex(self.inner_as_ref()); + let inner = bytes_to_hex(self.as_bytes()); format!("B0255({inner})") } } @@ -99,23 +99,23 @@ impl B0255<'_> { impl Str0255<'_> { /// Returns the value as a UTF-8 string if possible, otherwise as a hex string prefixed with 0x. pub fn as_utf8_or_hex(&self) -> String { - match core::str::from_utf8(self.inner_as_ref()) { + match core::str::from_utf8(self.as_bytes()) { Ok(s) => alloc::string::String::from(s), - Err(_) => format!("0x{}", bytes_to_hex(self.inner_as_ref())), + Err(_) => format!("0x{}", bytes_to_hex(self.as_bytes())), } } } impl fmt::Display for B064K<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let inner = bytes_to_hex(self.inner_as_ref()); + let inner = bytes_to_hex(self.as_bytes()); write!(f, "B064K({inner})") } } impl fmt::Display for U256<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let inner = bytes_to_hex(self.inner_as_ref().iter().rev()); + let inner = bytes_to_hex(self.as_bytes().iter().rev()); write!(f, "U256({inner})") } } @@ -123,7 +123,7 @@ impl fmt::Display for U256<'_> { impl fmt::Display for Seq0255<'_, U256<'_>> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let len = self.len(); - let as_hex = |item: &U256<'_>| bytes_to_hex(item.inner_as_ref().iter().rev()); + let as_hex = |item: &U256<'_>| bytes_to_hex(item.as_bytes().iter().rev()); write!(f, "Seq0255 write!(f, "[]"), @@ -153,7 +153,7 @@ impl fmt::Display for Seq064K<'_, B016M<'_>> { let len = self.len(); let as_hex = |item: &B016M<'_>| { - let hex = bytes_to_hex(item.inner_as_ref()); + let hex = bytes_to_hex(item.as_bytes()); if hex.len() > 500 { format!("{}…", &hex[..500], hex.len() - 500) @@ -189,7 +189,7 @@ impl fmt::Display for Seq064K<'_, B016M<'_>> { impl fmt::Display for Seq064K<'_, U256<'_>> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let len = self.len(); - let as_hex = |item: &U256<'_>| bytes_to_hex(item.inner_as_ref().iter().rev()); + let as_hex = |item: &U256<'_>| bytes_to_hex(item.as_bytes().iter().rev()); write!(f, "Seq064K write!(f, "[]"), @@ -257,17 +257,11 @@ impl fmt::Display for Seq064K<'_, u32> { impl fmt::Display for B032<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let item = bytes_to_hex(self.inner_as_ref()); + let item = bytes_to_hex(self.as_bytes()); write!(f, "B032({item})") } } -impl From<[u8; 32]> for U256<'_> { - fn from(v: [u8; 32]) -> Self { - Inner::Owned(v.into()) - } -} - use core::convert::{TryFrom, TryInto}; // Attempts to convert a `String` into a `Str0255<'a>`. @@ -279,12 +273,21 @@ impl TryFrom for Str0255<'_> { } } +// Attempts to convert a string slice into an owned `Str0255`. +impl TryFrom<&str> for Str0255<'_> { + type Error = crate::Error; + + fn try_from(value: &str) -> Result { + value.as_bytes().try_into() + } +} + /// Represents a reference to a 32-bit unsigned integer (`u32`), /// providing methods for convenient conversions. impl U32AsRef<'_> { /// Returns the `u32` value represented by this reference. pub fn as_u32(&self) -> u32 { - let inner = self.inner_as_ref(); + let inner = self.as_bytes(); u32::from_le_bytes([inner[0], inner[1], inner[2], inner[3]]) } } @@ -299,7 +302,7 @@ impl From for U32AsRef<'_> { impl<'a> From<&'a U32AsRef<'a>> for u32 { fn from(v: &'a U32AsRef<'a>) -> Self { - let b = v.inner_as_ref(); + let b = v.as_bytes(); u32::from_le_bytes([b[0], b[1], b[2], b[3]]) } } diff --git a/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs b/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs index 01ccea376f..ebf1fa5835 100644 --- a/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs +++ b/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs @@ -10,8 +10,7 @@ // ### `Seq0255` // - Represents a sequence of up to 255 elements. // - Includes utility methods such as: -// - `to_vec()`: Converts each element into its byte vector representation. -// - `inner_as_ref()`: Provides references to the inner data for each element. +// - `iter_bytes()`: Provides byte references for each element without allocating. // - `new()`: Creates a `Seq0255` instance, enforcing the maximum length constraint. // - Implements the `Decodable` trait for seamless deserialization, and `GetSize` to calculate the // encoded size, ensuring compatibility with various serialization formats. @@ -19,7 +18,7 @@ // ### `Seq064K` // - Represents a sequence of up to 65535 elements. // - Similar to `Seq0255`, it provides: -// - `to_vec()` and `inner_as_ref()` methods to convert or reference each element. +// - `iter_bytes()` to reference each element's bytes without allocating. // - `new()` enforces the maximum size limit, preventing excess memory usage. // - Like `Seq0255`, `Seq064K` is `Decodable` and implements `GetSize`, making it versatile for // serialization scenarios. @@ -52,57 +51,21 @@ use crate::{ }; use core::{marker::PhantomData, ops::Index, slice}; -// TODO add test for that -impl<'a, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> - Seq0255<'a, super::inner::Inner<'a, false, SIZE, HEADERSIZE, MAXSIZE>> +impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> + Seq0255<'a, Inner<'a, ISFIXED, SIZE, HEADERSIZE, MAXSIZE>> { - /// Converts the inner types to owned vector, and collects. - pub fn to_vec(&self) -> Vec> { - self.0.iter().map(|x| x.to_vec()).collect() - } - /// Converts the inner types to shared reference, and collects. - pub fn inner_as_ref(&self) -> Vec<&[u8]> { - self.0.iter().map(|x| x.inner_as_ref()).collect() + /// Iterates over element payload bytes without allocating. + pub fn iter_bytes(&self) -> impl Iterator { + self.0.iter().map(|x| x.as_bytes()) } } -// TODO add test for that -impl<'a, const SIZE: usize> Seq0255<'a, super::inner::Inner<'a, true, SIZE, 0, 0>> { - /// Converts the inner types to owned vector, and collects. - pub fn to_vec(&self) -> Vec> { - self.0.iter().map(|x| x.to_vec()).collect() - } - - /// Converts the inner types to shared reference, and collects. - pub fn inner_as_ref(&self) -> Vec<&[u8]> { - self.0.iter().map(|x| x.inner_as_ref()).collect() - } -} -// TODO add test for that -impl<'a, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> - Seq064K<'a, super::inner::Inner<'a, false, SIZE, HEADERSIZE, MAXSIZE>> +impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> + Seq064K<'a, Inner<'a, ISFIXED, SIZE, HEADERSIZE, MAXSIZE>> { - /// Converts the inner types to owned vector, and collects. - pub fn to_vec(&self) -> Vec> { - self.0.iter().map(|x| x.to_vec()).collect() - } - - /// Converts the inner types to shared reference, and collects. - pub fn inner_as_ref(&self) -> Vec<&[u8]> { - self.0.iter().map(|x| x.inner_as_ref()).collect() - } -} - -// TODO add test for that -impl<'a, const SIZE: usize> Seq064K<'a, super::inner::Inner<'a, true, SIZE, 0, 0>> { - /// Converts the inner types to owned vector, and collects. - pub fn to_vec(&self) -> Vec> { - self.0.iter().map(|x| x.to_vec()).collect() - } - - /// Converts the inner types to shared reference, and collects. - pub fn inner_as_ref(&self) -> Vec<&[u8]> { - self.0.iter().map(|x| x.inner_as_ref()).collect() + /// Iterates over element payload bytes without allocating. + pub fn iter_bytes(&self) -> impl Iterator { + self.0.iter().map(|x| x.as_bytes()) } } @@ -421,12 +384,22 @@ impl Seq0255<'_, T> { // Safe unwrap cause the initial value is a valid Seq0255 Seq0255::new(self.0).unwrap() } + + /// Alias for [`Seq0255::into_static`]. + pub fn into_owned(self) -> Seq0255<'static, T> { + self.into_static() + } } impl Sv2Option<'_, T> { /// converts the lifetime to static pub fn into_static(self) -> Sv2Option<'static, T> { Sv2Option::new(self.into_inner()) } + + /// Alias for [`Sv2Option::into_static`]. + pub fn into_owned(self) -> Sv2Option<'static, T> { + self.into_static() + } } impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> @@ -441,6 +414,13 @@ impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const // Safe unwrap cause the initial value is a valid Seq0255 Seq0255::new(static_seq).unwrap() } + + /// Alias for [`Seq0255::into_static`]. + pub fn into_owned( + self, + ) -> Seq0255<'static, Inner<'static, ISFIXED, SIZE, HEADERSIZE, MAXSIZE>> { + self.into_static() + } } impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> @@ -454,6 +434,13 @@ impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const let static_inner = inner.map(|x| x.into_static()); Sv2Option::new(static_inner) } + + /// Alias for [`Sv2Option::into_static`]. + pub fn into_owned( + self, + ) -> Sv2Option<'static, Inner<'static, ISFIXED, SIZE, HEADERSIZE, MAXSIZE>> { + self.into_static() + } } impl Seq064K<'_, T> { @@ -462,6 +449,11 @@ impl Seq064K<'_, T> { // Safe unwrap cause the initial value is a valid Seq064K Seq064K::new(self.0).unwrap() } + + /// Alias for [`Seq064K::into_static`]. + pub fn into_owned(self) -> Seq064K<'static, T> { + self.into_static() + } } impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> @@ -476,6 +468,13 @@ impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const // Safe unwrap cause the initial value is a valid Seq064K Seq064K::new(static_seq).unwrap() } + + /// Alias for [`Seq064K::into_static`]. + pub fn into_owned( + self, + ) -> Seq064K<'static, Inner<'static, ISFIXED, SIZE, HEADERSIZE, MAXSIZE>> { + self.into_static() + } } /// The lifetime 'a is defined. @@ -487,20 +486,23 @@ pub struct Sv2Option<'a, T>(Vec, PhantomData<&'a T>); impl<'a, const SIZE: usize> Sv2Option<'a, super::inner::Inner<'a, true, SIZE, 0, 0>> { /// Gets the owned first element of the sequence, if present pub fn to_option(&self) -> Option> { - let v: Vec> = self.0.iter().map(|x| x.to_vec()).collect(); - match v.len() { + match self.0.len() { 0 => None, - 1 => Some(v[0].clone()), + 1 => Some(self.0[0].to_owned_bytes()), // is impossible to deserialize Sv2Options with len bigger than 1 _ => unreachable!(), } } - /// Gets the reference to first element of the sequence, if present - pub fn inner_as_ref(&self) -> Option<&[u8]> { - let v: Vec<&[u8]> = self.0.iter().map(|x| x.inner_as_ref()).collect(); - match v.len() { +} + +impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> + Sv2Option<'a, Inner<'a, ISFIXED, SIZE, HEADERSIZE, MAXSIZE>> +{ + /// Gets the reference to the first element's payload bytes, if present. + pub fn as_option_bytes(&self) -> Option<&[u8]> { + match self.0.len() { 0 => None, - 1 => Some(v[0]), + 1 => Some(self.0[0].as_bytes()), // is impossible to deserialize Sv2Options with len bigger than 1 _ => unreachable!(), } diff --git a/sv2/binary-sv2/src/lib.rs b/sv2/binary-sv2/src/lib.rs index 409f8fcdd0..0b661be9ac 100644 --- a/sv2/binary-sv2/src/lib.rs +++ b/sv2/binary-sv2/src/lib.rs @@ -137,6 +137,11 @@ pub fn from_bytes<'a, T: Decodable<'a>>(data: &'a mut [u8]) -> Result T::from_bytes(data) } +/// Decodes an SV2-encoded byte vector into owned/static values. +pub fn from_bytes_owned>(data: Vec) -> Result { + crate::codec::decodable::from_bytes_owned(data) +} + /// Provides an interface and implementation details for decoding complex data structures /// from raw bytes or I/O streams. Handles deserialization of nested and primitive data /// structures through traits, enums, and helper functions for managing the decoding process. diff --git a/sv2/channels-sv2/src/client/extended.rs b/sv2/channels-sv2/src/client/extended.rs index f19afea9d0..11cc331bc9 100644 --- a/sv2/channels-sv2/src/client/extended.rs +++ b/sv2/channels-sv2/src/client/extended.rs @@ -292,8 +292,8 @@ impl<'a> ExtendedChannel<'a> { // try to strip bip141 bytes from coinbase_tx_prefix and coinbase_tx_suffix, if they are // present let new_extended_mining_job = match try_strip_bip141( - new_extended_mining_job.coinbase_tx_prefix.inner_as_ref(), - new_extended_mining_job.coinbase_tx_suffix.inner_as_ref(), + new_extended_mining_job.coinbase_tx_prefix.as_bytes(), + new_extended_mining_job.coinbase_tx_suffix.as_bytes(), ) .map_err(ExtendedChannelError::FailedToTryToStripBip141)? { @@ -368,14 +368,13 @@ impl<'a> ExtendedChannel<'a> { let deserialized_outputs = Vec::::consensus_decode( &mut set_custom_mining_job .coinbase_tx_outputs - .inner_as_ref() - .to_vec() + .to_owned_bytes() .as_slice(), ) .map_err(|_| ExtendedChannelError::FailedToDeserializeCoinbaseOutputs)?; let mut script_sig = vec![]; - script_sig.extend_from_slice(set_custom_mining_job.coinbase_prefix.inner_as_ref()); + script_sig.extend_from_slice(set_custom_mining_job.coinbase_prefix.as_bytes()); let full_extranonce_size = self.get_full_extranonce_size(); let full_extranonce = vec![0; full_extranonce_size]; script_sig.extend_from_slice(&full_extranonce); @@ -404,7 +403,7 @@ impl<'a> ExtendedChannel<'a> { + 32 // prev OutPoint + 4 // index + 1 // bytes in script - + set_custom_mining_job.coinbase_prefix.inner_as_ref().len(); + + set_custom_mining_job.coinbase_prefix.len(); let coinbase_tx_prefix = serialized_coinbase[0..prefix_index].to_vec(); @@ -551,7 +550,7 @@ impl<'a> ExtendedChannel<'a> { )); }; - let extranonce_size = share.extranonce.inner_as_ref().len(); + let extranonce_size = share.extranonce.len(); if extranonce_size != self.rollable_extranonce_size as usize { return Err(ShareValidationError::BadExtranonceSize( ERROR_CODE_SUBMIT_SHARES_BAD_EXTRANONCE_SIZE, @@ -560,7 +559,7 @@ impl<'a> ExtendedChannel<'a> { let mut full_extranonce = vec![]; full_extranonce.extend_from_slice(job.1.as_slice()); - full_extranonce.extend_from_slice(share.extranonce.inner_as_ref()); + full_extranonce.extend_from_slice(share.extranonce.as_bytes()); // calculate the merkle root from: // - job coinbase_tx_prefix @@ -568,10 +567,10 @@ impl<'a> ExtendedChannel<'a> { // - job coinbase_tx_suffix // - job merkle_path let merkle_root: [u8; 32] = merkle_root_from_path( - job.0.coinbase_tx_prefix.inner_as_ref(), - job.0.coinbase_tx_suffix.inner_as_ref(), + job.0.coinbase_tx_prefix.as_bytes(), + job.0.coinbase_tx_suffix.as_bytes(), full_extranonce.as_ref(), - &job.0.merkle_path.inner_as_ref(), + job.0.merkle_path.as_slice(), ) .ok_or(ShareValidationError::Invalid( ERROR_CODE_SUBMIT_SHARES_INVALID_SHARE, diff --git a/sv2/channels-sv2/src/client/standard.rs b/sv2/channels-sv2/src/client/standard.rs index dff74269fc..606c03dafc 100644 --- a/sv2/channels-sv2/src/client/standard.rs +++ b/sv2/channels-sv2/src/client/standard.rs @@ -238,10 +238,10 @@ impl<'a> StandardChannel<'a> { /// The new job is constructed using the current extranonce prefix. pub fn on_new_group_channel_job(&mut self, new_extended_mining_job: NewExtendedMiningJob<'a>) { let merkle_root = merkle_root_from_path( - new_extended_mining_job.coinbase_tx_prefix.inner_as_ref(), - new_extended_mining_job.coinbase_tx_suffix.inner_as_ref(), + new_extended_mining_job.coinbase_tx_prefix.as_bytes(), + new_extended_mining_job.coinbase_tx_suffix.as_bytes(), self.extranonce_prefix.as_bytes(), - &new_extended_mining_job.merkle_path.inner_as_ref(), + new_extended_mining_job.merkle_path.as_slice(), ) .expect("merkle root must be valid") .try_into() @@ -357,12 +357,7 @@ impl<'a> StandardChannel<'a> { )); }; - let merkle_root: [u8; 32] = job - .0 - .merkle_root - .inner_as_ref() - .try_into() - .expect("merkle root must be 32 bytes"); + let merkle_root = job.0.merkle_root.to_array(); let chain_tip = self .chain_tip diff --git a/sv2/channels-sv2/src/extranonce_manager/allocator.rs b/sv2/channels-sv2/src/extranonce_manager/allocator.rs index db13af052a..e31ebf0718 100644 --- a/sv2/channels-sv2/src/extranonce_manager/allocator.rs +++ b/sv2/channels-sv2/src/extranonce_manager/allocator.rs @@ -428,12 +428,12 @@ mod tests { assert_eq!(alloc.allocated_count(), 0); let ext = alloc.allocate_extended(16).unwrap(); - assert_eq!(ext.as_bytes().len(), 4); + assert_eq!(ext.len(), 4); assert_eq!(&ext.as_bytes()[0..2], &[0x00, 0x01]); assert_eq!(alloc.allocated_count(), 1); let std = alloc.allocate_standard().unwrap(); - assert_eq!(std.as_bytes().len(), 20); + assert_eq!(std.len(), 20); assert_eq!(&std.as_bytes()[0..2], &[0x00, 0x01]); assert_eq!(alloc.allocated_count(), 2); } @@ -459,7 +459,7 @@ mod tests { assert_eq!(alloc.rollable_extranonce_size(), 14); let ext = alloc.allocate_extended(14).unwrap(); - assert_eq!(ext.as_bytes().len(), 6); + assert_eq!(ext.len(), 6); assert_eq!(&ext.as_bytes()[0..4], &[0xAA, 0xBB, 0xCC, 0xDD]); } @@ -490,7 +490,7 @@ mod tests { let ext = alloc.allocate_extended(4).unwrap(); // Prefix bytes must be [upstream | local_prefix | local_index]. - assert_eq!(ext.as_bytes().len(), 16); + assert_eq!(ext.len(), 16); assert_eq!(&ext.as_bytes()[0..4], upstream_prefix.as_slice()); assert_eq!(&ext.as_bytes()[4..15], local_prefix_bytes.as_slice()); // local_index is 1 byte; first allocation index is 0. @@ -626,7 +626,7 @@ mod tests { let mut alloc = ExtranonceAllocator::new(vec![0x01], 20, 256).unwrap(); let std = alloc.allocate_standard().unwrap(); - assert_eq!(std.as_bytes().len(), 20); + assert_eq!(std.len(), 20); assert!(std.as_bytes()[2..].iter().all(|&b| b == 0)); } diff --git a/sv2/channels-sv2/src/server/extended.rs b/sv2/channels-sv2/src/server/extended.rs index ee178b64d8..26b5dcbc47 100644 --- a/sv2/channels-sv2/src/server/extended.rs +++ b/sv2/channels-sv2/src/server/extended.rs @@ -715,7 +715,7 @@ impl<'a> ExtendedChannel<'a> { )); }; - let extranonce_size = share.extranonce.inner_as_ref().len(); + let extranonce_size = share.extranonce.len(); if extranonce_size != self.rollable_extranonce_size as usize { self.share_accounting .increment_rejected_shares(ERROR_CODE_SUBMIT_SHARES_BAD_EXTRANONCE_SIZE); @@ -727,7 +727,7 @@ impl<'a> ExtendedChannel<'a> { let extranonce_prefix = job.get_extranonce_prefix(); let mut full_extranonce = vec![]; full_extranonce.extend_from_slice(extranonce_prefix); - full_extranonce.extend(share.extranonce.inner_as_ref()); + full_extranonce.extend(share.extranonce.as_bytes()); // calculate the merkle root from: // - job coinbase_tx_prefix @@ -738,7 +738,7 @@ impl<'a> ExtendedChannel<'a> { &job.get_coinbase_tx_prefix_without_bip141(), &job.get_coinbase_tx_suffix_without_bip141(), full_extranonce.as_ref(), - &job.get_merkle_path().inner_as_ref(), + job.get_merkle_path().as_slice(), ) .ok_or(ShareValidationError::Invalid( ERROR_CODE_SUBMIT_SHARES_INVALID_SHARE, diff --git a/sv2/channels-sv2/src/server/jobs/extended.rs b/sv2/channels-sv2/src/server/jobs/extended.rs index 0866f2070f..81216da937 100644 --- a/sv2/channels-sv2/src/server/jobs/extended.rs +++ b/sv2/channels-sv2/src/server/jobs/extended.rs @@ -57,7 +57,7 @@ impl<'a> ExtendedJob<'a> { job_message: NewExtendedMiningJob<'a>, ) -> Result { let template_coinbase_outputs = deserialize_template_outputs( - template.coinbase_tx_outputs.to_vec(), + template.coinbase_tx_outputs.to_owned_bytes(), template.coinbase_tx_outputs_count, ) .map_err(|_| ExtendedJobError::FailedToDeserializeCoinbaseOutputs)?; @@ -117,7 +117,7 @@ impl<'a> ExtendedJob<'a> { &self.get_coinbase_tx_prefix_without_bip141(), &self.get_coinbase_tx_suffix_without_bip141(), &extranonce_prefix, - &self.get_merkle_path().inner_as_ref(), + self.get_merkle_path().as_slice(), ) .ok_or(ExtendedJobError::FailedToCalculateMerkleRoot)? .try_into() @@ -154,12 +154,12 @@ impl<'a> ExtendedJob<'a> { /// Returns the coinbase transaction without for this job without BIP141 data. pub fn get_coinbase_tx_prefix_without_bip141(&self) -> Vec { - self.job_message.coinbase_tx_prefix.inner_as_ref().to_vec() + self.job_message.coinbase_tx_prefix.to_owned_bytes() } /// Returns the coinbase transaction suffix for this job without BIP141 data. pub fn get_coinbase_tx_suffix_without_bip141(&self) -> Vec { - self.job_message.coinbase_tx_suffix.inner_as_ref().to_vec() + self.job_message.coinbase_tx_suffix.to_owned_bytes() } /// Sets the extranonce prefix for this job. diff --git a/sv2/channels-sv2/src/server/jobs/factory.rs b/sv2/channels-sv2/src/server/jobs/factory.rs index 266555c3b2..7d3ee128f3 100644 --- a/sv2/channels-sv2/src/server/jobs/factory.rs +++ b/sv2/channels-sv2/src/server/jobs/factory.rs @@ -176,7 +176,7 @@ impl JobFactory { &coinbase_tx_prefix, &coinbase_tx_suffix, &extranonce_prefix, - &merkle_path.inner_as_ref(), + merkle_path.as_slice(), ) .expect("merkle root must be valid") .try_into() @@ -374,7 +374,7 @@ impl JobFactory { } let template_outputs = deserialize_template_outputs( - template.coinbase_tx_outputs.to_vec(), + template.coinbase_tx_outputs.to_owned_bytes(), template.coinbase_tx_outputs_count, ) .map_err(|_| JobFactoryError::DeserializeCoinbaseOutputsError)?; @@ -386,7 +386,7 @@ impl JobFactory { let serialized_outputs = serialize(&coinbase_tx_outputs); let mut coinbase_prefix = vec![]; - coinbase_prefix.extend_from_slice(&template.coinbase_prefix.to_vec()); + coinbase_prefix.extend_from_slice(template.coinbase_prefix.as_bytes()); coinbase_prefix.extend_from_slice(&self.op_pushbytes_pool_miner_tag()?); coinbase_prefix.push(full_extranonce_size as u8); // OP_PUSHBYTES_X (for the full extranonce) @@ -424,10 +424,7 @@ impl JobFactory { extranonce_prefix: Vec, full_extranonce_size: usize, ) -> Result, JobFactoryError> { - let serialized_outputs = set_custom_mining_job - .coinbase_tx_outputs - .inner_as_ref() - .to_vec(); + let serialized_outputs = set_custom_mining_job.coinbase_tx_outputs.to_owned_bytes(); let coinbase_outputs = Vec::::consensus_decode(&mut serialized_outputs.as_slice()) .map_err(|_| JobFactoryError::DeserializeCoinbaseOutputsError)?; @@ -489,13 +486,12 @@ impl JobFactory { m: SetCustomMiningJob<'_>, full_extranonce_size: usize, ) -> Result { - let deserialized_outputs = Vec::::consensus_decode( - &mut m.coinbase_tx_outputs.inner_as_ref().to_vec().as_slice(), - ) - .map_err(|_| JobFactoryError::DeserializeCoinbaseOutputsError)?; + let deserialized_outputs = + Vec::::consensus_decode(&mut m.coinbase_tx_outputs.to_owned_bytes().as_slice()) + .map_err(|_| JobFactoryError::DeserializeCoinbaseOutputsError)?; let mut script_sig = vec![]; - script_sig.extend_from_slice(m.coinbase_prefix.inner_as_ref()); + script_sig.extend_from_slice(m.coinbase_prefix.as_bytes()); script_sig.extend_from_slice(&vec![0; full_extranonce_size]); // Create transaction input @@ -530,7 +526,7 @@ impl JobFactory { + 32 // prev OutPoint + 4 // index + 1 // bytes in script - + m.coinbase_prefix.inner_as_ref().len(); + + m.coinbase_prefix.len(); let coinbase_tx_prefix = serialized_coinbase[0..index].to_vec(); @@ -551,7 +547,7 @@ impl JobFactory { + 32 // prev OutPoint + 4 // index + 1 // bytes in script - + m.coinbase_prefix.inner_as_ref().len() + + m.coinbase_prefix.len() + full_extranonce_size; let coinbase_tx_suffix = serialized_coinbase[index..].to_vec(); @@ -586,7 +582,7 @@ impl JobFactory { } let mut template_outputs = deserialize_template_outputs( - template.coinbase_tx_outputs.to_vec(), + template.coinbase_tx_outputs.to_owned_bytes(), template.coinbase_tx_outputs_count, ) .map_err(|_| JobFactoryError::DeserializeCoinbaseOutputsError)?; @@ -596,7 +592,7 @@ impl JobFactory { let op_pushbytes_pool_miner_tag = self.op_pushbytes_pool_miner_tag()?; let mut script_sig = vec![]; - script_sig.extend_from_slice(&template.coinbase_prefix.to_vec()); + script_sig.extend_from_slice(template.coinbase_prefix.as_bytes()); script_sig.extend_from_slice(&op_pushbytes_pool_miner_tag); script_sig.push(full_extranonce_size as u8); // OP_PUSHBYTES_X (for the full extranonce) script_sig.extend_from_slice(&vec![0; full_extranonce_size]); diff --git a/sv2/channels-sv2/src/server/jobs/standard.rs b/sv2/channels-sv2/src/server/jobs/standard.rs index d1d0e8b354..898ca628a7 100644 --- a/sv2/channels-sv2/src/server/jobs/standard.rs +++ b/sv2/channels-sv2/src/server/jobs/standard.rs @@ -65,7 +65,7 @@ impl<'a> StandardJob<'a> { job_message: NewMiningJob<'a>, ) -> Result { let template_coinbase_outputs = deserialize_template_outputs( - template.coinbase_tx_outputs.to_vec(), + template.coinbase_tx_outputs.to_owned_bytes(), template.coinbase_tx_outputs_count, ) .map_err(|_| StandardJobError::FailedToDeserializeCoinbaseOutputs)?; diff --git a/sv2/channels-sv2/src/server/standard.rs b/sv2/channels-sv2/src/server/standard.rs index 8fcf04caf2..dd3eca4a8f 100644 --- a/sv2/channels-sv2/src/server/standard.rs +++ b/sv2/channels-sv2/src/server/standard.rs @@ -63,7 +63,7 @@ use mining_sv2::{ 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 std::{collections::HashMap, marker::PhantomData}; use template_distribution_sv2::{NewTemplate, SetNewPrevHash}; use tracing::debug; @@ -615,11 +615,7 @@ impl<'a> StandardChannel<'a> { )); }; - let merkle_root: [u8; 32] = job - .get_merkle_root() - .inner_as_ref() - .try_into() - .expect("merkle root must be 32 bytes"); + let merkle_root = job.get_merkle_root().to_array(); let chain_tip = self .chain_tip @@ -682,7 +678,7 @@ impl<'a> StandardChannel<'a> { .op_pushbytes_pool_miner_tag() .map_err(|_| ShareValidationError::InvalidCoinbase)?; - let mut script_sig = job.get_template().coinbase_prefix.to_vec(); + let mut script_sig = job.get_template().coinbase_prefix.to_owned_bytes(); script_sig.extend(op_pushbytes_pool_miner_tag); script_sig.push(self.extranonce_prefix.len() as u8); // OP_PUSHBYTES_X (for the extranonce) script_sig.extend(job.get_extranonce_prefix()); diff --git a/sv2/channels-sv2/src/target.rs b/sv2/channels-sv2/src/target.rs index abd233df1b..08b8748c7f 100644 --- a/sv2/channels-sv2/src/target.rs +++ b/sv2/channels-sv2/src/target.rs @@ -9,7 +9,7 @@ use primitive_types::U256 as U256Primitive; /// Converts a `u256` to a [`BlockHash`] type. pub fn u256_to_block_hash(v: U256<'static>) -> BlockHash { - let hash: [u8; 32] = v.to_vec().try_into().unwrap(); + let hash = v.into_array(); let hash = Hash::from_slice(&hash).unwrap(); BlockHash::from_raw_hash(hash) } @@ -171,7 +171,7 @@ pub fn hash_rate_from_target(target: U256<'static>, share_per_min: f64) -> Resul } let mut target_arr: [u8; 32] = [0; 32]; let slice: &mut [u8] = &mut target_arr; - slice.copy_from_slice(target.inner_as_ref()); + slice.copy_from_slice(target.as_bytes()); target_arr.reverse(); let target = U256Primitive::from_big_endian(target_arr.as_ref()); // we calculate the numerator 2^256-t diff --git a/sv2/codec-sv2/benches/common.rs b/sv2/codec-sv2/benches/common.rs index 4f92bb2587..38f1eff172 100644 --- a/sv2/codec-sv2/benches/common.rs +++ b/sv2/codec-sv2/benches/common.rs @@ -36,11 +36,11 @@ impl OwnedMsg { #[allow(dead_code)] pub fn from_zc(msg: ZeroCopyMsg<'_>) -> Self { let mut merkle_root = [0u8; 32]; - merkle_root.copy_from_slice(msg.merkle_root.inner_as_ref()); + merkle_root.copy_from_slice(msg.merkle_root.as_bytes()); OwnedMsg { channel_id: msg.channel_id, merkle_root, - coinbase_suffix: msg.coinbase_suffix.inner_as_ref().to_vec(), + coinbase_suffix: msg.coinbase_suffix.to_owned_bytes(), } } } diff --git a/sv2/parsers-sv2/src/tlv_extensions/mod.rs b/sv2/parsers-sv2/src/tlv_extensions/mod.rs index 2a0d8d772a..511a5459f4 100644 --- a/sv2/parsers-sv2/src/tlv_extensions/mod.rs +++ b/sv2/parsers-sv2/src/tlv_extensions/mod.rs @@ -66,8 +66,8 @@ impl TlvField for UserIdentity { /// Converts this UserIdentity into a TLV structure. fn to_tlv(&self) -> Result { // Validate length - if self.as_bytes().len() > MAX_USER_IDENTITY_LENGTH { - return Err(UserIdentityError::TooLong(self.as_bytes().len()).into()); + if self.len() > MAX_USER_IDENTITY_LENGTH { + return Err(UserIdentityError::TooLong(self.len()).into()); } Ok(Tlv::new( diff --git a/sv2/subprotocols/mining/src/new_mining_job.rs b/sv2/subprotocols/mining/src/new_mining_job.rs index c5afb2f8a2..af1aa1bd79 100644 --- a/sv2/subprotocols/mining/src/new_mining_job.rs +++ b/sv2/subprotocols/mining/src/new_mining_job.rs @@ -214,8 +214,7 @@ mod tests { job_id, min_ntime: Sv2Option::new(min_ntime), version, - merkle_root: U256::try_from(merkle_root.to_vec()) - .expect("NewMiningJob: failed to convert merkle_root to B032"), + merkle_root: U256::from(merkle_root), }; let static_nmj = nmj.clone().as_static(); static_nmj.channel_id == nmj.channel_id diff --git a/sv2/subprotocols/mining/src/open_channel.rs b/sv2/subprotocols/mining/src/open_channel.rs index 13544fbeef..4481733537 100644 --- a/sv2/subprotocols/mining/src/open_channel.rs +++ b/sv2/subprotocols/mining/src/open_channel.rs @@ -56,7 +56,7 @@ impl OpenStandardMiningChannel<'_> { pub fn update_id(&mut self, new_id: u32) { let bytes_new = new_id.to_le_bytes(); - let bytes_old = self.request_id.inner_as_mut(); + let bytes_old = self.request_id.as_mut_bytes(); bytes_old[0] = bytes_new[0]; bytes_old[1] = bytes_new[1]; bytes_old[2] = bytes_new[2]; @@ -107,7 +107,7 @@ impl OpenStandardMiningChannelSuccess<'_> { pub fn update_id(&mut self, new_id: u32) { let bytes_new = new_id.to_le_bytes(); - let bytes_old = self.request_id.inner_as_mut(); + let bytes_old = self.request_id.as_mut_bytes(); bytes_old[0] = bytes_new[0]; bytes_old[1] = bytes_new[1]; bytes_old[2] = bytes_new[2]; @@ -332,9 +332,7 @@ mod tests { request_id: U32AsRef::from(request_id), channel_id, target: U256::from(target), - extranonce_prefix: B032::try_from(extranonce_prefix.to_vec()).expect( - "OpenStandardMiningChannelSuccess: failed to convert extranonce_prefix to B032", - ), + extranonce_prefix: B032::try_from(extranonce_prefix).unwrap(), group_channel_id, }; let test_request_id_1 = osmcs.get_request_id_as_u32(); From cf697b249c2f804d4c4b90fd476c95b8840b5725 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Jun 2026 20:16:36 +0530 Subject: [PATCH 12/20] Remove u32AsRef as its not part of specs and not really helpful Part of removal of U32AsRef: https://github.com/stratum-mining/stratum/issues/2215, we don't really need this. --- .../end_to_end_serialization_for_datatypes.rs | 4 +- sv2/binary-sv2/src/codec/decodable.rs | 10 +---- .../src/datatypes/non_copy_data_types/mod.rs | 44 +------------------ sv2/subprotocols/mining/src/open_channel.rs | 28 ++++-------- 4 files changed, 13 insertions(+), 73 deletions(-) diff --git a/fuzz/fuzz_targets/end_to_end_serialization_for_datatypes.rs b/fuzz/fuzz_targets/end_to_end_serialization_for_datatypes.rs index 8ce0aa8c77..53bb09afb9 100644 --- a/fuzz/fuzz_targets/end_to_end_serialization_for_datatypes.rs +++ b/fuzz/fuzz_targets/end_to_end_serialization_for_datatypes.rs @@ -5,7 +5,7 @@ mod common; use binary_sv2::{ Decodable, Encodable, GetSize, PubKey, Seq0255, Seq064K, Signature, Str0255, Sv2Option, - U32AsRef, B016M, B0255, B032, B064K, U24, U256, + B016M, B0255, B032, B064K, U24, U256, }; use libfuzzer_sys::fuzz_target; @@ -14,7 +14,6 @@ enum FuzzInput { PubKey(Vec), Signature(Vec), Str0255(Vec), - U32AsRef(Vec), B016M(Vec), B0255(Vec), B032(Vec), @@ -77,7 +76,6 @@ fuzz_target!(|input: FuzzInput| { FuzzInput::PubKey(data) => test_datatype_roundtrip!(PubKey, data), FuzzInput::Signature(data) => test_datatype_roundtrip!(Signature, data), FuzzInput::Str0255(data) => test_datatype_roundtrip!(Str0255, data), - FuzzInput::U32AsRef(data) => test_datatype_roundtrip!(U32AsRef, data), FuzzInput::B016M(data) => test_datatype_roundtrip!(B016M, data), FuzzInput::B0255(data) => test_datatype_roundtrip!(B0255, data), FuzzInput::B032(data) => test_datatype_roundtrip!(B032, data), diff --git a/sv2/binary-sv2/src/codec/decodable.rs b/sv2/binary-sv2/src/codec/decodable.rs index b96a63c6f9..46c32b8236 100644 --- a/sv2/binary-sv2/src/codec/decodable.rs +++ b/sv2/binary-sv2/src/codec/decodable.rs @@ -123,7 +123,6 @@ pub enum PrimitiveMarker { U256, Signature, U32, - U32AsRef, F32, U64, B032, @@ -166,7 +165,6 @@ pub enum DecodablePrimitive<'a> { U256(U256<'a>), Signature(Signature<'a>), U32(u32), - U32AsRef(U32AsRef<'a>), F32(f32), U64(u64), B032(B032<'a>), @@ -206,7 +204,6 @@ impl SizeHint for PrimitiveMarker { Self::U256 => U256::size_hint(data, offset), Self::Signature => Signature::size_hint(data, offset), Self::U32 => u32::size_hint(data, offset), - Self::U32AsRef => U32AsRef::size_hint(data, offset), Self::F32 => f32::size_hint(data, offset), Self::U64 => u64::size_hint(data, offset), Self::B032 => B032::size_hint(data, offset), @@ -315,7 +312,7 @@ impl PrimitiveMarker { Self::U32 => Ok(DecodablePrimitive::U32(u32::from_bytes_( &mut data[offset..], )?)), - Self::U32AsRef => Ok(DecodablePrimitive::U32AsRef(U32AsRef::from_bytes_( + Self::U32 => Ok(DecodablePrimitive::U32(u32::from_bytes_( &mut data[offset..], )?)), Self::F32 => Ok(DecodablePrimitive::F32(f32::from_bytes_( @@ -370,7 +367,6 @@ impl PrimitiveMarker { Self::U256 => decode_owned_inner!(U256, U256), Self::Signature => decode_owned_inner!(Signature, Signature), Self::U32 => decode_owned_copy!(u32, U32), - Self::U32AsRef => decode_owned_inner!(U32AsRef, U32AsRef), Self::F32 => decode_owned_copy!(f32, F32), Self::U64 => decode_owned_copy!(u64, U64), Self::B032 => decode_owned_inner!(B032, B032), @@ -450,9 +446,6 @@ impl PrimitiveMarker { reader, )?)), Self::U32 => Ok(DecodablePrimitive::U32(u32::from_reader_(reader)?)), - Self::U32AsRef => Ok(DecodablePrimitive::U32AsRef(U32AsRef::from_reader_( - reader, - )?)), Self::F32 => Ok(DecodablePrimitive::F32(f32::from_reader_(reader)?)), Self::U64 => Ok(DecodablePrimitive::U64(u64::from_reader_(reader)?)), Self::B032 => Ok(DecodablePrimitive::B032(B032::from_reader_(reader)?)), @@ -473,7 +466,6 @@ impl GetSize for DecodablePrimitive<'_> { DecodablePrimitive::U256(v) => v.get_size(), DecodablePrimitive::Signature(v) => v.get_size(), DecodablePrimitive::U32(v) => v.get_size(), - DecodablePrimitive::U32AsRef(v) => v.get_size(), DecodablePrimitive::F32(v) => v.get_size(), DecodablePrimitive::U64(v) => v.get_size(), DecodablePrimitive::B032(v) => v.get_size(), diff --git a/sv2/binary-sv2/src/datatypes/non_copy_data_types/mod.rs b/sv2/binary-sv2/src/datatypes/non_copy_data_types/mod.rs index feb2649a7e..ab46d4556b 100644 --- a/sv2/binary-sv2/src/datatypes/non_copy_data_types/mod.rs +++ b/sv2/binary-sv2/src/datatypes/non_copy_data_types/mod.rs @@ -7,15 +7,14 @@ // arrays (`B0255`, `B064K`). // # Features -// - **Fixed-size Aliases**: Types like [`U32AsRef`], [`U256`], [`PubKey`], and [`Signature`] -// represent specific byte sizes, often used in cryptographic contexts or protocol identifiers. +// - **Fixed-size Aliases**: Types like [`U256`], [`Mac`], [`PubKey`], and [`Signature`] represent +// specific byte sizes, often used in cryptographic contexts or protocol identifiers. // - **Variable-size Aliases**: Types like [`B032`], [`B0255`], [`Str0255`], [`B064K`], and // [`B016M`] handle data with bounded sizes, providing flexibility for dynamic data. // - **Traits and Conversions**: Implements traits like `From`, `TryFrom`, and `Clone` for // seamless transformations between owned and reference-based values. // # Type Aliases -// - **[`U32AsRef`]**: 4-byte representation for small identifiers or integer values. // - **[`U256`]**: 32-byte cryptographic hash (e.g., SHA-256 or protocol IDs). // - **[`PubKey`]**: 32-byte public key (e.g., Ed25519). // - **[`Signature`]**: 64-byte cryptographic signature. @@ -31,9 +30,6 @@ mod seq_inner; pub use inner::Inner; pub use seq_inner::{Seq0255, Seq064K, Sv2Option}; -/// Type alias for a 4-byte slice or owned data represented using the `Inner` -/// type with fixed-size configuration. -pub type U32AsRef<'a> = Inner<'a, true, 4, 0, 0>; /// Type alias for a 32-byte slice or owned data (commonly used for cryptographic /// hashes or IDs) represented using the `Inner` type with fixed-size configuration. pub type U256<'a> = Inner<'a, true, 32, 0, 0>; @@ -67,17 +63,6 @@ fn bytes_to_hex<'a>(bytes: impl IntoIterator) -> String { hex } -impl fmt::Display for U32AsRef<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let inner = self.as_bytes(); - write!( - f, - "U32AsRef({})", - u32::from_le_bytes([inner[0], inner[1], inner[2], inner[3]]) - ) - } -} - impl fmt::Display for Sv2Option<'_, u32> { // internally Sv2Option is pub struct Sv2Option<'a, T>(pub Vec, PhantomData<&'a T>); fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -281,28 +266,3 @@ impl TryFrom<&str> for Str0255<'_> { value.as_bytes().try_into() } } - -/// Represents a reference to a 32-bit unsigned integer (`u32`), -/// providing methods for convenient conversions. -impl U32AsRef<'_> { - /// Returns the `u32` value represented by this reference. - pub fn as_u32(&self) -> u32 { - let inner = self.as_bytes(); - u32::from_le_bytes([inner[0], inner[1], inner[2], inner[3]]) - } -} - -impl From for U32AsRef<'_> { - fn from(v: u32) -> Self { - let bytes = v.to_le_bytes(); - let inner = vec![bytes[0], bytes[1], bytes[2], bytes[3]]; - U32AsRef::Owned(inner) - } -} - -impl<'a> From<&'a U32AsRef<'a>> for u32 { - fn from(v: &'a U32AsRef<'a>) -> Self { - let b = v.as_bytes(); - u32::from_le_bytes([b[0], b[1], b[2], b[3]]) - } -} diff --git a/sv2/subprotocols/mining/src/open_channel.rs b/sv2/subprotocols/mining/src/open_channel.rs index 4481733537..ddc27f7bbe 100644 --- a/sv2/subprotocols/mining/src/open_channel.rs +++ b/sv2/subprotocols/mining/src/open_channel.rs @@ -1,5 +1,5 @@ use alloc::{string::ToString, vec::Vec}; -use binary_sv2::{Deserialize, Serialize, Str0255, U32AsRef, B032, U256}; +use binary_sv2::{Deserialize, Serialize, Str0255, B032, U256}; use core::{convert::TryInto, fmt}; /// Message used by a downstream to request opening a Standard Channel. /// @@ -13,7 +13,7 @@ pub struct OpenStandardMiningChannel<'decoder> { /// Used for matching responses from upstream. /// /// The value must be connection-wide unique and is not interpreted by the upstream. - pub request_id: U32AsRef<'decoder>, + pub request_id: u32, /// Unconstrained sequence of bytes. /// /// Whatever is needed by upstream role to identify/authenticate the downstream, e.g. @@ -51,16 +51,11 @@ impl fmt::Display for OpenStandardMiningChannel<'_> { impl OpenStandardMiningChannel<'_> { pub fn get_request_id_as_u32(&self) -> u32 { - (&self.request_id).into() + self.request_id } pub fn update_id(&mut self, new_id: u32) { - let bytes_new = new_id.to_le_bytes(); - let bytes_old = self.request_id.as_mut_bytes(); - bytes_old[0] = bytes_new[0]; - bytes_old[1] = bytes_new[1]; - bytes_old[2] = bytes_new[2]; - bytes_old[3] = bytes_new[3]; + self.request_id = new_id; } } @@ -72,7 +67,7 @@ pub struct OpenStandardMiningChannelSuccess<'decoder> { /// /// Specified by downstream role and should be extracted from the corresponding /// [`OpenStandardMiningChannel`] message. - pub request_id: U32AsRef<'decoder>, + pub request_id: u32, /// Newly assigned identifier of the channel, stable for the whole lifetime of the connection. /// /// This will also be used for broadcasting new jobs by [`crate::NewMiningJob`]. @@ -102,16 +97,11 @@ impl fmt::Display for OpenStandardMiningChannelSuccess<'_> { impl OpenStandardMiningChannelSuccess<'_> { pub fn get_request_id_as_u32(&self) -> u32 { - (&self.request_id).into() + self.request_id } pub fn update_id(&mut self, new_id: u32) { - let bytes_new = new_id.to_le_bytes(); - let bytes_old = self.request_id.as_mut_bytes(); - bytes_old[0] = bytes_new[0]; - bytes_old[1] = bytes_new[1]; - bytes_old[2] = bytes_new[2]; - bytes_old[3] = bytes_new[3]; + self.request_id = new_id; } } @@ -303,7 +293,7 @@ mod tests { ) -> bool { let max_target: [u8; 32] = from_arbitrary_vec_to_array(max_target); let mut osmc = OpenStandardMiningChannel { - request_id: U32AsRef::from(request_id), + request_id, user_identity: Str0255::try_from(user_identity.clone()) .expect("could not convert string to Str0255"), nominal_hash_rate, @@ -329,7 +319,7 @@ mod tests { let target = from_arbitrary_vec_to_array(target); let extranonce_prefix = from_arbitrary_vec_to_array(extranonce_prefix); let mut osmcs = OpenStandardMiningChannelSuccess { - request_id: U32AsRef::from(request_id), + request_id, channel_id, target: U256::from(target), extranonce_prefix: B032::try_from(extranonce_prefix).unwrap(), From 13a0f3f5b4f70003b505042e93bf56cc14ede672 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Jun 2026 20:18:43 +0530 Subject: [PATCH 13/20] remove unused error types Here, we remove error type variants across the crate which are not really needed. --- sv1/src/utils.rs | 16 +++++++++++++--- sv2/binary-sv2/src/lib.rs | 30 ------------------------------ 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/sv1/src/utils.rs b/sv1/src/utils.rs index 079a2c3399..134f4a57d4 100644 --- a/sv1/src/utils.rs +++ b/sv1/src/utils.rs @@ -226,9 +226,19 @@ impl<'a> TryFrom<&str> for PrevHash<'a> { } Ok(PrevHash(prev_hash_arr.into())) } - _ => Err(error::Error::BadBytesConvert( - binary_sv2::Error::InvalidU256(prev_hash_stratum_order.len()), - )), + _ => { + let len = prev_hash_stratum_order.len(); + Err(error::Error::BadBytesConvert( + binary_sv2::Error::ValueExceedsMaxSize( + true, + 32, + 0, + 0, + prev_hash_stratum_order, + len, + ), + )) + } } } } diff --git a/sv2/binary-sv2/src/lib.rs b/sv2/binary-sv2/src/lib.rs index 0b661be9ac..ae59f1b6b6 100644 --- a/sv2/binary-sv2/src/lib.rs +++ b/sv2/binary-sv2/src/lib.rs @@ -243,40 +243,13 @@ pub enum Error { /// Indicates an attempt to read beyond a valid range. OutOfBound, - /// Raised when a non-binary value is interpreted as a boolean. - NotABool(u8), - /// Occurs when an unexpected size mismatch arises during a write operation, specifying /// expected and actual sizes. WriteError(usize, usize), - /// Signifies an overflow condition where a `u32` exceeds the maximum allowable `u24` value. - U24TooBig(u32), - - /// Reports a size mismatch for a signature, such as when it does not match the expected size. - InvalidSignatureSize(usize), - - /// Raised when a `u256` value is invalid, typically due to size discrepancies. - InvalidU256(usize), - /// Indicates an invalid `u24` representation. InvalidU24(u32), - /// Error indicating that a byte array exceeds the maximum allowed size for `B0255`. - InvalidB0255Size(usize), - - /// Error indicating that a byte array exceeds the maximum allowed size for `B064K`. - InvalidB064KSize(usize), - - /// Error indicating that a byte array exceeds the maximum allowed size for `B016M`. - InvalidB016MSize(usize), - - /// Raised when a sequence size exceeds `0255`. - InvalidSeq0255Size(usize), - - /// Error when trying to encode a non-primitive data type. - NonPrimitiveTypeCannotBeEncoded, - /// Generic conversion error related to primitive types. PrimitiveConversionError, @@ -315,9 +288,6 @@ pub enum Error { /// Error for protocol-specific invalid values. ValueIsNotAValidProtocol(u8), - /// Raised when an unsupported or unknown message type is encountered. - UnknownMessageType(u8), - /// Indicates a protocol constraint violation where `Sv2Option` unexpectedly contains multiple /// elements. Sv2OptionHaveMoreThenOneElement(u8), From 75e08553db27c6c2b90a11987759d875504fa40a Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Jun 2026 20:24:32 +0530 Subject: [PATCH 14/20] Add MAC sv2 primitive Solves issue: https://github.com/stratum-mining/stratum/issues/2175, via this we are fully adhering to specs and supporting all primitives mentioned --- sv2/binary-sv2/src/codec/decodable.rs | 10 +++++++--- sv2/binary-sv2/src/codec/encodable.rs | 12 ++++++------ sv2/binary-sv2/src/codec/impls.rs | 12 ++++++------ sv2/binary-sv2/src/datatypes/mod.rs | 8 ++++---- .../src/datatypes/non_copy_data_types/mod.rs | 12 +++++++----- .../src/datatypes/non_copy_data_types/seq_inner.rs | 1 + sv2/binary-sv2/src/lib.rs | 7 ++++--- 7 files changed, 35 insertions(+), 27 deletions(-) diff --git a/sv2/binary-sv2/src/codec/decodable.rs b/sv2/binary-sv2/src/codec/decodable.rs index 46c32b8236..de1de5020d 100644 --- a/sv2/binary-sv2/src/codec/decodable.rs +++ b/sv2/binary-sv2/src/codec/decodable.rs @@ -1,6 +1,6 @@ use crate::{ codec::{GetSize, SizeHint}, - datatypes::{Signature, Sv2DataType, U32AsRef, B016M, B0255, B032, B064K, U24, U256}, + datatypes::{Mac, Signature, Sv2DataType, B016M, B0255, B032, B064K, U24, U256}, Error, }; use alloc::vec::Vec; @@ -121,6 +121,7 @@ pub enum PrimitiveMarker { Bool, U24, U256, + Mac, Signature, U32, F32, @@ -163,6 +164,7 @@ pub enum DecodablePrimitive<'a> { Bool(bool), U24(U24), U256(U256<'a>), + Mac(Mac<'a>), Signature(Signature<'a>), U32(u32), F32(f32), @@ -202,6 +204,7 @@ impl SizeHint for PrimitiveMarker { Self::Bool => bool::size_hint(data, offset), Self::U24 => U24::size_hint(data, offset), Self::U256 => U256::size_hint(data, offset), + Self::Mac => Mac::size_hint(data, offset), Self::Signature => Signature::size_hint(data, offset), Self::U32 => u32::size_hint(data, offset), Self::F32 => f32::size_hint(data, offset), @@ -306,10 +309,10 @@ impl PrimitiveMarker { Self::U256 => Ok(DecodablePrimitive::U256(U256::from_bytes_( &mut data[offset..], )?)), - Self::Signature => Ok(DecodablePrimitive::Signature(Signature::from_bytes_( + Self::Mac => Ok(DecodablePrimitive::Mac(Mac::from_bytes_( &mut data[offset..], )?)), - Self::U32 => Ok(DecodablePrimitive::U32(u32::from_bytes_( + Self::Signature => Ok(DecodablePrimitive::Signature(Signature::from_bytes_( &mut data[offset..], )?)), Self::U32 => Ok(DecodablePrimitive::U32(u32::from_bytes_( @@ -464,6 +467,7 @@ impl GetSize for DecodablePrimitive<'_> { DecodablePrimitive::Bool(v) => v.get_size(), DecodablePrimitive::U24(v) => v.get_size(), DecodablePrimitive::U256(v) => v.get_size(), + DecodablePrimitive::Mac(v) => v.get_size(), DecodablePrimitive::Signature(v) => v.get_size(), DecodablePrimitive::U32(v) => v.get_size(), DecodablePrimitive::F32(v) => v.get_size(), diff --git a/sv2/binary-sv2/src/codec/encodable.rs b/sv2/binary-sv2/src/codec/encodable.rs index eb2bc003af..54b59f638b 100644 --- a/sv2/binary-sv2/src/codec/encodable.rs +++ b/sv2/binary-sv2/src/codec/encodable.rs @@ -1,6 +1,6 @@ use crate::{ codec::GetSize, - datatypes::{Signature, Sv2DataType, U32AsRef, B016M, B0255, B032, B064K, U24, U256}, + datatypes::{Mac, Signature, Sv2DataType, B016M, B0255, B032, B064K, U24, U256}, Error, }; use alloc::vec::Vec; @@ -80,12 +80,12 @@ pub enum EncodablePrimitive<'a> { U24(U24), /// U256 Primitive, representing a U256 type U256(U256<'a>), + /// Mac Primitive, representing a MAC type + Mac(Mac<'a>), /// Signature Primitive, representing a Signature type Signature(Signature<'a>), /// U32 Primitive, representing a u32 type U32(u32), - /// U32AsRef Primitive, representing a U32AsRef type - U32AsRef(U32AsRef<'a>), /// F32 Primitive, representing a f32 type F32(f32), /// U64 Primitive, representing a u64 type @@ -114,9 +114,9 @@ impl EncodablePrimitive<'_> { Self::Bool(v) => v.to_slice(dst), Self::U24(v) => v.to_slice(dst), Self::U256(v) => v.to_slice(dst), + Self::Mac(v) => v.to_slice(dst), Self::Signature(v) => v.to_slice(dst), Self::U32(v) => v.to_slice(dst), - Self::U32AsRef(v) => v.to_slice(dst), Self::F32(v) => v.to_slice(dst), Self::U64(v) => v.to_slice(dst), Self::B032(v) => v.to_slice(dst), @@ -140,9 +140,9 @@ impl EncodablePrimitive<'_> { Self::Bool(v) => v.to_writer_(writer), Self::U24(v) => v.to_writer_(writer), Self::U256(v) => v.to_writer_(writer), + Self::Mac(v) => v.to_writer_(writer), Self::Signature(v) => v.to_writer_(writer), Self::U32(v) => v.to_writer_(writer), - Self::U32AsRef(v) => v.to_writer_(writer), Self::F32(v) => v.to_writer_(writer), Self::U64(v) => v.to_writer_(writer), Self::B032(v) => v.to_writer_(writer), @@ -163,9 +163,9 @@ impl GetSize for EncodablePrimitive<'_> { Self::Bool(v) => v.get_size(), Self::U24(v) => v.get_size(), Self::U256(v) => v.get_size(), + Self::Mac(v) => v.get_size(), Self::Signature(v) => v.get_size(), Self::U32(v) => v.get_size(), - Self::U32AsRef(v) => v.get_size(), Self::F32(v) => v.get_size(), Self::U64(v) => v.get_size(), Self::B032(v) => v.get_size(), diff --git a/sv2/binary-sv2/src/codec/impls.rs b/sv2/binary-sv2/src/codec/impls.rs index bf0bdad965..fca36478f7 100644 --- a/sv2/binary-sv2/src/codec/impls.rs +++ b/sv2/binary-sv2/src/codec/impls.rs @@ -120,12 +120,12 @@ impl_get_marker!( (f32, F32), (u64, U64), (U256<'_>, U256), + (Mac<'_>, Mac), (Signature<'_>, Signature), (B032<'_>, B032), (B0255<'_>, B0255), (B064K<'_>, B064K), (B016M<'_>, B016M), - (U32AsRef<'_>, U32AsRef), ); impl_decodable!( @@ -137,12 +137,12 @@ impl_decodable!( (bool, Bool), (U24, U24), (U256<'a>, U256), + (Mac<'a>, Mac), (Signature<'a>, Signature), (B032<'a>, B032), (B0255<'a>, B0255), (B064K<'a>, B064K), (B016M<'a>, B016M), - (U32AsRef<'a>, U32AsRef), ); impl_try_from_decodable_primitive!( @@ -154,12 +154,12 @@ impl_try_from_decodable_primitive!( (bool, Bool), (U24, U24), (U256<'a>, U256), + (Mac<'a>, Mac), (Signature<'a>, Signature), (B032<'a>, B032), (B0255<'a>, B0255), (B064K<'a>, B064K), (B016M<'a>, B016M), - (U32AsRef<'a>, U32AsRef), ); impl_try_from_decodable_field!( @@ -171,12 +171,12 @@ impl_try_from_decodable_field!( bool, U24, U256<'a>, + Mac<'a>, Signature<'a>, B032<'a>, B0255<'a>, B064K<'a>, B016M<'a>, - U32AsRef<'a>, ); impl_encodable_field_conversion!( @@ -188,12 +188,12 @@ impl_encodable_field_conversion!( (f32, F32), (u64, U64), (U256<'a>, U256), + (Mac<'a>, Mac), (Signature<'a>, Signature), (B032<'a>, B032), (B0255<'a>, B0255), (B064K<'a>, B064K), (B016M<'a>, B016M), - (U32AsRef<'a>, U32AsRef), ); impl_field_marker_from_owned!( @@ -207,11 +207,11 @@ impl_field_marker_from_owned!( ); impl_field_marker_from_borrowed!( + (Inner<'a, true, 16, 0, 0>, Mac), (Inner<'a, true, 32, 0, 0>, U256), (Inner<'a, true, 64, 0, 0>, Signature), (B032<'a>, B032), (Inner<'a, false, 1, 1, 255>, B0255), (Inner<'a, false, 1, 2, { 2_usize.pow(16) - 1 }>, B064K), (Inner<'a, false, 1, 3, { 2_usize.pow(24) - 1 }>, B016M), - (U32AsRef<'a>, U32AsRef), ); diff --git a/sv2/binary-sv2/src/datatypes/mod.rs b/sv2/binary-sv2/src/datatypes/mod.rs index 200ab20461..60ae1e4cfe 100644 --- a/sv2/binary-sv2/src/datatypes/mod.rs +++ b/sv2/binary-sv2/src/datatypes/mod.rs @@ -19,8 +19,8 @@ // strings, requiring size handling logic for SV2 compatibility. // // ### Re-exports -// Re-exports common data types used in SV2 serialization, such as `PubKey`, `Signature`, `Seq0255`, -// and others, simplifying protocol data handling with concrete implementations of `Sv2DataType`. +// Re-exports common data types used in SV2 serialization, such as `Signature`, `Seq0255`, and +// others, simplifying protocol data handling with concrete implementations of `Sv2DataType`. // // The `Sv2DataType` trait and its implementations enable seamless conversion between in-memory // representations and serialized forms, ensuring efficient protocol communication and @@ -36,8 +36,8 @@ mod copy_data_types; use crate::codec::decodable::FieldMarker; pub use copy_data_types::U24; pub use non_copy_data_types::{ - Inner, PubKey, Seq0255, Seq064K, Signature, Str0255, Sv2Option, U32AsRef, B016M, B0255, B032, - B064K, U256, + Inner, Mac, PubKey, Seq0255, Seq064K, Signature, Str0255, Sv2Option, B016M, B0255, B032, B064K, + U256, }; use alloc::vec::Vec; diff --git a/sv2/binary-sv2/src/datatypes/non_copy_data_types/mod.rs b/sv2/binary-sv2/src/datatypes/non_copy_data_types/mod.rs index ab46d4556b..9fcf8987bb 100644 --- a/sv2/binary-sv2/src/datatypes/non_copy_data_types/mod.rs +++ b/sv2/binary-sv2/src/datatypes/non_copy_data_types/mod.rs @@ -3,8 +3,8 @@ // // The core component is the [`Inner`] type, a wrapper for managing both fixed and variable-length // data slices or owned values. It offers aliases for commonly used data types like 32-byte hashes -// (`U256`), public keys (`PubKey`), cryptographic signatures (`Signature`), and dynamically-sized -// arrays (`B0255`, `B064K`). +// (`U256`), cryptographic signatures (`Signature`), and dynamically-sized arrays (`B0255`, +// `B064K`). // # Features // - **Fixed-size Aliases**: Types like [`U256`], [`Mac`], [`PubKey`], and [`Signature`] represent @@ -16,7 +16,8 @@ // # Type Aliases // - **[`U256`]**: 32-byte cryptographic hash (e.g., SHA-256 or protocol IDs). -// - **[`PubKey`]**: 32-byte public key (e.g., Ed25519). +// - **[`Mac`]**: 16-byte message authentication code. +// - **[`PubKey`]**: 32-byte Secp256k1 public key x-coordinate. // - **[`Signature`]**: 64-byte cryptographic signature. // - **[`B032`], [`B0255`], [`Str0255`]**: Variable-size representations for optional fields or // protocol data. @@ -33,8 +34,9 @@ pub use seq_inner::{Seq0255, Seq064K, Sv2Option}; /// Type alias for a 32-byte slice or owned data (commonly used for cryptographic /// hashes or IDs) represented using the `Inner` type with fixed-size configuration. pub type U256<'a> = Inner<'a, true, 32, 0, 0>; -/// Type alias for a 32-byte public key represented using the `Inner` type -/// with fixed-size configuration. +/// Type alias for a 16-byte message authentication code. +pub type Mac<'a> = Inner<'a, true, 16, 0, 0>; +/// Type alias for a 32-byte Secp256k1 public key x-coordinate. pub type PubKey<'a> = Inner<'a, true, 32, 0, 0>; /// Type alias for a 64-byte cryptographic signature represented using the /// `Inner` type with fixed-size configuration. diff --git a/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs b/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs index ebf1fa5835..1aa0455152 100644 --- a/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs +++ b/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs @@ -359,6 +359,7 @@ impl_into_encodable_field_for_seq!(U24); impl_into_encodable_field_for_seq!(u32); impl_into_encodable_field_for_seq!(u64); impl_into_encodable_field_for_seq!(U256<'a>); +impl_into_encodable_field_for_seq!(Mac<'a>); impl_into_encodable_field_for_seq!(Signature<'a>); impl_into_encodable_field_for_seq!(B0255<'a>); impl_into_encodable_field_for_seq!(B064K<'a>); diff --git a/sv2/binary-sv2/src/lib.rs b/sv2/binary-sv2/src/lib.rs index ae59f1b6b6..9a5d7bab44 100644 --- a/sv2/binary-sv2/src/lib.rs +++ b/sv2/binary-sv2/src/lib.rs @@ -20,13 +20,14 @@ //! u64 <-> U64 //! U256 <-> U256 //! Str0255 <-> STRO_255 +//! Mac <-> MAC +//! PubKey <-> PUBKEY //! Signature<-> SIGNATURE //! B032 <-> B0_32 //! B0255 <-> B0_255 //! B064K <-> B0_64K //! B016M <-> B0_16M //! [u8] <-> BYTES -//! Pubkey <-> PUBKEY //! Seq0255 <-> SEQ0_255[T] //! Seq064K <-> SEQ0_64K[T] //! ``` @@ -71,8 +72,8 @@ pub use encodable::Encodable as Serialize; mod codec; mod datatypes; pub use datatypes::{ - PubKey, Seq0255, Seq064K, Signature, Str0255, Sv2DataType, Sv2Option, U32AsRef, B016M, B0255, - B032, B064K, U24, U256, + Mac, PubKey, Seq0255, Seq064K, Signature, Str0255, Sv2DataType, Sv2Option, B016M, B0255, B032, + B064K, U24, U256, }; pub use crate::codec::{ From 09d67d2751fc4a272d0184326411f9d5b53c5959 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Jun 2026 20:54:52 +0530 Subject: [PATCH 15/20] Remove duplicate decode_owned --- sv2/binary-sv2/src/codec/decodable.rs | 40 --------------------------- 1 file changed, 40 deletions(-) diff --git a/sv2/binary-sv2/src/codec/decodable.rs b/sv2/binary-sv2/src/codec/decodable.rs index de1de5020d..c46d817172 100644 --- a/sv2/binary-sv2/src/codec/decodable.rs +++ b/sv2/binary-sv2/src/codec/decodable.rs @@ -339,46 +339,6 @@ impl PrimitiveMarker { } } - fn decode_owned( - &self, - data: &[u8], - offset: usize, - ) -> Result, Error> { - macro_rules! decode_owned_copy { - ($ty:ty, $variant:ident) => {{ - let mut owned = data[offset..].to_vec(); - Ok(DecodablePrimitive::$variant(<$ty>::from_bytes_( - &mut owned, - )?)) - }}; - } - - macro_rules! decode_owned_inner { - ($ty:ty, $variant:ident) => {{ - let mut owned = data[offset..].to_vec(); - Ok(DecodablePrimitive::$variant( - <$ty>::from_bytes_(&mut owned)?.into_static(), - )) - }}; - } - - match self { - Self::U8 => decode_owned_copy!(u8, U8), - Self::U16 => decode_owned_copy!(u16, U16), - Self::Bool => decode_owned_copy!(bool, Bool), - Self::U24 => decode_owned_copy!(U24, U24), - Self::U256 => decode_owned_inner!(U256, U256), - Self::Signature => decode_owned_inner!(Signature, Signature), - Self::U32 => decode_owned_copy!(u32, U32), - Self::F32 => decode_owned_copy!(f32, F32), - Self::U64 => decode_owned_copy!(u64, U64), - Self::B032 => decode_owned_inner!(B032, B032), - Self::B0255 => decode_owned_inner!(B0255, B0255), - Self::B064K => decode_owned_inner!(B064K, B064K), - Self::B016M => decode_owned_inner!(B016M, B016M), - } - } - fn decode_owned( &self, data: &[u8], From b4aebeb7c28c2c9289ac852c485cbd4552e6d09f Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Jun 2026 20:26:23 +0530 Subject: [PATCH 16/20] xRemove u8Owned Completes the issue: https://github.com/stratum-mining/stratum/issues/2215, removes u8Owned and uses u8 instead --- sv2/binary-sv2/src/codec/encodable.rs | 5 ----- .../src/datatypes/non_copy_data_types/seq_inner.rs | 12 ++++-------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/sv2/binary-sv2/src/codec/encodable.rs b/sv2/binary-sv2/src/codec/encodable.rs index 54b59f638b..7b695dcbc2 100644 --- a/sv2/binary-sv2/src/codec/encodable.rs +++ b/sv2/binary-sv2/src/codec/encodable.rs @@ -70,8 +70,6 @@ impl<'a, T: Into>> Encodable for T { pub enum EncodablePrimitive<'a> { /// U8 Primitive, representing a byte U8(u8), - /// Owned U8 Primitive, representing an owned byte - OwnedU8(u8), /// U16 Primitive, representing a u16 type U16(u16), /// Bool Primitive, representing a bool type @@ -109,7 +107,6 @@ impl EncodablePrimitive<'_> { fn encode(&self, dst: &mut [u8]) -> Result { match self { Self::U8(v) => v.to_slice(dst), - Self::OwnedU8(v) => v.to_slice(dst), Self::U16(v) => v.to_slice(dst), Self::Bool(v) => v.to_slice(dst), Self::U24(v) => v.to_slice(dst), @@ -135,7 +132,6 @@ impl EncodablePrimitive<'_> { pub fn write(&self, writer: &mut impl Write) -> Result<(), E> { match self { Self::U8(v) => v.to_writer_(writer), - Self::OwnedU8(v) => v.to_writer_(writer), Self::U16(v) => v.to_writer_(writer), Self::Bool(v) => v.to_writer_(writer), Self::U24(v) => v.to_writer_(writer), @@ -158,7 +154,6 @@ impl GetSize for EncodablePrimitive<'_> { fn get_size(&self) -> usize { match self { Self::U8(v) => v.get_size(), - Self::OwnedU8(v) => v.get_size(), Self::U16(v) => v.get_size(), Self::Bool(v) => v.get_size(), Self::U24(v) => v.get_size(), diff --git a/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs b/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs index 1aa0455152..2707c74b6f 100644 --- a/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs +++ b/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs @@ -307,10 +307,10 @@ macro_rules! impl_into_encodable_field_for_seq { let inner_len = v.0.len() as u16; let mut as_encodable: Vec = Vec::with_capacity(inner_len as usize + 2); - as_encodable.push(EncodableField::Primitive(EncodablePrimitive::OwnedU8( + as_encodable.push(EncodableField::Primitive(EncodablePrimitive::U8( inner_len.to_le_bytes()[0], ))); - as_encodable.push(EncodableField::Primitive(EncodablePrimitive::OwnedU8( + as_encodable.push(EncodableField::Primitive(EncodablePrimitive::U8( inner_len.to_le_bytes()[1], ))); for element in v.0 { @@ -325,9 +325,7 @@ macro_rules! impl_into_encodable_field_for_seq { let inner_len = v.0.len() as u8; let mut as_encodable: Vec = Vec::with_capacity((inner_len as usize) + 1); - as_encodable.push(EncodableField::Primitive(EncodablePrimitive::OwnedU8( - inner_len, - ))); + as_encodable.push(EncodableField::Primitive(EncodablePrimitive::U8(inner_len))); for element in v.0 { as_encodable.push(element.into()); } @@ -340,9 +338,7 @@ macro_rules! impl_into_encodable_field_for_seq { let inner_len = v.0.len() as u8; let mut as_encodable: Vec = Vec::with_capacity((inner_len as usize) + 1); - as_encodable.push(EncodableField::Primitive(EncodablePrimitive::OwnedU8( - inner_len, - ))); + as_encodable.push(EncodableField::Primitive(EncodablePrimitive::U8(inner_len))); for element in v.0 { as_encodable.push(element.into()); } From c2cf01bb05852fa52462dfd42c2dd674836c6c6c Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Jun 2026 20:33:18 +0530 Subject: [PATCH 17/20] Remove u256_from_int and commented import and make sure not to vec iter instead use copied for from impl --- sv2/binary-sv2/src/lib.rs | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/sv2/binary-sv2/src/lib.rs b/sv2/binary-sv2/src/lib.rs index 9a5d7bab44..1a84a67ae6 100644 --- a/sv2/binary-sv2/src/lib.rs +++ b/sv2/binary-sv2/src/lib.rs @@ -170,7 +170,6 @@ pub fn from_bytes_owned>(data: Vec) -> Result> for EncodableField<'_> { #[cfg(feature = "with_buffer_pool")] impl From for EncodableField<'static> { fn from(v: buffer_sv2::Slice) -> Self { - EncodableField::Struct(v.as_ref().to_vec().into_iter().map(Into::into).collect()) + EncodableField::Struct(v.as_ref().iter().copied().map(Into::into).collect()) } } - -/// Converts a value implementing the `Into` trait into a custom `U256` type. -pub fn u256_from_int>(value: V) -> U256<'static> { - // initialize u256 as a bytes vec of len 24 - let mut u256 = vec![0_u8; 24]; - let val: u64 = value.into(); - for v in &(val.to_le_bytes()) { - // add 8 bytes to u256 - u256.push(*v) - } - // Always safe cause u256 is 24 + 8 (32) bytes - let u256: U256 = u256.try_into().unwrap(); - u256 -} From c9febd4aa112651810413bf712fea6c924b2f798 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Jun 2026 20:38:00 +0530 Subject: [PATCH 18/20] Remove into_owned Removes into_owned as internally it calls into_static and no point having this if it just calls into_static --- .../datatypes/non_copy_data_types/inner.rs | 5 --- .../non_copy_data_types/seq_inner.rs | 36 ------------------- 2 files changed, 41 deletions(-) diff --git a/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs b/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs index 0bc4e74d24..fa588dfc83 100644 --- a/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs +++ b/sv2/binary-sv2/src/datatypes/non_copy_data_types/inner.rs @@ -399,11 +399,6 @@ impl Inner::Owned(data), } } - - /// Alias for [`Inner::into_static`]. - pub fn into_owned(self) -> Inner<'static, ISFIXED, SIZE, HEADERSIZE, MAXSIZE> { - self.into_static() - } } impl Inner<'_, true, SIZE, 0, 0> { diff --git a/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs b/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs index 2707c74b6f..d6b4f142c0 100644 --- a/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs +++ b/sv2/binary-sv2/src/datatypes/non_copy_data_types/seq_inner.rs @@ -381,22 +381,12 @@ impl Seq0255<'_, T> { // Safe unwrap cause the initial value is a valid Seq0255 Seq0255::new(self.0).unwrap() } - - /// Alias for [`Seq0255::into_static`]. - pub fn into_owned(self) -> Seq0255<'static, T> { - self.into_static() - } } impl Sv2Option<'_, T> { /// converts the lifetime to static pub fn into_static(self) -> Sv2Option<'static, T> { Sv2Option::new(self.into_inner()) } - - /// Alias for [`Sv2Option::into_static`]. - pub fn into_owned(self) -> Sv2Option<'static, T> { - self.into_static() - } } impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> @@ -411,13 +401,6 @@ impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const // Safe unwrap cause the initial value is a valid Seq0255 Seq0255::new(static_seq).unwrap() } - - /// Alias for [`Seq0255::into_static`]. - pub fn into_owned( - self, - ) -> Seq0255<'static, Inner<'static, ISFIXED, SIZE, HEADERSIZE, MAXSIZE>> { - self.into_static() - } } impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> @@ -431,13 +414,6 @@ impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const let static_inner = inner.map(|x| x.into_static()); Sv2Option::new(static_inner) } - - /// Alias for [`Sv2Option::into_static`]. - pub fn into_owned( - self, - ) -> Sv2Option<'static, Inner<'static, ISFIXED, SIZE, HEADERSIZE, MAXSIZE>> { - self.into_static() - } } impl Seq064K<'_, T> { @@ -446,11 +422,6 @@ impl Seq064K<'_, T> { // Safe unwrap cause the initial value is a valid Seq064K Seq064K::new(self.0).unwrap() } - - /// Alias for [`Seq064K::into_static`]. - pub fn into_owned(self) -> Seq064K<'static, T> { - self.into_static() - } } impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> @@ -465,13 +436,6 @@ impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const // Safe unwrap cause the initial value is a valid Seq064K Seq064K::new(static_seq).unwrap() } - - /// Alias for [`Seq064K::into_static`]. - pub fn into_owned( - self, - ) -> Seq064K<'static, Inner<'static, ISFIXED, SIZE, HEADERSIZE, MAXSIZE>> { - self.into_static() - } } /// The lifetime 'a is defined. From 4cd8ecd9cad0664624a2eedbcd7e162402ccea6f Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Jun 2026 20:38:31 +0530 Subject: [PATCH 19/20] Make sure writer doesn't end up in recursion loop Previously, the writer method would lead to unbounded recursion, this commit solves via directly calling encodable field writer method --- sv2/binary-sv2/src/codec/encodable.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sv2/binary-sv2/src/codec/encodable.rs b/sv2/binary-sv2/src/codec/encodable.rs index 7b695dcbc2..c4b376c856 100644 --- a/sv2/binary-sv2/src/codec/encodable.rs +++ b/sv2/binary-sv2/src/codec/encodable.rs @@ -54,10 +54,10 @@ impl<'a, T: Into>> Encodable for T { } #[cfg(not(feature = "no_std"))] - #[allow(clippy::wrong_self_convention, unconditional_recursion)] + #[allow(clippy::wrong_self_convention)] fn to_writer(self, dst: &mut impl Write) -> Result<(), E> { let encoded_field = self.into(); - encoded_field.to_writer(dst) + EncodableField::to_writer(&encoded_field, dst) } } From fe3c97314d0ac528bdc096e7bc81b754c148bc4b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Jun 2026 20:41:24 +0530 Subject: [PATCH 20/20] Remove redundant imports --- sv2/binary-sv2/examples/encode_decode.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sv2/binary-sv2/examples/encode_decode.rs b/sv2/binary-sv2/examples/encode_decode.rs index 8842a5cfd8..66169aceb4 100644 --- a/sv2/binary-sv2/examples/encode_decode.rs +++ b/sv2/binary-sv2/examples/encode_decode.rs @@ -1,7 +1,5 @@ -use binary_sv2::{from_bytes, to_bytes, U24}; -pub use binary_sv2::{Decodable as Deserialize, Encodable as Serialize}; +use binary_sv2::{from_bytes, to_bytes, Deserialize, Serialize, U24}; use core::convert::TryInto; -pub use derive_codec_sv2::{Decodable as Deserialize, Encodable as Serialize}; // The `Test` struct is expanded using the `Deserialize` and `Serialize` procedural macros. // These macros provide the necessary methods for serializing and deserializing the struct.