Add borsh impls for SignedEmbeddedEllipticCurveKeys

This commit is contained in:
Luke Parker
2025-09-05 07:21:07 -04:00
parent 28dbef8a1c
commit 2077e485bb
4 changed files with 101 additions and 78 deletions

View File

@@ -252,35 +252,7 @@ mod substrate {
} }
impl Decode for Transaction { impl Decode for Transaction {
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> { fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
struct ScaleRead<'a, I: scale::Input>(&'a mut I, Option<scale::Error>); serai_primitives::read_scale_as_borsh(input)
impl<I: scale::Input> borsh::io::Read for ScaleRead<'_, I> {
fn read(&mut self, buf: &mut [u8]) -> borsh::io::Result<usize> {
let remaining_len = self.0.remaining_len().map_err(|err| {
self.1 = Some(err);
#[allow(clippy::io_other_error)]
borsh::io::Error::new(borsh::io::ErrorKind::Other, "")
})?;
// If we're still calling `read`, we try to read at least one more byte
let to_read = buf.len().min(remaining_len.unwrap_or(1));
// This may not be _allocated_ making this over-zealous, but it's the best we can do
self.0.on_before_alloc_mem(to_read).map_err(|err| {
self.1 = Some(err);
#[allow(clippy::io_other_error)]
borsh::io::Error::new(borsh::io::ErrorKind::Other, "")
})?;
self.0.read(&mut buf[.. to_read]).map_err(|err| {
self.1 = Some(err);
#[allow(clippy::io_other_error)]
borsh::io::Error::new(borsh::io::ErrorKind::Other, "")
})?;
Ok(to_read)
}
}
let mut input = ScaleRead(input, None);
match Self::deserialize_reader(&mut input) {
Ok(res) => Ok(res),
Err(_) => Err(input.1.unwrap()),
}
} }
} }

View File

