use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
#[cfg(feature = "std")]
use application_crypto::AppKey;
#[cfg(feature = "std")]
use sp_keystore::{CryptoStore, Error as KeystoreError, SyncCryptoStorePtr};
use sp_std::prelude::Vec;
use primitives::RuntimeDebug;
use runtime_primitives::traits::AppVerify;
use super::{SigningContext, ValidatorId, ValidatorIndex, ValidatorSignature};
#[derive(Clone, PartialEq, Eq, RuntimeDebug)]
pub struct Signed<Payload, RealPayload = Payload>(UncheckedSigned<Payload, RealPayload>);
impl<Payload, RealPayload> Signed<Payload, RealPayload> {
pub fn into_unchecked(self) -> UncheckedSigned<Payload, RealPayload> {
self.0
}
}
#[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, TypeInfo)]
pub struct UncheckedSigned<Payload, RealPayload = Payload> {
payload: Payload,
validator_index: ValidatorIndex,
signature: ValidatorSignature,
real_payload: sp_std::marker::PhantomData<RealPayload>,
}
impl<Payload: EncodeAs<RealPayload>, RealPayload: Encode> Signed<Payload, RealPayload> {
#[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))
}
#[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))
}
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)
}
}
pub fn as_unchecked(&self) -> &UncheckedSigned<Payload, RealPayload> {
&self.0
}
#[inline]
pub fn payload(&self) -> &Payload {
&self.0.payload
}
#[inline]
pub fn validator_index(&self) -> ValidatorIndex {
self.0.validator_index
}
#[inline]
pub fn signature(&self) -> &ValidatorSignature {
&self.0.signature
}
#[inline]
pub fn into_payload(self) -> Payload {
self.0.payload
}
pub fn convert_payload(&self) -> Signed<RealPayload>
where
for<'a> &'a Payload: Into<RealPayload>,
{
Signed(self.0.unchecked_convert_payload())
}
pub fn convert_to_superpayload<SuperPayload>(
self,
claimed: SuperPayload,
) -> Result<Signed<SuperPayload, RealPayload>, (Self, SuperPayload)>
where
SuperPayload: EncodeAs<RealPayload>,
Payload: Encode,
{
if claimed.encode_as() == self.0.payload.encode_as() {
Ok(Signed(UncheckedSigned {
payload: claimed,
validator_index: self.0.validator_index,
signature: self.0.signature,
real_payload: sp_std::marker::PhantomData,
}))
} else {
Err((self, claimed))
}
}
}
impl<Payload: EncodeAs<RealPayload>, RealPayload: Encode> UncheckedSigned<Payload, RealPayload> {
#[cfg(feature = "std")]
pub fn new(
payload: Payload,
validator_index: ValidatorIndex,
signature: ValidatorSignature,
) -> Self {
Self { payload, validator_index, signature, real_payload: std::marker::PhantomData }
}
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)
}
#[inline]
pub fn unchecked_payload(&self) -> &Payload {
&self.payload
}
#[inline]
pub fn unchecked_validator_index(&self) -> ValidatorIndex {
self.validator_index
}
#[inline]
pub fn unchecked_signature(&self) -> &ValidatorSignature {
&self.signature
}
#[inline]
pub fn unchecked_into_payload(self) -> Payload {
self.payload
}
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> {
let mut out = payload.encode_as();
out.extend(context.encode());
out
}
#[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,
}))
}
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(())
}
}
#[cfg(any(feature = "runtime-benchmarks", feature = "std"))]
pub fn benchmark_sign<H: Encode>(
public: &super::ValidatorId,
payload: Payload,
context: &SigningContext<H>,
validator_index: ValidatorIndex,
) -> Self {
use application_crypto::RuntimeAppPublic;
let data = Self::payload_data(&payload, context);
let signature = public.sign(&data).unwrap();
Self { payload, validator_index, signature, real_payload: sp_std::marker::PhantomData }
}
#[cfg(any(feature = "runtime-benchmarks", feature = "std"))]
pub fn benchmark_signature(&self) -> ValidatorSignature {
self.signature.clone()
}
#[cfg(feature = "std")]
pub fn set_signature(&mut self, signature: ValidatorSignature) {
self.signature = signature
}
}
impl<Payload, RealPayload> From<Signed<Payload, RealPayload>>
for UncheckedSigned<Payload, RealPayload>
{
fn from(signed: Signed<Payload, RealPayload>) -> Self {
signed.0
}
}
pub trait EncodeAs<T> {
fn encode_as(&self) -> Vec<u8>;
}
impl<T: Encode> EncodeAs<T> for T {
fn encode_as(&self) -> Vec<u8> {
self.encode()
}
}