mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 14:01:06 +00:00
More secure Signed implementation (#2963)
* Remove signature verification in backing. `SignedFullStatement` now signals that the signature has already been checked. * Remove unused check_payload function. * Introduced unchecked signed variants. * Fix inclusion to use unchecked variant. * More unchecked variants. * Use unchecked variants in protocols. * Start fixing statement-distribution. * Fixup statement distribution. * Fix inclusion. * Fix warning. * Fix backing properly. * Fix bitfield distribution. * Make crypto store optional for `RuntimeInfo`. * Factor out utility functions. * get_group_rotation_info * WIP: Collator cleanup + check signatures. * Convenience signature checking functions. * Check signature on collator-side. * Fix warnings. * Fix collator side tests. * Get rid of warnings. * Better Signed/UncheckedSigned implementation. Also get rid of Encode/Decode for Signed! *party* * Get rid of dead code. * Move Signed in its own module. * into_checked -> try_into_checked * Fix merge.
This commit is contained in:
@@ -18,8 +18,6 @@
|
||||
//! perspective.
|
||||
|
||||
use sp_std::prelude::*;
|
||||
#[cfg(feature = "std")]
|
||||
use sp_std::convert::TryInto;
|
||||
use sp_std::cmp::Ordering;
|
||||
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
@@ -29,13 +27,9 @@ use serde::{Serialize, Deserialize};
|
||||
#[cfg(feature = "std")]
|
||||
use parity_util_mem::{MallocSizeOf, MallocSizeOfOps};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use sp_keystore::{CryptoStore, SyncCryptoStorePtr, Error as KeystoreError};
|
||||
use primitives::RuntimeDebug;
|
||||
use runtime_primitives::traits::{AppVerify, Block as BlockT};
|
||||
use inherents::InherentIdentifier;
|
||||
#[cfg(feature = "std")]
|
||||
use application_crypto::AppKey;
|
||||
use application_crypto::KeyTypeId;
|
||||
|
||||
pub use runtime_primitives::traits::{BlakeTwo256, Hash as HashT, Verify, IdentifyAccount};
|
||||
@@ -731,9 +725,6 @@ impl CompactStatement {
|
||||
}
|
||||
}
|
||||
|
||||
/// A signed compact statement, suitable to be sent to the chain.
|
||||
pub type SignedStatement = Signed<CompactStatement>;
|
||||
|
||||
/// An either implicit or explicit attestation to the validity of a parachain
|
||||
/// candidate.
|
||||
#[derive(Clone, Eq, PartialEq, Decode, Encode, RuntimeDebug)]
|
||||
@@ -866,156 +857,6 @@ pub mod id {
|
||||
pub const PARACHAIN_HOST: ApiId = *b"parahost";
|
||||
}
|
||||
|
||||
/// This helper trait ensures that we can encode Statement as CompactStatement,
|
||||
/// and anything as itself.
|
||||
///
|
||||
/// This resembles `parity_scale_codec::EncodeLike`, but it's distinct:
|
||||
/// EncodeLike is a marker trait which asserts at the typesystem level that
|
||||
/// one type's encoding is a valid encoding for another type. It doesn't
|
||||
/// perform any type conversion when encoding.
|
||||
///
|
||||
/// This trait, on the other hand, provides a method which can be used to
|
||||
/// simultaneously convert and encode one type as another.
|
||||
pub trait EncodeAs<T> {
|
||||
/// Convert Self into T, then encode T.
|
||||
///
|
||||
/// This is useful when T is a subset of Self, reducing encoding costs;
|
||||
/// its signature also means that we do not need to clone Self in order
|
||||
/// to retain ownership, as we would if we were to do
|
||||
/// `self.clone().into().encode()`.
|
||||
fn encode_as(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
impl<T: Encode> EncodeAs<T> for T {
|
||||
fn encode_as(&self) -> Vec<u8> {
|
||||
self.encode()
|
||||
}
|
||||
}
|
||||
|
||||
/// A signed type which encapsulates the common desire to sign some data and validate a signature.
|
||||
///
|
||||
/// Note that the internal fields are not public; they are all accessable by immutable getters.
|
||||
/// This reduces the chance that they are accidentally mutated, invalidating the signature.
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)]
|
||||
pub struct Signed<Payload, RealPayload = Payload> {
|
||||
/// The payload is part of the signed data. The rest is the signing context,
|
||||
/// which is known both at signing and at validation.
|
||||
payload: Payload,
|
||||
/// The index of the validator signing this statement.
|
||||
validator_index: ValidatorIndex,
|
||||
/// The signature by the validator of the signed payload.
|
||||
signature: ValidatorSignature,
|
||||
/// This ensures the real payload is tracked at the typesystem level.
|
||||
real_payload: sp_std::marker::PhantomData<RealPayload>,
|
||||
}
|
||||
|
||||
// We can't bound this on `Payload: Into<RealPayload>` beacuse that conversion consumes
|
||||
// the payload, and we don't want that. We can't bound it on `Payload: AsRef<RealPayload>`
|
||||
// because there's no blanket impl of `AsRef<T> for T`. In the end, we just invent our
|
||||
// own trait which does what we need: EncodeAs.
|
||||
impl<Payload: EncodeAs<RealPayload>, RealPayload: Encode> Signed<Payload, RealPayload> {
|
||||
fn payload_data<H: Encode>(payload: &Payload, context: &SigningContext<H>) -> Vec<u8> {
|
||||
// equivalent to (real_payload, context).encode()
|
||||
let mut out = payload.encode_as();
|
||||
out.extend(context.encode());
|
||||
out
|
||||
}
|
||||
|
||||
/// Used to create a `Signed` from already existing parts.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn new<H: Encode>(
|
||||
payload: Payload,
|
||||
validator_index: ValidatorIndex,
|
||||
signature: ValidatorSignature,
|
||||
context: &SigningContext<H>,
|
||||
key: &ValidatorId,
|
||||
) -> Option<Self> {
|
||||
let s = Self {
|
||||
payload,
|
||||
validator_index,
|
||||
signature,
|
||||
real_payload: std::marker::PhantomData,
|
||||
};
|
||||
|
||||
s.check_signature(context, key).ok()?;
|
||||
|
||||
Some(s)
|
||||
}
|
||||
|
||||
/// Sign this payload with the given context and key, storing the validator index.
|
||||
#[cfg(feature = "std")]
|
||||
pub async fn sign<H: Encode>(
|
||||
keystore: &SyncCryptoStorePtr,
|
||||
payload: Payload,
|
||||
context: &SigningContext<H>,
|
||||
validator_index: ValidatorIndex,
|
||||
key: &ValidatorId,
|
||||
) -> Result<Option<Self>, KeystoreError> {
|
||||
let data = Self::payload_data(&payload, context);
|
||||
let signature = CryptoStore::sign_with(
|
||||
&**keystore,
|
||||
ValidatorId::ID,
|
||||
&key.into(),
|
||||
&data,
|
||||
).await?;
|
||||
|
||||
let signature = match signature {
|
||||
Some(sig) => sig.try_into().map_err(|_| KeystoreError::KeyNotSupported(ValidatorId::ID))?,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
Ok(Some(Self {
|
||||
payload,
|
||||
validator_index,
|
||||
signature,
|
||||
real_payload: std::marker::PhantomData,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Validate the payload given the context and public key.
|
||||
pub fn check_signature<H: Encode>(&self, context: &SigningContext<H>, key: &ValidatorId) -> Result<(), ()> {
|
||||
let data = Self::payload_data(&self.payload, context);
|
||||
if self.signature.verify(data.as_slice(), key) { Ok(()) } else { Err(()) }
|
||||
}
|
||||
|
||||
/// Immutably access the payload.
|
||||
#[inline]
|
||||
pub fn payload(&self) -> &Payload {
|
||||
&self.payload
|
||||
}
|
||||
|
||||
/// Immutably access the validator index.
|
||||
#[inline]
|
||||
pub fn validator_index(&self) -> ValidatorIndex {
|
||||
self.validator_index
|
||||
}
|
||||
|
||||
/// Immutably access the signature.
|
||||
#[inline]
|
||||
pub fn signature(&self) -> &ValidatorSignature {
|
||||
&self.signature
|
||||
}
|
||||
|
||||
/// Discard signing data, get the payload
|
||||
// Note: can't `impl<P, R> From<Signed<P, R>> for P` because the orphan rule exception doesn't
|
||||
// handle this case yet. Likewise can't `impl<P, R> Into<P> for Signed<P, R>` because it might
|
||||
// potentially conflict with the global blanket impl, even though it currently doesn't.
|
||||
#[inline]
|
||||
pub fn into_payload(self) -> Payload {
|
||||
self.payload
|
||||
}
|
||||
|
||||
/// Convert `Payload` into `RealPayload`.
|
||||
pub fn convert_payload(&self) -> Signed<RealPayload> where for<'a> &'a Payload: Into<RealPayload> {
|
||||
Signed {
|
||||
signature: self.signature.clone(),
|
||||
validator_index: self.validator_index,
|
||||
payload: self.payload().into(),
|
||||
real_payload: sp_std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Custom validity errors used in Polkadot while validating transactions.
|
||||
#[repr(u8)]
|
||||
pub enum ValidityError {
|
||||
|
||||
@@ -44,8 +44,8 @@ pub use polkadot_parachain::primitives::{
|
||||
// Export some basic parachain primitives from v0.
|
||||
pub use crate::v0::{
|
||||
CollatorId, CollatorSignature, PARACHAIN_KEY_TYPE_ID, ValidatorId, ValidatorIndex,
|
||||
ValidatorSignature, SigningContext, Signed, ValidityAttestation,
|
||||
CompactStatement, SignedStatement, EncodeAs,
|
||||
ValidatorSignature, SigningContext, ValidityAttestation,
|
||||
CompactStatement,
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
@@ -58,6 +58,10 @@ pub use crate::v0::{ValidatorPair, CollatorPair};
|
||||
pub use sp_staking::SessionIndex;
|
||||
pub use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
|
||||
|
||||
/// Signed data.
|
||||
mod signed;
|
||||
pub use signed::{Signed, UncheckedSigned, EncodeAs};
|
||||
|
||||
/// A declarations of storage keys where an external observer can find some interesting data.
|
||||
pub mod well_known_keys {
|
||||
use super::{Id, HrmpChannelId};
|
||||
@@ -169,6 +173,7 @@ pub mod well_known_keys {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Unique identifier for the Parachains Inherent
|
||||
pub const PARACHAINS_INHERENT_IDENTIFIER: InherentIdentifier = *b"parachn0";
|
||||
|
||||
@@ -461,11 +466,19 @@ impl From<BitVec<bitvec::order::Lsb0, u8>> for AvailabilityBitfield {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A signed compact statement, suitable to be sent to the chain.
|
||||
pub type SignedStatement = Signed<CompactStatement>;
|
||||
|
||||
/// A bitfield signed by a particular validator about the availability of pending candidates.
|
||||
pub type SignedAvailabilityBitfield = Signed<AvailabilityBitfield>;
|
||||
/// A signed bitfield with signature not yet checked.
|
||||
pub type UncheckedSignedAvailabilityBitfield = UncheckedSigned<AvailabilityBitfield>;
|
||||
|
||||
/// A set of signed availability bitfields. Should be sorted by validator index, ascending.
|
||||
pub type SignedAvailabilityBitfields = Vec<SignedAvailabilityBitfield>;
|
||||
/// A set of unchecked signed availability bitfields. Should be sorted by validator index, ascending.
|
||||
pub type UncheckedSignedAvailabilityBitfields = Vec<UncheckedSignedAvailabilityBitfield>;
|
||||
|
||||
/// A backed (or backable, depending on context) candidate.
|
||||
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
|
||||
@@ -1120,7 +1133,7 @@ pub struct DisputeState<N = BlockNumber> {
|
||||
#[derive(Encode, Decode, Clone, PartialEq, RuntimeDebug)]
|
||||
pub struct InherentData<HDR: HeaderT = Header> {
|
||||
/// Signed bitfields by validators about availability.
|
||||
pub bitfields: SignedAvailabilityBitfields,
|
||||
pub bitfields: UncheckedSignedAvailabilityBitfields,
|
||||
/// Backed candidates for inclusion in the block.
|
||||
pub backed_candidates: Vec<BackedCandidate<HDR::Hash>>,
|
||||
/// Sets of dispute votes for inclusion,
|
||||
@@ -0,0 +1,282 @@
|
||||
// Copyright 2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
|
||||
use sp_std::prelude::Vec;
|
||||
#[cfg(feature = "std")]
|
||||
use sp_std::convert::TryInto;
|
||||
#[cfg(feature = "std")]
|
||||
use application_crypto::AppKey;
|
||||
#[cfg(feature = "std")]
|
||||
use sp_keystore::{CryptoStore, SyncCryptoStorePtr, Error as KeystoreError};
|
||||
|
||||
use primitives::RuntimeDebug;
|
||||
use runtime_primitives::traits::AppVerify;
|
||||
|
||||
use crate::v0::{SigningContext, ValidatorId, ValidatorSignature, ValidatorIndex};
|
||||
|
||||
/// Signed data with signature already verified.
|
||||
///
|
||||
/// NOTE: This type does not have an Encode/Decode instance, as this would cancel out our
|
||||
/// valid signature guarantees. If you need to encode/decode you have to convert into an
|
||||
/// `UncheckedSigned` first.
|
||||
///
|
||||
/// `Signed` can easily be converted into `UncheckedSigned` and conversion back via `into_signed`
|
||||
/// enforces a valid signature again.
|
||||
#[derive(Clone, PartialEq, Eq, RuntimeDebug)]
|
||||
pub struct Signed<Payload, RealPayload = Payload>(UncheckedSigned<Payload, RealPayload>);
|
||||
|
||||
/// Unchecked signed data, can be converted to `Signed` by checking the signature.
|
||||
#[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode)]
|
||||
pub struct UncheckedSigned<Payload, RealPayload = Payload> {
|
||||
/// The payload is part of the signed data. The rest is the signing context,
|
||||
/// which is known both at signing and at validation.
|
||||
payload: Payload,
|
||||
/// The index of the validator signing this statement.
|
||||
validator_index: ValidatorIndex,
|
||||
/// The signature by the validator of the signed payload.
|
||||
signature: ValidatorSignature,
|
||||
/// This ensures the real payload is tracked at the typesystem level.
|
||||
real_payload: sp_std::marker::PhantomData<RealPayload>,
|
||||
}
|
||||
|
||||
impl<Payload: EncodeAs<RealPayload>, RealPayload: Encode> Signed<Payload, RealPayload> {
|
||||
/// Used to create a `Signed` from already existing parts.
|
||||
///
|
||||
/// The signature is checked as part of the process.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn new<H: Encode>(
|
||||
payload: Payload,
|
||||
validator_index: ValidatorIndex,
|
||||
signature: ValidatorSignature,
|
||||
context: &SigningContext<H>,
|
||||
key: &ValidatorId,
|
||||
) -> Option<Self> {
|
||||
let s = UncheckedSigned {
|
||||
payload,
|
||||
validator_index,
|
||||
signature,
|
||||
real_payload: std::marker::PhantomData,
|
||||
};
|
||||
|
||||
s.check_signature(context, key).ok()?;
|
||||
|
||||
Some(Self(s))
|
||||
}
|
||||
|
||||
/// Create a new `Signed` by signing data.
|
||||
#[cfg(feature = "std")]
|
||||
pub async fn sign<H: Encode>(
|
||||
keystore: &SyncCryptoStorePtr,
|
||||
payload: Payload,
|
||||
context: &SigningContext<H>,
|
||||
validator_index: ValidatorIndex,
|
||||
key: &ValidatorId,
|
||||
) -> Result<Option<Self>, KeystoreError> {
|
||||
let r = UncheckedSigned::sign(keystore, payload, context, validator_index, key).await?;
|
||||
Ok(r.map(Self))
|
||||
}
|
||||
|
||||
/// Try to convert from `UncheckedSigned` by checking the signature.
|
||||
pub fn try_from_unchecked<H: Encode>(
|
||||
unchecked: UncheckedSigned<Payload, RealPayload>,
|
||||
context: &SigningContext<H>,
|
||||
key: &ValidatorId
|
||||
) -> Result<Self, UncheckedSigned<Payload, RealPayload>> {
|
||||
if unchecked.check_signature(context, key).is_ok() {
|
||||
Ok(Self(unchecked))
|
||||
} else {
|
||||
Err(unchecked)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to data as unchecked.
|
||||
pub fn as_unchecked(&self) -> &UncheckedSigned<Payload, RealPayload> {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Immutably access the payload.
|
||||
#[inline]
|
||||
pub fn payload(&self) -> &Payload {
|
||||
&self.0.payload
|
||||
}
|
||||
|
||||
/// Immutably access the validator index.
|
||||
#[inline]
|
||||
pub fn validator_index(&self) -> ValidatorIndex {
|
||||
self.0.validator_index
|
||||
}
|
||||
|
||||
/// Immutably access the signature.
|
||||
#[inline]
|
||||
pub fn signature(&self) -> &ValidatorSignature {
|
||||
&self.0.signature
|
||||
}
|
||||
|
||||
/// Discard signing data, get the payload
|
||||
#[inline]
|
||||
pub fn into_payload(self) -> Payload {
|
||||
self.0.payload
|
||||
}
|
||||
|
||||
/// Convert `Payload` into `RealPayload`.
|
||||
pub fn convert_payload(&self) -> Signed<RealPayload> where for<'a> &'a Payload: Into<RealPayload> {
|
||||
Signed(self.0.unchecked_convert_payload())
|
||||
}
|
||||
}
|
||||
|
||||
// We can't bound this on `Payload: Into<RealPayload>` beacuse that conversion consumes
|
||||
// the payload, and we don't want that. We can't bound it on `Payload: AsRef<RealPayload>`
|
||||
// because there's no blanket impl of `AsRef<T> for T`. In the end, we just invent our
|
||||
// own trait which does what we need: EncodeAs.
|
||||
impl<Payload: EncodeAs<RealPayload>, RealPayload: Encode> UncheckedSigned<Payload, RealPayload> {
|
||||
/// Used to create a `UncheckedSigned` from already existing parts.
|
||||
///
|
||||
/// Signature is not checked here, hence `UncheckedSigned`.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn new(
|
||||
payload: Payload,
|
||||
validator_index: ValidatorIndex,
|
||||
signature: ValidatorSignature,
|
||||
) -> Self {
|
||||
Self {
|
||||
payload,
|
||||
validator_index,
|
||||
signature,
|
||||
real_payload: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check signature and convert to `Signed` if successful.
|
||||
pub fn try_into_checked<H: Encode>(
|
||||
self,
|
||||
context: &SigningContext<H>,
|
||||
key: &ValidatorId
|
||||
) -> Result<Signed<Payload, RealPayload>, Self> {
|
||||
Signed::try_from_unchecked(self, context, key)
|
||||
}
|
||||
|
||||
/// Immutably access the payload.
|
||||
#[inline]
|
||||
pub fn unchecked_payload(&self) -> &Payload {
|
||||
&self.payload
|
||||
}
|
||||
|
||||
/// Immutably access the validator index.
|
||||
#[inline]
|
||||
pub fn unchecked_validator_index(&self) -> ValidatorIndex {
|
||||
self.validator_index
|
||||
}
|
||||
|
||||
/// Immutably access the signature.
|
||||
#[inline]
|
||||
pub fn unchecked_signature(&self) -> &ValidatorSignature {
|
||||
&self.signature
|
||||
}
|
||||
|
||||
/// Discard signing data, get the payload
|
||||
#[inline]
|
||||
pub fn unchecked_into_payload(self) -> Payload {
|
||||
self.payload
|
||||
}
|
||||
|
||||
/// Convert `Payload` into `RealPayload`.
|
||||
pub fn unchecked_convert_payload(&self) -> UncheckedSigned<RealPayload> where for<'a> &'a Payload: Into<RealPayload> {
|
||||
UncheckedSigned {
|
||||
signature: self.signature.clone(),
|
||||
validator_index: self.validator_index,
|
||||
payload: (&self.payload).into(),
|
||||
real_payload: sp_std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn payload_data<H: Encode>(payload: &Payload, context: &SigningContext<H>) -> Vec<u8> {
|
||||
// equivalent to (real_payload, context).encode()
|
||||
let mut out = payload.encode_as();
|
||||
out.extend(context.encode());
|
||||
out
|
||||
}
|
||||
|
||||
/// Sign this payload with the given context and key, storing the validator index.
|
||||
#[cfg(feature = "std")]
|
||||
async fn sign<H: Encode>(
|
||||
keystore: &SyncCryptoStorePtr,
|
||||
payload: Payload,
|
||||
context: &SigningContext<H>,
|
||||
validator_index: ValidatorIndex,
|
||||
key: &ValidatorId,
|
||||
) -> Result<Option<Self>, KeystoreError> {
|
||||
let data = Self::payload_data(&payload, context);
|
||||
let signature = CryptoStore::sign_with(
|
||||
&**keystore,
|
||||
ValidatorId::ID,
|
||||
&key.into(),
|
||||
&data,
|
||||
).await?;
|
||||
|
||||
let signature = match signature {
|
||||
Some(sig) => sig.try_into().map_err(|_| KeystoreError::KeyNotSupported(ValidatorId::ID))?,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
Ok(Some(Self {
|
||||
payload,
|
||||
validator_index,
|
||||
signature,
|
||||
real_payload: std::marker::PhantomData,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Validate the payload given the context and public key.
|
||||
fn check_signature<H: Encode>(&self, context: &SigningContext<H>, key: &ValidatorId) -> Result<(), ()> {
|
||||
let data = Self::payload_data(&self.payload, context);
|
||||
if self.signature.verify(data.as_slice(), key) { Ok(()) } else { Err(()) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl<Payload, RealPayload> From<Signed<Payload, RealPayload>> for UncheckedSigned<Payload, RealPayload> {
|
||||
fn from(signed: Signed<Payload, RealPayload>) -> Self {
|
||||
signed.0
|
||||
}
|
||||
}
|
||||
|
||||
/// This helper trait ensures that we can encode Statement as CompactStatement,
|
||||
/// and anything as itself.
|
||||
///
|
||||
/// This resembles `parity_scale_codec::EncodeLike`, but it's distinct:
|
||||
/// EncodeLike is a marker trait which asserts at the typesystem level that
|
||||
/// one type's encoding is a valid encoding for another type. It doesn't
|
||||
/// perform any type conversion when encoding.
|
||||
///
|
||||
/// This trait, on the other hand, provides a method which can be used to
|
||||
/// simultaneously convert and encode one type as another.
|
||||
pub trait EncodeAs<T> {
|
||||
/// Convert Self into T, then encode T.
|
||||
///
|
||||
/// This is useful when T is a subset of Self, reducing encoding costs;
|
||||
/// its signature also means that we do not need to clone Self in order
|
||||
/// to retain ownership, as we would if we were to do
|
||||
/// `self.clone().into().encode()`.
|
||||
fn encode_as(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
impl<T: Encode> EncodeAs<T> for T {
|
||||
fn encode_as(&self) -> Vec<u8> {
|
||||
self.encode()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user