@@ -1,9 +1,7 @@
use borsh::{BorshSerialize, BorshDeserialize}; use borsh::{BorshSerialize, BorshDeserialize};
use sp_core::{ConstU32, bounded::BoundedVec};
use serai_primitives::{ use serai_primitives::{
crypto::{ExternalKey, EmbeddedEllipticCurveKeys, KeyPair, Signature}, crypto::{SignedEmbeddedEllipticCurveKeys, KeyPair, Signature},
address::SeraiAddress, address::SeraiAddress,
balance::Amount, balance::Amount,
network_id::*, network_id::*,
@@ -40,14 +38,8 @@ pub enum Call {
}, },
/// Set a validator's keys on embedded elliptic curves for a specific network. /// Set a validator's keys on embedded elliptic curves for a specific network.
set_embedded_elliptic_curve_keys { set_embedded_elliptic_curve_keys {
/// The network the origin is setting their embedded elliptic curve keys for.
network: ExternalNetworkId,
/// The keys on the embedded elliptic curves. /// The keys on the embedded elliptic curves.
#[borsh( keys: SignedEmbeddedEllipticCurveKeys,
serialize_with = "serai_primitives::sp_borsh::borsh_serialize_bounded_vec",
deserialize_with = "serai_primitives::sp_borsh::borsh_deserialize_bounded_vec"
)]
keys: EmbeddedEllipticCurveKeys,
}, },
/// Allocate stake to a network. /// Allocate stake to a network.
allocate { allocate {

View File

@@ -1,5 +1,5 @@
use zeroize::Zeroize; use zeroize::Zeroize;
use borsh::{BorshSerialize, BorshDeserialize}; use borsh::{io, BorshSerialize, BorshDeserialize};
use sp_core::{ConstU32, bounded::BoundedVec}; use sp_core::{ConstU32, bounded::BoundedVec};
@@ -137,30 +137,54 @@ impl EmbeddedEllipticCurveKeys {
} }
} }
#[cfg(feature = "non_canonical_scale_derivations")] impl BorshSerialize for EmbeddedEllipticCurveKeys {
impl scale::Encode for EmbeddedEllipticCurveKeys { fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
match self { match self {
EmbeddedEllipticCurveKeys::Bitcoin(e, s) | EmbeddedEllipticCurveKeys::Ethereum(e, s) => { EmbeddedEllipticCurveKeys::Bitcoin(e, s) | EmbeddedEllipticCurveKeys::Ethereum(e, s) => {
let mut res = [0; 66]; let mut res = [0; 1 + 32 + 33];
res[0] = self.network() as u8; res[0] = self.network() as u8;
res[1 .. 33].copy_from_slice(e); res[1 .. 33].copy_from_slice(e);
res[33 ..].copy_from_slice(s); res[33 ..].copy_from_slice(s);
f(&res) writer.write_all(&res)
} }
EmbeddedEllipticCurveKeys::Monero(e) => { EmbeddedEllipticCurveKeys::Monero(e) => {
let mut res = [0; 33]; let mut res = [0; 1 + 32];
res[0] = self.network() as u8; res[0] = self.network() as u8;
res[1 ..].copy_from_slice(e); res[1 ..].copy_from_slice(e);
f(&res) writer.write_all(&res)
} }
} }
} }
} }
impl BorshDeserialize for EmbeddedEllipticCurveKeys {
fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
let network_id = ExternalNetworkId::deserialize_reader(&mut *reader)?;
let embedwards25519 = <[u8; 32]>::deserialize_reader(&mut *reader)?;
Ok(match network_id {
ExternalNetworkId::Bitcoin => {
let secq256k1 = <[u8; 33]>::deserialize_reader(&mut *reader)?;
EmbeddedEllipticCurveKeys::Bitcoin(embedwards25519, secq256k1.into())
}
ExternalNetworkId::Ethereum => {
let secq256k1 = <[u8; 33]>::deserialize_reader(&mut *reader)?;
EmbeddedEllipticCurveKeys::Ethereum(embedwards25519, secq256k1.into())
}
ExternalNetworkId::Monero => EmbeddedEllipticCurveKeys::Monero(embedwards25519),
})
}
}
#[cfg(feature = "non_canonical_scale_derivations")]
impl scale::Encode for EmbeddedEllipticCurveKeys {
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
f(&borsh::to_vec(self).unwrap())
}
}
#[cfg(feature = "non_canonical_scale_derivations")] #[cfg(feature = "non_canonical_scale_derivations")]
impl scale::MaxEncodedLen for EmbeddedEllipticCurveKeys { impl scale::MaxEncodedLen for EmbeddedEllipticCurveKeys {
fn max_encoded_len() -> usize { fn max_encoded_len() -> usize {
66 1 + 32 + 33
} }
} }
#[cfg(feature = "non_canonical_scale_derivations")] #[cfg(feature = "non_canonical_scale_derivations")]
@@ -168,20 +192,7 @@ impl scale::EncodeLike<EmbeddedEllipticCurveKeys> for EmbeddedEllipticCurveKeys
#[cfg(feature = "non_canonical_scale_derivations")] #[cfg(feature = "non_canonical_scale_derivations")]
impl scale::Decode for EmbeddedEllipticCurveKeys { impl scale::Decode for EmbeddedEllipticCurveKeys {
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> { fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
let network_id = ExternalNetworkId::decode(&mut *input)?; crate::read_scale_as_borsh(input)
let embedwards25519 =
<<Embedwards25519 as WrappedGroup>::G as GroupEncoding>::Repr::decode(&mut *input)?;
Ok(match network_id {
ExternalNetworkId::Bitcoin => {
let secq256k1 = <[u8; 33]>::decode(&mut *input)?;
EmbeddedEllipticCurveKeys::Bitcoin(embedwards25519, secq256k1.into())
}
ExternalNetworkId::Ethereum => {
let secq256k1 = <[u8; 33]>::decode(&mut *input)?;
EmbeddedEllipticCurveKeys::Ethereum(embedwards25519, secq256k1.into())
}
ExternalNetworkId::Monero => EmbeddedEllipticCurveKeys::Monero(embedwards25519),
})
} }
} }
#[cfg(feature = "non_canonical_scale_derivations")] #[cfg(feature = "non_canonical_scale_derivations")]
@@ -284,40 +295,37 @@ impl SignedEmbeddedEllipticCurveKeys {
} }
} }
#[cfg(feature = "non_canonical_scale_derivations")] impl BorshSerialize for SignedEmbeddedEllipticCurveKeys {
impl scale::Encode for SignedEmbeddedEllipticCurveKeys { fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
match self { match self {
SignedEmbeddedEllipticCurveKeys::Bitcoin(e, s, e_sig, s_sig) | SignedEmbeddedEllipticCurveKeys::Bitcoin(e, s, e_sig, s_sig) |
SignedEmbeddedEllipticCurveKeys::Ethereum(e, s, e_sig, s_sig) => { SignedEmbeddedEllipticCurveKeys::Ethereum(e, s, e_sig, s_sig) => {
let mut res = [0; 195]; let mut res = [0; 1 + 32 + 33 + 32 + 32 + 33 + 32];
res[0] = self.network() as u8; res[0] = self.network() as u8;
res[1 .. 33].copy_from_slice(e); res[1 .. 33].copy_from_slice(e);
res[33 .. 66].copy_from_slice(s); res[33 .. 66].copy_from_slice(s);
res[66 .. 130].copy_from_slice(e_sig); res[66 .. 130].copy_from_slice(e_sig);
res[130 ..].copy_from_slice(s_sig); res[130 ..].copy_from_slice(s_sig);
f(&res) writer.write_all(&res)
} }
SignedEmbeddedEllipticCurveKeys::Monero(e, e_sig) => { SignedEmbeddedEllipticCurveKeys::Monero(e, e_sig) => {
let mut res = [0; 97]; let mut res = [0; 1 + 32 + 32 + 32];
res[0] = self.network() as u8; res[0] = self.network() as u8;
res[1 .. 33].copy_from_slice(e); res[1 .. 33].copy_from_slice(e);
res[33 ..].copy_from_slice(e_sig); res[33 ..].copy_from_slice(e_sig);
f(&res) writer.write_all(&res)
} }
} }
} }
} }
#[cfg(feature = "non_canonical_scale_derivations")]
impl scale::EncodeLike<SignedEmbeddedEllipticCurveKeys> for SignedEmbeddedEllipticCurveKeys {} impl BorshDeserialize for SignedEmbeddedEllipticCurveKeys {
#[cfg(feature = "non_canonical_scale_derivations")] fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
impl scale::Decode for SignedEmbeddedEllipticCurveKeys { let embedded_elliptic_curve_keys = EmbeddedEllipticCurveKeys::deserialize_reader(&mut *reader)?;
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> { let embedwards25519_signature = <[u8; 64]>::deserialize_reader(&mut *reader)?;
let embedded_elliptic_curve_keys = EmbeddedEllipticCurveKeys::decode(input)?;
let embedwards25519_signature = <[u8; 64]>::decode(&mut *input)?;
Ok(match embedded_elliptic_curve_keys { Ok(match embedded_elliptic_curve_keys {
EmbeddedEllipticCurveKeys::Bitcoin(e, s) => { EmbeddedEllipticCurveKeys::Bitcoin(e, s) => {
let secq256k1_signature = <[u8; 65]>::decode(&mut *input)?; let secq256k1_signature = <[u8; 65]>::deserialize_reader(&mut *reader)?;
SignedEmbeddedEllipticCurveKeys::Bitcoin( SignedEmbeddedEllipticCurveKeys::Bitcoin(
e, e,
s, s,
@@ -326,7 +334,7 @@ impl scale::Decode for SignedEmbeddedEllipticCurveKeys {
) )
} }
EmbeddedEllipticCurveKeys::Ethereum(e, s) => { EmbeddedEllipticCurveKeys::Ethereum(e, s) => {
let secq256k1_signature = <[u8; 65]>::decode(&mut *input)?; let secq256k1_signature = <[u8; 65]>::deserialize_reader(&mut *reader)?;
SignedEmbeddedEllipticCurveKeys::Ethereum( SignedEmbeddedEllipticCurveKeys::Ethereum(
e, e,
s, s,
@@ -340,6 +348,21 @@ impl scale::Decode for SignedEmbeddedEllipticCurveKeys {
}) })
} }
} }
#[cfg(feature = "non_canonical_scale_derivations")]
impl scale::Encode for SignedEmbeddedEllipticCurveKeys {
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
f(&borsh::to_vec(self).unwrap())
}
}
#[cfg(feature = "non_canonical_scale_derivations")]
impl scale::EncodeLike<SignedEmbeddedEllipticCurveKeys> for SignedEmbeddedEllipticCurveKeys {}
#[cfg(feature = "non_canonical_scale_derivations")]
impl scale::Decode for SignedEmbeddedEllipticCurveKeys {
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
crate::read_scale_as_borsh(input)
}
}
#[cfg(feature = "non_canonical_scale_derivations")] #[cfg(feature = "non_canonical_scale_derivations")]
impl scale::DecodeWithMemTracking for SignedEmbeddedEllipticCurveKeys {} impl scale::DecodeWithMemTracking for SignedEmbeddedEllipticCurveKeys {}

View File

@@ -102,3 +102,39 @@ pub mod prelude {
pub use crate::validator_sets::{Session, ValidatorSet, ExternalValidatorSet, Slash, SlashReport}; pub use crate::validator_sets::{Session, ValidatorSet, ExternalValidatorSet, Slash, SlashReport};
pub use crate::instructions::*; pub use crate::instructions::*;
} }
#[doc(hidden)]
#[cfg(feature = "non_canonical_scale_derivations")]
pub fn read_scale_as_borsh<T: borsh::BorshDeserialize, I: scale::Input>(
input: &mut I,
) -> Result<T, scale::Error> {
struct ScaleRead<'a, I: scale::Input>(&'a mut I, Option<scale::Error>);
impl<I: scale::Input> borsh::io::Read for ScaleRead<'_, I> {
fn read(&mut self, buf: &mut [u8]) -> borsh::io::Result<usize> {
let remaining_len = self.0.remaining_len().map_err(|err| {
self.1 = Some(err);
#[allow(clippy::io_other_error)]
borsh::io::Error::new(borsh::io::ErrorKind::Other, "")
})?;
// If we're still calling `read`, we try to read at least one more byte
let to_read = buf.len().min(remaining_len.unwrap_or(1));
// This may not be _allocated_ making this over-zealous, but it's the best we can do
self.0.on_before_alloc_mem(to_read).map_err(|err| {
self.1 = Some(err);
#[allow(clippy::io_other_error)]
borsh::io::Error::new(borsh::io::ErrorKind::Other, "")
})?;
self.0.read(&mut buf[.. to_read]).map_err(|err| {
self.1 = Some(err);
#[allow(clippy::io_other_error)]
borsh::io::Error::new(borsh::io::ErrorKind::Other, "")
})?;
Ok(to_read)
}
}
let mut input = ScaleRead(input, None);
match T::deserialize_reader(&mut input) {
Ok(res) => Ok(res),
Err(_) => Err(input.1.unwrap()),
}
}