Files
serai/substrate/primitives/src/account.rs
Luke Parker a141deaf36 Smash the singular Ciphersuite trait into multiple
This helps identify where the various functionalities are used, or rather, not
used. The `Ciphersuite` trait present in `patches/ciphersuite`, facilitating
the entire FCMP++ tree, only requires the markers _and_ canonical point
decoding. I've opened a PR to upstream such a trait into `group`
(https://github.com/zkcrypto/group/pull/68).

`WrappedGroup` is still justified for as long as `Group::generator` exists.
Moving `::generator()` to its own trait, on an independent structure (upstream)
would be massively appreciated. @tarcieri also wanted to update from
`fn generator()` to `const GENERATOR`, which would encourage further discussion
on https://github.com/zkcrypto/group/issues/32 and
https://github.com/zkcrypto/group/issues/45, which have been stagnant.

The `Id` trait is occasionally used yet really should be first off the chopping
block.

Finally, `WithPreferredHash` is only actually used around a third of the time,
which more than justifies it being a separate trait.

---

Updates `dalek_ff_group::Scalar` to directly re-export
`curve25519_dalek::Scalar`, as without issue. `dalek_ff_group::RistrettoPoint`
also could be replaced with an export of `curve25519_dalek::RistrettoPoint`,
yet the coordinator relies on how we implemented `Hash` on it for the hell of
it so it isn't worth it at this time. `dalek_ff_group::EdwardsPoint` can't be
replaced for an re-export of `curve25519_dalek::SubgroupPoint` as it doesn't
implement `zeroize`, `subtle` traits within a released, non-yanked version.
Relevance to https://github.com/serai-dex/serai/issues/201 and
https://github.com/dalek-cryptography/curve25519-dalek/issues/811#issuecomment-3247732746.

Also updates the `Ristretto` ciphersuite to prefer `Blake2b-512` over
`SHA2-512`. In order to maintain compliance with FROST's IETF standard,
`modular-frost` defines its own ciphersuite for Ristretto which still uses
`SHA2-512`.
2025-09-03 13:50:20 -04:00

145 lines
3.9 KiB
Rust

#[cfg(feature = "std")]
use zeroize::Zeroize;
#[cfg(feature = "borsh")]
use borsh::{BorshSerialize, BorshDeserialize};
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};
use scale::{Encode, Decode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_core::sr25519::Public;
pub use sp_core::sr25519::Signature;
#[cfg(feature = "std")]
use sp_core::{Pair as PairTrait, sr25519::Pair};
use sp_runtime::traits::{LookupError, Lookup, StaticLookup};
pub type PublicKey = Public;
#[cfg(feature = "borsh")]
pub fn borsh_serialize_public<W: borsh::io::Write>(
public: &Public,
writer: &mut W,
) -> Result<(), borsh::io::Error> {
borsh::BorshSerialize::serialize(&public.0, writer)
}
#[cfg(feature = "borsh")]
pub fn borsh_deserialize_public<R: borsh::io::Read>(
reader: &mut R,
) -> Result<Public, borsh::io::Error> {
let public: [u8; 32] = borsh::BorshDeserialize::deserialize_reader(reader)?;
Ok(Public(public))
}
#[cfg(feature = "borsh")]
pub fn borsh_serialize_signature<W: borsh::io::Write>(
signature: &Signature,
writer: &mut W,
) -> Result<(), borsh::io::Error> {
borsh::BorshSerialize::serialize(&signature.0, writer)
}
#[cfg(feature = "borsh")]
pub fn borsh_deserialize_signature<R: borsh::io::Read>(
reader: &mut R,
) -> Result<Signature, borsh::io::Error> {
let signature: [u8; 64] = borsh::BorshDeserialize::deserialize_reader(reader)?;
Ok(Signature(signature))
}
// TODO: Remove this for solely Public?
#[derive(
Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encode, Decode, MaxEncodedLen, TypeInfo,
)]
#[cfg_attr(feature = "std", derive(Zeroize))]
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SeraiAddress(pub [u8; 32]);
impl SeraiAddress {
pub fn new(key: [u8; 32]) -> SeraiAddress {
SeraiAddress(key)
}
}
impl From<[u8; 32]> for SeraiAddress {
fn from(key: [u8; 32]) -> SeraiAddress {
SeraiAddress(key)
}
}
impl From<PublicKey> for SeraiAddress {
fn from(key: PublicKey) -> SeraiAddress {
SeraiAddress(key.0)
}
}
impl From<SeraiAddress> for PublicKey {
fn from(address: SeraiAddress) -> PublicKey {
PublicKey::from_raw(address.0)
}
}
#[cfg(feature = "std")]
impl std::fmt::Display for SeraiAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// TODO: Bech32
write!(f, "{:?}", self.0)
}
}
/// Create a Substraate key pair by a name.
///
/// This should never be considered to have a secure private key. It has effectively no entropy.
#[cfg(feature = "std")]
pub fn insecure_pair_from_name(name: &str) -> Pair {
Pair::from_string(&format!("//{name}"), None).unwrap()
}
/// Create a private key for an arbitrary ciphersuite by a name.
///
/// This key should never be considered a secure private key. It has effectively no entropy.
#[cfg(feature = "std")]
pub fn insecure_arbitrary_key_from_name<C: ciphersuite::WithPreferredHash>(name: &str) -> C::F {
C::hash_to_F(name.as_bytes())
}
pub struct AccountLookup;
impl Lookup for AccountLookup {
type Source = SeraiAddress;
type Target = PublicKey;
fn lookup(&self, source: SeraiAddress) -> Result<PublicKey, LookupError> {
Ok(PublicKey::from_raw(source.0))
}
}
impl StaticLookup for AccountLookup {
type Source = SeraiAddress;
type Target = PublicKey;
fn lookup(source: SeraiAddress) -> Result<PublicKey, LookupError> {
Ok(source.into())
}
fn unlookup(source: PublicKey) -> SeraiAddress {
source.into()
}
}
pub const fn system_address(pallet: &'static [u8]) -> SeraiAddress {
let mut address = [0; 32];
let mut set = false;
// Implement a while loop since we can't use a for loop
let mut i = 0;
while i < pallet.len() {
address[i] = pallet[i];
if address[i] != 0 {
set = true;
}
i += 1;
}
// Make sure this address isn't the identity point
// Doesn't do address != [0; 32] since that's not const
assert!(set, "address is the identity point");
SeraiAddress(address)
}