1
// Copyright (C) Parity Technologies (UK) Ltd.
2
// This file is part of Polkadot.
3

            
4
// Polkadot is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Polkadot is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
use codec::{Decode, Encode};
18
use scale_info::TypeInfo;
19

            
20
use alloc::vec::Vec;
21
#[cfg(feature = "std")]
22
use sp_application_crypto::AppCrypto;
23
#[cfg(feature = "std")]
24
use sp_keystore::{Error as KeystoreError, KeystorePtr};
25

            
26
use sp_core::RuntimeDebug;
27
use sp_runtime::traits::AppVerify;
28

            
29
use super::{SigningContext, ValidatorId, ValidatorIndex, ValidatorSignature};
30

            
31
/// Signed data with signature already verified.
32
///
33
/// NOTE: This type does not have an Encode/Decode instance, as this would cancel out our
34
/// valid signature guarantees. If you need to encode/decode you have to convert into an
35
/// `UncheckedSigned` first.
36
///
37
/// `Signed` can easily be converted into `UncheckedSigned` and conversion back via `into_signed`
38
/// enforces a valid signature again.
39
#[derive(Clone, PartialEq, Eq, RuntimeDebug)]
40
pub struct Signed<Payload, RealPayload = Payload>(UncheckedSigned<Payload, RealPayload>);
41

            
42
impl<Payload, RealPayload> Signed<Payload, RealPayload> {
43
	/// Convert back to an unchecked type.
44
	pub fn into_unchecked(self) -> UncheckedSigned<Payload, RealPayload> {
45
		self.0
46
	}
47
}
48

            
49
/// Unchecked signed data, can be converted to `Signed` by checking the signature.
50
#[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, TypeInfo)]
51
pub struct UncheckedSigned<Payload, RealPayload = Payload> {
52
	/// The payload is part of the signed data. The rest is the signing context,
53
	/// which is known both at signing and at validation.
54
	payload: Payload,
55
	/// The index of the validator signing this statement.
56
	validator_index: ValidatorIndex,
57
	/// The signature by the validator of the signed payload.
58
	signature: ValidatorSignature,
59
	/// This ensures the real payload is tracked at the typesystem level.
60
	real_payload: core::marker::PhantomData<RealPayload>,
61
}
62

            
63
impl<Payload: EncodeAs<RealPayload>, RealPayload: Encode> Signed<Payload, RealPayload> {
64
	/// Used to create a `Signed` from already existing parts.
65
	///
66
	/// The signature is checked as part of the process.
67
	#[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
	/// Create a new `Signed` by signing data.
88
	#[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
	/// Try to convert from `UncheckedSigned` by checking the signature.
101
	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
	/// Get a reference to data as unchecked.
114
	pub fn as_unchecked(&self) -> &UncheckedSigned<Payload, RealPayload> {
115
		&self.0
116
	}
117

            
118
	/// Immutably access the payload.
119
	#[inline]
120
	pub fn payload(&self) -> &Payload {
121
		&self.0.payload
122
	}
123

            
124
	/// Immutably access the validator index.
125
	#[inline]
126
	pub fn validator_index(&self) -> ValidatorIndex {
127
		self.0.validator_index
128
	}
129

            
130
	/// Immutably access the signature.
131
	#[inline]
132
	pub fn signature(&self) -> &ValidatorSignature {
133
		&self.0.signature
134
	}
135

            
136
	/// Discard signing data, get the payload
137
	#[inline]
138
	pub fn into_payload(self) -> Payload {
139
		self.0.payload
140
	}
141

            
142
	/// Convert `Payload` into `RealPayload`.
143
	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
	/// Convert `Payload` into some claimed `SuperPayload` if the encoding matches.
151
	///
152
	/// Succeeds if and only if the super-payload provided actually encodes as
153
	/// the expected payload.
154
	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
	/// Convert `Payload` into some converted `SuperPayload` if the encoding matches.
174
	///
175
	/// This invokes the closure on the current payload, which is irreversible.
176
	///
177
	/// Succeeds if and only if the super-payload provided actually encodes as
178
	/// the expected payload.
179
	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

            
202
// We can't bound this on `Payload: Into<RealPayload>` because that conversion consumes
203
// the payload, and we don't want that. We can't bound it on `Payload: AsRef<RealPayload>`
204
// because there's no blanket impl of `AsRef<T> for T`. In the end, we just invent our
205
// own trait which does what we need: EncodeAs.
206
impl<Payload: EncodeAs<RealPayload>, RealPayload: Encode> UncheckedSigned<Payload, RealPayload> {
207
	/// Used to create a `UncheckedSigned` from already existing parts.
208
	///
209
	/// Signature is not checked here, hence `UncheckedSigned`.
210
	#[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
	/// Check signature and convert to `Signed` if successful.
220
	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
	/// Immutably access the payload.
229
	#[inline]
230
	pub fn unchecked_payload(&self) -> &Payload {
231
		&self.payload
232
	}
233

            
234
	/// Immutably access the validator index.
235
	#[inline]
236
	pub fn unchecked_validator_index(&self) -> ValidatorIndex {
237
		self.validator_index
238
	}
239

            
240
	/// Immutably access the signature.
241
	#[inline]
242
	pub fn unchecked_signature(&self) -> &ValidatorSignature {
243
		&self.signature
244
	}
245

            
246
	/// Discard signing data, get the payload
247
	#[inline]
248
	pub fn unchecked_into_payload(self) -> Payload {
249
		self.payload
250
	}
251

            
252
	/// Convert `Payload` into `RealPayload`.
253
	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
		// equivalent to (`real_payload`, context).encode()
267
		let mut out = payload.encode_as();
268
		out.extend(context.encode());
269
		out
270
	}
271

            
272
	/// Sign this payload with the given context and key, storing the validator index.
273
	#[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
	/// Validate the payload given the context and public key
293
	/// without creating a `Signed` type.
294
	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
	/// Sign this payload with the given context and pair.
308
	#[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
	/// Immutably access the signature.
323
	#[cfg(any(feature = "runtime-benchmarks", feature = "std"))]
324
	pub fn benchmark_signature(&self) -> ValidatorSignature {
325
		self.signature.clone()
326
	}
327

            
328
	/// Set the signature. Only should be used for creating testing mocks.
329
	#[cfg(feature = "std")]
330
	pub fn set_signature(&mut self, signature: ValidatorSignature) {
331
		self.signature = signature
332
	}
333
}
334

            
335
impl<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

            
343
/// This helper trait ensures that we can encode `Statement` as `CompactStatement`,
344
/// and anything as itself.
345
///
346
/// This resembles `codec::EncodeLike`, but it's distinct:
347
/// `EncodeLike` is a marker trait which asserts at the typesystem level that
348
/// one type's encoding is a valid encoding for another type. It doesn't
349
/// perform any type conversion when encoding.
350
///
351
/// This trait, on the other hand, provides a method which can be used to
352
/// simultaneously convert and encode one type as another.
353
pub trait EncodeAs<T> {
354
	/// Convert Self into T, then encode T.
355
	///
356
	/// This is useful when T is a subset of Self, reducing encoding costs;
357
	/// its signature also means that we do not need to clone Self in order
358
	/// to retain ownership, as we would if we were to do
359
	/// `self.clone().into().encode()`.
360
	fn encode_as(&self) -> Vec<u8>;
361
}
362

            
363
impl<T: Encode> EncodeAs<T> for T {
364
	fn encode_as(&self) -> Vec<u8> {
365
		self.encode()
366
	}
367
}