pink_utils/
attestation.rs

1//! Utilities to create and verify off-chain attestation
2
3use alloc::vec::Vec;
4use core::fmt;
5use pink::chain_extension::{signing, SigType};
6use scale::{Decode, Encode};
7
8/// A signed payload produced by a [`Generator`], and can be validated by [`Verifier`].
9#[derive(Clone, Encode, Decode, Debug, PartialEq, Eq)]
10#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
11pub struct Attestation {
12    pub data: Vec<u8>,
13    pub signature: Vec<u8>,
14    // TODO: add metadata
15}
16
17/// An attestation verifier
18#[derive(Debug, Encode, Decode, Clone)]
19#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
20pub struct Verifier {
21    pub pubkey: Vec<u8>,
22}
23
24impl Verifier {
25    /// Verifies an attestation
26    pub fn verify(&self, attestation: &Attestation) -> bool {
27        signing::verify(
28            &attestation.data,
29            &self.pubkey,
30            &attestation.signature,
31            SigType::Sr25519,
32        )
33    }
34
35    /// Verifies an attestation and decodes the inner data
36    pub fn verify_as<T: Decode>(&self, attestation: &Attestation) -> Option<T> {
37        if !self.verify(attestation) {
38            return None;
39        }
40        Decode::decode(&mut &attestation.data[..]).ok()
41    }
42}
43
44/// An attestation generator
45#[derive(Encode, Decode, Clone)]
46#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
47pub struct Generator {
48    pub privkey: Vec<u8>,
49}
50
51impl Generator {
52    /// Produces a signed attestation with the given `data`
53    pub fn sign<T: Clone + Encode + Decode>(&self, data: T) -> Attestation {
54        let encoded = Encode::encode(&data);
55        let signature = signing::sign(&encoded, &self.privkey, SigType::Sr25519);
56        Attestation {
57            data: encoded,
58            signature,
59        }
60    }
61}
62
63impl fmt::Debug for Generator {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        // We don't want to leak the privkey to anyone
66        write!(f, "Generator")
67    }
68}
69
70/// Creates a pair of attestation utility to do off-chain attestation
71pub fn create(salt: &[u8]) -> (Generator, Verifier) {
72    let privkey = signing::derive_sr25519_key(salt);
73    let pubkey = signing::get_public_key(&privkey, SigType::Sr25519);
74    (Generator { privkey }, Verifier { pubkey })
75}
76
77#[cfg(test)]
78mod test {
79    use super::*;
80
81    #[ink::test]
82    fn it_works() {
83        pink_chain_extension::mock_ext::mock_all_ext();
84
85        // Generate an attestation and verify it later
86        #[derive(Clone, Encode, Decode, scale_info::TypeInfo)]
87        struct SomeData {
88            x: u32,
89        }
90
91        let (generator, verifier) = create(b"salt");
92        let attestation = generator.sign(SomeData { x: 123 });
93        assert!(verifier.verify(&attestation));
94    }
95}