1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
//! # Attestations Pallet
//!
//! A pallet with attestations functionality to help developers integrate attestations to their projects.
// We make sure this pallet uses `no_std` for compiling to Wasm.
#![cfg_attr(not(feature = "std"), no_std)]
pub mod schema;
pub use crate::schema::SIZE_STRINGS;
// Re-export pallet items so that they can be accessed from the crate namespace.
pub use pallet::*;
// Ensure the timestamp pallet is included in your runtime.
// use frame_system::ensure_signed;
// use pallet_timestamp::Pallet as TimestampPallet;
// FRAME pallets require their own "mock runtimes" to be able to run unit tests. This module
// contains a mock runtime specific for testing this pallet's functionality.
#[cfg(test)]
mod mock;
// Every callable function or "dispatchable" a pallet exposes must have weight values that correctly
// estimate a dispatchable's execution time. The benchmarking module is used to calculate weights
// for each dispatchable and generates this pallet's weight.rs file. Learn more about benchmarking here: https://docs.substrate.io/test/benchmark/
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
pub mod weights;
pub use weights::*;
// All pallet logic is defined in its own module and must be annotated by the `pallet` attribute.
#[frame_support::pallet]
pub mod pallet {
// Import various useful types required by all FRAME pallets.
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use crate::schema::{Schema, Attestation};
// The `Pallet` struct serves as a placeholder to implement traits, methods and dispatchables
// (`Call`s) in this pallet.
#[pallet::pallet]
#[pallet::without_storage_info]
pub struct Pallet<T>(_);
/// The pallet's configuration trait.
#[pallet::config]
pub trait Config: frame_system::Config {
/// The overarching runtime event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// A type representing the weights required by the dispatchables of this pallet.
type WeightInfo: WeightInfo;
}
// Storage item for this pallet.
//
// In this pallet, we are declaring two storage items called `Scehmas` and `Attestations`
// to store the corresponding data, and two counters for unique ID management.
/// Schema storage for the pallet.
#[pallet::storage]
#[pallet::getter(fn schema)]
pub type Schemas<T: Config> = StorageMap<_, Blake2_128Concat, u32, Schema>;
/// Attestation storage for the pallet.
#[pallet::storage]
#[pallet::getter(fn attestation)]
pub type Attestations<T: Config> = StorageMap<_, Blake2_128Concat, u32, Attestation>;
/// Schema ID counter for unique ID management.
#[pallet::storage]
#[pallet::getter(fn next_schema_id)]
pub type NextSchemaId<T> = StorageValue<_, u32, ValueQuery>;
/// Attestation ID counter for unique ID management.
#[pallet::storage]
#[pallet::getter(fn next_attestation_id)]
pub type NextAttestationId<T> = StorageValue<_, u32, ValueQuery>;
/// Events that functions in this pallet can emit.
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// Event emitted when a schema is inserted. The parameters are the ID of the schema and the account that created it.
SchemaInserted { schema_id: u32, who: T::AccountId },
/// Event emitted when an attestation is inserted. The parameters are the ID of the attestation and the account that created it.
AttestationInserted { attestation_id: u32, who: T::AccountId },
}
/// Errors that can be returned by this pallet.
///
/// Errors tell users that something went wrong so it's important that their naming is
/// informative. Similar to events, error documentation is added to a node's metadata so it's
/// equally important that they have helpful documentation associated with them.
///
/// This type of runtime error can be up to 4 bytes in size should you want to return additional
/// information.
#[pallet::error]
pub enum Error<T> {
/// There was an attempt to insert an attestation with an invalid schema id.
SchemaNotFound,
/// The issuer's account ID is longer than the allowed account id length.
AccountIdTooLong,
/// The block number conversion failed.
BlockNumberConversionFailed,
}
/// The pallet's dispatchable functions ([`Call`]s).
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Function which inserts a schema into the pallet storage.
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::insert_schema())]
pub fn insert_schema(origin: OriginFor<T>, schema: Schema) -> DispatchResult {
let who = ensure_signed(origin)?;
// Check if the counter is already initialized, if not set it to 1
if NextSchemaId::<T>::get() == 0 {
NextSchemaId::<T>::put(1);
}
// Get the next unique schema ID
let schema_id = NextSchemaId::<T>::get();
// Update the schema with the new ID
let mut new_schema = schema;
new_schema.id = schema_id;
// Encode the issuer's account ID into a Vec<u8>
let account_id_as_bytes = who.encode();
// Convert the Vec<u8> to BoundedVec<u8, ConstU32<SIZE_STRINGS>>
let issuer: BoundedVec<u8, ConstU32<SIZE_STRINGS>> = account_id_as_bytes.try_into()
.map_err(|_| Error::<T>::AccountIdTooLong)?;
// Update the schema with the new issuer
new_schema.issuer = issuer;
// Insert or update the schema in the storage map
Schemas::<T>::insert(schema_id, new_schema);
// Increment the ID for the next schema
NextSchemaId::<T>::put(schema_id + 1);
// Emit the SchemaInserted event
Self::deposit_event(Event::SchemaInserted { schema_id, who });
Ok(())
}
/// Function which inserts an attestation into the pallet storage.
///
/// Requires at least one schema to be previously inserted.
#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::insert_attestation())]
pub fn insert_attestation(origin: OriginFor<T>, attestation: Attestation) -> DispatchResult {
let who = ensure_signed(origin)?;
// Check if the counter is already initialized, if not set it to 1
if NextAttestationId::<T>::get() == 0 {
NextAttestationId::<T>::put(1);
}
// Validate that the schemaID exists in the Schemas storage
ensure!(
Schemas::<T>::contains_key(attestation.schema_id),
Error::<T>::SchemaNotFound
);
// Get the next unique attestation ID
let attestation_id = NextAttestationId::<T>::get();
// Retrieve the current block number
let current_block_number = <frame_system::Pallet<T>>::block_number();
// Convert the block number to u32
let current_block_number_u32: u32 = current_block_number.try_into().map_err(|_| Error::<T>::BlockNumberConversionFailed)?;
// Update the attestation with the new ID and block number
let mut new_attestation = attestation;
new_attestation.id = attestation_id;
new_attestation.block_number = current_block_number_u32;
// Encode the issuer's account ID into a Vec<u8>
let account_id_as_bytes = who.encode();
// Convert the Vec<u8> to BoundedVec<u8, ConstU32<SIZE_STRINGS>>
let issuer: BoundedVec<u8, ConstU32<SIZE_STRINGS>> = account_id_as_bytes.try_into()
.map_err(|_| Error::<T>::AccountIdTooLong)?;
// Update the attestation with the new issuer
new_attestation.issuer = issuer;
// Insert the attestation in the storage map
Attestations::<T>::insert(attestation_id, new_attestation);
// Increment the ID for the next attestation
NextAttestationId::<T>::put(attestation_id + 1);
// Emit the AttestationInserted event
Self::deposit_event(Event::AttestationInserted { attestation_id, who });
Ok(())
}
}
}