1use codec::{Decode, DecodeWithMemTracking, Encode};
18use scale_info::TypeInfo;
19
20use alloc::vec::Vec;
21#[cfg(feature = "std")]
22use sp_application_crypto::AppCrypto;
23#[cfg(feature = "std")]
24use sp_keystore::{Error as KeystoreError, KeystorePtr};
25
26use sp_core::RuntimeDebug;
27use sp_runtime::traits::AppVerify;
28
29use super::{SigningContext, ValidatorId, ValidatorIndex, ValidatorSignature};
30
31#[derive(Clone, PartialEq, Eq, RuntimeDebug)]
40pub struct Signed<Payload, RealPayload = Payload>(UncheckedSigned<Payload, RealPayload>);
41
42impl<Payload, RealPayload> Signed<Payload, RealPayload> {
43 pub fn into_unchecked(self) -> UncheckedSigned<Payload, RealPayload> {
45 self.0
46 }
47}
48
49#[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
51pub struct UncheckedSigned<Payload, RealPayload = Payload> {
52 payload: Payload,
55 validator_index: ValidatorIndex,
57 signature: ValidatorSignature,
59 real_payload: core::marker::PhantomData<RealPayload>,
61}
62
63impl<Payload: EncodeAs<RealPayload>, RealPayload: Encode> Signed<Payload, RealPayload> {
64 #[cfg(feature = "std")]
68 pub fn new<H: Encode>(
69 payload: Payload,
70 validator_index: ValidatorIndex,
71 signature: ValidatorSignature,
72 context: &SigningContext<H>,
73 key: &ValidatorId,
74 ) -> Option<Self> {
75 let s = UncheckedSigned {
76 payload,
77 validator_index,
78 signature,
79 real_payload: std::marker::PhantomData,
80 };
81
82 s.check_signature(context, key).ok()?;
83
84 Some(Self(s))
85 }
86
87 #[cfg(feature = "std")]
89 pub fn sign<H: Encode>(
90 keystore: &KeystorePtr,
91 payload: Payload,
92 context: &SigningContext<H>,
93 validator_index: ValidatorIndex,
94 key: &ValidatorId,
95 ) -> Result<Option<Self>, KeystoreError> {
96 let r = UncheckedSigned::sign(keystore, payload, context, validator_index, key)?;
97 Ok(r.map(Self))
98 }
99
100 pub fn try_from_unchecked<H: Encode>(
102 unchecked: UncheckedSigned<Payload, RealPayload>,
103 context: &SigningContext<H>,
104 key: &ValidatorId,
105 ) -> Result<Self, UncheckedSigned<Payload, RealPayload>> {
106 if unchecked.check_signature(context, key).is_ok() {
107 Ok(Self(unchecked))
108 } else {
109 Err(unchecked)
110 }
111 }
112
113 pub fn as_unchecked(&self) -> &UncheckedSigned<Payload, RealPayload> {
115 &self.0
116 }
117
118 #[inline]
120 pub fn payload(&self) -> &Payload {
121 &self.0.payload
122 }
123
124 #[inline]
126 pub fn validator_index(&self) -> ValidatorIndex {
127 self.0.validator_index
128 }
129
130 #[inline]
132 pub fn signature(&self) -> &ValidatorSignature {
133 &self.0.signature
134 }
135
136 #[inline]
138 pub fn into_payload(self) -> Payload {
139 self.0.payload
140 }
141
142 pub fn convert_payload(&self) -> Signed<RealPayload>
144 where
145 for<'a> &'a Payload: Into<RealPayload>,
146 {
147 Signed(self.0.unchecked_convert_payload())
148 }
149
150 pub fn convert_to_superpayload<SuperPayload>(
155 self,
156 claimed: SuperPayload,
157 ) -> Result<Signed<SuperPayload, RealPayload>, (Self, SuperPayload)>
158 where
159 SuperPayload: EncodeAs<RealPayload>,
160 {
161 if claimed.encode_as() == self.0.payload.encode_as() {
162 Ok(Signed(UncheckedSigned {
163 payload: claimed,
164 validator_index: self.0.validator_index,
165 signature: self.0.signature,
166 real_payload: core::marker::PhantomData,
167 }))
168 } else {
169 Err((self, claimed))
170 }
171 }
172
173 pub fn convert_to_superpayload_with<F, SuperPayload>(
180 self,
181 convert: F,
182 ) -> Result<Signed<SuperPayload, RealPayload>, SuperPayload>
183 where
184 F: FnOnce(Payload) -> SuperPayload,
185 SuperPayload: EncodeAs<RealPayload>,
186 {
187 let expected_encode_as = self.0.payload.encode_as();
188 let converted = convert(self.0.payload);
189 if converted.encode_as() == expected_encode_as {
190 Ok(Signed(UncheckedSigned {
191 payload: converted,
192 validator_index: self.0.validator_index,
193 signature: self.0.signature,
194 real_payload: core::marker::PhantomData,
195 }))
196 } else {
197 Err(converted)
198 }
199 }
200}
201
202impl<Payload: EncodeAs<RealPayload>, RealPayload: Encode> UncheckedSigned<Payload, RealPayload> {
207 #[cfg(feature = "std")]
211 pub fn new(
212 payload: Payload,
213 validator_index: ValidatorIndex,
214 signature: ValidatorSignature,
215 ) -> Self {
216 Self { payload, validator_index, signature, real_payload: std::marker::PhantomData }
217 }
218
219 pub fn try_into_checked<H: Encode>(
221 self,
222 context: &SigningContext<H>,
223 key: &ValidatorId,
224 ) -> Result<Signed<Payload, RealPayload>, Self> {
225 Signed::try_from_unchecked(self, context, key)
226 }
227
228 #[inline]
230 pub fn unchecked_payload(&self) -> &Payload {
231 &self.payload
232 }
233
234 #[inline]
236 pub fn unchecked_validator_index(&self) -> ValidatorIndex {
237 self.validator_index
238 }
239
240 #[inline]
242 pub fn unchecked_signature(&self) -> &ValidatorSignature {
243 &self.signature
244 }
245
246 #[inline]
248 pub fn unchecked_into_payload(self) -> Payload {
249 self.payload
250 }
251
252 pub fn unchecked_convert_payload(&self) -> UncheckedSigned<RealPayload>
254 where
255 for<'a> &'a Payload: Into<RealPayload>,
256 {
257 UncheckedSigned {
258 signature: self.signature.clone(),
259 validator_index: self.validator_index,
260 payload: (&self.payload).into(),
261 real_payload: core::marker::PhantomData,
262 }
263 }
264
265 fn payload_data<H: Encode>(payload: &Payload, context: &SigningContext<H>) -> Vec<u8> {
266 let mut out = payload.encode_as();
268 out.extend(context.encode());
269 out
270 }
271
272 #[cfg(feature = "std")]
274 fn sign<H: Encode>(
275 keystore: &KeystorePtr,
276 payload: Payload,
277 context: &SigningContext<H>,
278 validator_index: ValidatorIndex,
279 key: &ValidatorId,
280 ) -> Result<Option<Self>, KeystoreError> {
281 let data = Self::payload_data(&payload, context);
282 let signature =
283 keystore.sr25519_sign(ValidatorId::ID, key.as_ref(), &data)?.map(|sig| Self {
284 payload,
285 validator_index,
286 signature: sig.into(),
287 real_payload: std::marker::PhantomData,
288 });
289 Ok(signature)
290 }
291
292 pub fn check_signature<H: Encode>(
295 &self,
296 context: &SigningContext<H>,
297 key: &ValidatorId,
298 ) -> Result<(), ()> {
299 let data = Self::payload_data(&self.payload, context);
300 if self.signature.verify(data.as_slice(), key) {
301 Ok(())
302 } else {
303 Err(())
304 }
305 }
306
307 #[cfg(any(feature = "runtime-benchmarks", feature = "std"))]
309 pub fn benchmark_sign<H: Encode>(
310 public: &super::ValidatorId,
311 payload: Payload,
312 context: &SigningContext<H>,
313 validator_index: ValidatorIndex,
314 ) -> Self {
315 use sp_application_crypto::RuntimeAppPublic;
316 let data = Self::payload_data(&payload, context);
317 let signature = public.sign(&data).unwrap();
318
319 Self { payload, validator_index, signature, real_payload: core::marker::PhantomData }
320 }
321
322 #[cfg(any(feature = "runtime-benchmarks", feature = "std"))]
324 pub fn benchmark_signature(&self) -> ValidatorSignature {
325 self.signature.clone()
326 }
327
328 #[cfg(feature = "std")]
330 pub fn set_signature(&mut self, signature: ValidatorSignature) {
331 self.signature = signature
332 }
333}
334
335impl<Payload, RealPayload> From<Signed<Payload, RealPayload>>
336 for UncheckedSigned<Payload, RealPayload>
337{
338 fn from(signed: Signed<Payload, RealPayload>) -> Self {
339 signed.0
340 }
341}
342
343pub trait EncodeAs<T> {
354 fn encode_as(&self) -> Vec<u8>;
361}
362
363impl<T: Encode> EncodeAs<T> for T {
364 fn encode_as(&self) -> Vec<u8> {
365 self.encode()
366 }
367}