pink_attestation/
lib.rs

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