diff --git a/pallets/core/src/common/authorization.rs b/pallets/core/src/common/authorization.rs new file mode 100644 index 000000000..28602b25c --- /dev/null +++ b/pallets/core/src/common/authorization.rs @@ -0,0 +1,58 @@ +use crate::{common::Signature, did, util::Action}; +use codec::Encode; + +use super::ToStateChange; + +/// Authorizes action performed by `Self` over supplied target using given key. +pub trait AuthorizeTarget { + fn ensure_authorizes_target( + &self, + _: &Key, + _: &A, + ) -> Result<(), crate::did::Error> + where + A: Action, + { + Ok(()) + } +} + +type AuthorizationResult = Result< + Option::Signer, ::Key>>, + crate::did::Error, +>; + +/// Authorizes signed action. +pub trait AuthorizeSignedAction: Signature +where + Self::Signer: AuthorizeTarget, +{ + fn authorizes_signed_action( + &self, + action: &A, + ) -> AuthorizationResult + where + A: ToStateChange, + { + let signer_pubkey = self.key::().ok_or(did::Error::::NoKeyForDid)?; + let encoded_state_change = action.to_state_change().encode(); + + self.signer() + .ensure_authorizes_target(&signer_pubkey, action)?; + + self.verify_raw_bytes(&encoded_state_change, &signer_pubkey) + .map_err(Into::into) + .map(|yes| { + yes.then(|| Authorization { + signer: self.signer(), + key: signer_pubkey, + }) + }) + } +} + +/// Successfully authorized signer along with its key. +pub struct Authorization { + pub signer: S, + pub key: K, +} diff --git a/pallets/core/src/common/mod.rs b/pallets/core/src/common/mod.rs index 6ac91114e..37a3db742 100644 --- a/pallets/core/src/common/mod.rs +++ b/pallets/core/src/common/mod.rs @@ -1,17 +1,21 @@ use core::marker::PhantomData; +pub mod authorization; pub mod keys; pub mod limits; pub mod policy; pub mod signatures; +pub mod signed_action; pub mod state_change; pub mod storage_version; pub mod types; +pub use authorization::*; pub use keys::*; pub use limits::*; pub use policy::*; pub use signatures::*; +pub use signed_action::*; pub use state_change::*; pub use storage_version::*; pub use types::*; diff --git a/pallets/core/src/common/policy.rs b/pallets/core/src/common/policy.rs index 0b8a486ca..6fcef5be4 100644 --- a/pallets/core/src/common/policy.rs +++ b/pallets/core/src/common/policy.rs @@ -7,12 +7,10 @@ use super::{Limits, ToStateChange}; use crate::util::btree_set; use crate::{ + common::{AuthorizeTarget, Signature}, did, - did::{ - AuthorizeTarget, Did, DidKey, DidMethodKey, DidOrDidMethodKey, DidOrDidMethodKeySignature, - Signed, SignedActionWithNonce, - }, - util::{Action, ActionWithNonce, NonceError, UpdateWithNonceError, WithNonce}, + did::{Did, DidKey, DidMethodKey, DidOrDidMethodKey, DidOrDidMethodKeySignature}, + util::{Action, ActionExecutionError, ActionWithNonce, NonceError, StorageRef, WithNonce}, }; use alloc::vec::Vec; use codec::{Decode, Encode, MaxEncodedLen}; @@ -121,72 +119,24 @@ impl Policy { pub fn is_empty(&self) -> bool { self.len() == 0 } - - /// Executes action over target data providing a mutable reference if all checks succeed. - /// - /// Unlike `try_exec_action_over_data`, this action may result in a removal of a data, if the value under option - /// will be taken. - /// - /// Checks: - /// 1. Verify that `proof` authorizes `action` according to `policy`. - /// 2. Verify that the action is not a replayed payload by ensuring each provided controller nonce equals the last nonce plus 1. - /// - /// Returns a mutable reference to the underlying data wrapped into an option if the command is authorized, - /// otherwise returns Err. - pub fn try_exec_removable_action( - entity: &mut Option, - f: F, - action: S, - proof: Vec>, - ) -> Result - where - T: crate::did::Config, - V: HasPolicy, - F: FnOnce(S, &mut Option) -> Result, - WithNonce: ActionWithNonce + ToStateChange, - Did: AuthorizeTarget< as Action>::Target, DidKey>, - DidMethodKey: AuthorizeTarget< as Action>::Target, DidMethodKey>, - E: From - + From> - + From - + From, - { - // check the signer set satisfies policy - match entity - .as_ref() - .ok_or(PolicyExecutionError::NoEntity)? - .policy() - { - Policy::OneOf(controllers) => { - ensure!( - proof.len() == 1 && controllers.contains(&proof[0].data().signer()), - PolicyExecutionError::NotAuthorized - ); - } - } - - rec_update(action, entity, f, &mut proof.into_iter()) - } } fn rec_update( action: A, - data: &mut Option, - f: impl FnOnce(A, &mut Option) -> Result, + data: &mut D, + f: impl FnOnce(A, &mut D) -> Result, proof: &mut impl Iterator>, ) -> Result where - E: From + From + From>, + E: From + From + From>, WithNonce: ActionWithNonce + ToStateChange, - Did: AuthorizeTarget< as Action>::Target, DidKey>, - DidMethodKey: AuthorizeTarget< as Action>::Target, DidMethodKey>, + as Action>::Target: StorageRef, { if let Some(sig_with_nonce) = proof.next() { let action_with_nonce = WithNonce::new_with_nonce(action, sig_with_nonce.nonce); - let signed_action = - SignedActionWithNonce::new(action_with_nonce, sig_with_nonce.into_data()); + let signed_action = action_with_nonce.signed(sig_with_nonce.into_data()); - signed_action.execute(|action, _| rec_update(action.into_data(), data, f, proof)) + signed_action.execute(|action, _, _| rec_update(action.into_data(), data, f, proof)) } else { f(action, data) } @@ -209,9 +159,93 @@ impl AuthorizeTarget for PolicyExecutor {} pub type DidSignatureWithNonce = WithNonce>; /// Denotes an entity which has an associated `Policy`. -pub trait HasPolicy { +pub trait HasPolicy: Sized { /// Returns underlying `Policy`. fn policy(&self) -> &Policy; + + /// Executes action over target data providing a mutable reference if all checks succeed. + /// + /// Unlike `try_exec_action_over_data`, this action may result in a removal of a data, if the value under option + /// will be taken. + /// + /// Checks: + /// 1. Verify that `proof` authorizes `action` according to `policy`. + /// 2. Verify that the action is not a replayed payload by ensuring each provided controller nonce equals the last nonce plus 1. + /// + /// Returns a mutable reference to the underlying data wrapped into an option if the command is authorized, + /// otherwise returns Err. + fn execute( + &mut self, + f: F, + action: A, + proof: Vec>, + ) -> Result + where + T: crate::did::Config, + F: FnOnce(A, &mut Self) -> Result, + WithNonce: ActionWithNonce + ToStateChange, + as Action>::Target: StorageRef, + E: From + + From> + + From + + From, + { + // check the signer set satisfies policy + match self.policy() { + Policy::OneOf(controllers) => { + ensure!( + proof.len() == 1 && controllers.contains(&proof[0].data().signer()), + PolicyExecutionError::NotAuthorized + ); + } + } + + rec_update(action, self, f, &mut proof.into_iter()) + } + + /// Executes action over target data providing a mutable reference if all checks succeed. + /// + /// Unlike `try_exec_action_over_data`, this action may result in a removal of a data, if the value under option + /// will be taken. + /// + /// Checks: + /// 1. Verify that `proof` authorizes `action` according to `policy`. + /// 2. Verify that the action is not a replayed payload by ensuring each provided controller nonce equals the last nonce plus 1. + /// + /// Returns a mutable reference to the underlying data wrapped into an option if the command is authorized, + /// otherwise returns Err. + fn execute_removable( + this_opt: &mut Option, + f: F, + action: A, + proof: Vec>, + ) -> Result + where + T: crate::did::Config, + F: FnOnce(A, &mut Option) -> Result, + WithNonce: ActionWithNonce + ToStateChange, + as Action>::Target: StorageRef, + E: From + + From> + + From + + From, + { + // check the signer set satisfies policy + match this_opt + .as_ref() + .ok_or(PolicyExecutionError::NoEntity)? + .policy() + { + Policy::OneOf(controllers) => { + ensure!( + proof.len() == 1 && controllers.contains(&proof[0].data().signer()), + PolicyExecutionError::NotAuthorized + ); + } + } + + rec_update(action, this_opt, f, &mut proof.into_iter()) + } } /// Authorization logic containing rules to modify some data entity. diff --git a/pallets/core/src/common/signatures.rs b/pallets/core/src/common/signatures.rs index a409af8ee..d955035c4 100644 --- a/pallets/core/src/common/signatures.rs +++ b/pallets/core/src/common/signatures.rs @@ -10,6 +10,18 @@ use sp_core::{ed25519, sr25519, Pair}; use sp_runtime::traits::Verify; use sp_std::{borrow::Borrow, convert::TryInto}; +/// Signature entity. +pub trait Signature: Sized { + type Signer: Clone; + type Key; + + fn signer(&self) -> Self::Signer; + + fn key(&self) -> Option; + + fn verify_raw_bytes(&self, message: &[u8], key: &Self::Key) -> Result; +} + #[derive(PartialEq, Eq, Encode, Decode, Clone, Debug, Default)] pub struct SigTypes { sr: V, @@ -125,7 +137,7 @@ impl DidMethodKeySigValue { let p = libsecp256k1::PublicKey::parse_compressed(pk_bytes).unwrap(); libsecp256k1::verify(&m, &sig, &p) } - _ => todo!(), //Err(VerificationError::IncompatibleKey(public_key.clone()))?, + _ => Err(VerificationError::IncompatibleKey)?, }; Ok(result) @@ -166,7 +178,7 @@ pub enum SigValue { #[derive(Debug, Clone, PartialEq, Eq)] pub enum VerificationError { - IncompatibleKey(PublicKey), + IncompatibleKey, } impl SigValue { @@ -223,7 +235,7 @@ impl SigValue { let p = libsecp256k1::PublicKey::parse_compressed(pk_bytes).unwrap(); libsecp256k1::verify(&m, &sig, &p) } - _ => Err(VerificationError::IncompatibleKey(*public_key))?, + _ => Err(VerificationError::IncompatibleKey)?, }; Ok(result) diff --git a/pallets/core/src/common/signed_action.rs b/pallets/core/src/common/signed_action.rs new file mode 100644 index 000000000..2cc5fb17c --- /dev/null +++ b/pallets/core/src/common/signed_action.rs @@ -0,0 +1,51 @@ +use crate::{ + common::{Authorization, AuthorizeSignedAction, AuthorizeTarget, ToStateChange}, + did::*, + util::{action::*, with_nonce::*, WrappedActionWithNonce}, +}; +use core::ops::Deref; + +impl SignedActionWithNonce +where + A: ActionWithNonce + ToStateChange, + Sig: AuthorizeSignedAction, + Sig::Signer: AuthorizeTarget + Deref, +{ + /// Verifies signer's signature and nonce, then executes given action providing a mutable reference to the + /// value associated with the target. + /// In case of a successful result, commits all storage changes and increases the signer's nonce. + pub fn execute(self, f: F) -> Result + where + F: FnOnce(A, &mut >::Value, Sig::Signer) -> Result, + E: From + From + From>, + A::Target: StorageRef, + ::Target: StorageRef> + Clone, + { + self.execute_removable(|action, data, actor| f(action, data.as_mut().unwrap(), actor)) + } + + /// Verifies signer's signature and nonce, then executes given action providing a mutable reference to the + /// option containing a value associated with the target. + /// In case of a successful result, commits all storage changes and increases the signer's nonce. + pub fn execute_removable(self, f: F) -> Result + where + F: FnOnce(A, &mut Option<>::Value>, Sig::Signer) -> Result, + E: From + From + From>, + A::Target: StorageRef, + ::Target: StorageRef> + Clone, + { + let SignedActionWithNonce { + action, signature, .. + } = self; + + let Authorization { signer, .. } = signature + .authorizes_signed_action(&action)? + .ok_or(Error::::InvalidSignature)?; + + WrappedActionWithNonce::::new(action.nonce(), (*signer).clone(), action) + .execute_and_increase_nonce(|WrappedActionWithNonce { action, .. }, _| { + action.execute_removable(|action, target_data| f(action, target_data, signer)) + }) + .map_err(Into::into) + } +} diff --git a/pallets/core/src/common/types.rs b/pallets/core/src/common/types.rs index a19d35478..8f7b09f05 100644 --- a/pallets/core/src/common/types.rs +++ b/pallets/core/src/common/types.rs @@ -1,8 +1,7 @@ use codec::{Decode, Encode, MaxEncodedLen}; use core::fmt::Debug; -use frame_support::pallet_prelude::*; -use scale_info::TypeInfo; -use sp_runtime::traits::*; + +pub use crate::util::Types; #[derive( Encode, Decode, Copy, scale_info_derive::TypeInfo, Clone, PartialEq, Eq, Debug, MaxEncodedLen, @@ -13,34 +12,3 @@ pub enum CurveType { /// BLS12-381 Bls12381, } - -/// Defines associated types used by `dock-core`. -pub trait Types: Clone + Eq { - type BlockNumber: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + AtLeast32BitUnsigned - + Default - + Bounded - + Copy - + sp_std::hash::Hash - + sp_std::str::FromStr - + MaybeMallocSizeOf - + MaxEncodedLen - + TypeInfo; - - type AccountId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + Ord - + MaxEncodedLen; -} - -impl Types for T { - type BlockNumber = T::BlockNumber; - type AccountId = T::AccountId; -} diff --git a/pallets/core/src/modules/accumulator/actions.rs b/pallets/core/src/modules/accumulator/actions.rs index d8343d6da..9d8227c83 100644 --- a/pallets/core/src/modules/accumulator/actions.rs +++ b/pallets/core/src/modules/accumulator/actions.rs @@ -108,9 +108,9 @@ pub struct UpdateAccumulator { } crate::impl_action_with_nonce! { - for (): - UpdateAccumulator with 1 as len, () as target, - RemoveAccumulator with 1 as len, () as target + for AccumulatorId: + UpdateAccumulator with 1 as len, id as target, + RemoveAccumulator with 1 as len, id as target } crate::impl_action_with_nonce! { diff --git a/pallets/core/src/modules/accumulator/benchmarks.rs b/pallets/core/src/modules/accumulator/benchmarks.rs index cabb6c8e0..2e740bd0a 100644 --- a/pallets/core/src/modules/accumulator/benchmarks.rs +++ b/pallets/core/src/modules/accumulator/benchmarks.rs @@ -2,7 +2,7 @@ use super::*; use crate::{ common::state_change::ToStateChange, did::{DidSignature, UncheckedDidKey}, - util::IncId, + util::{Action, IncId}, }; use frame_benchmarking::{benchmarks, whitelisted_caller}; use frame_system::RawOrigin; @@ -62,17 +62,14 @@ crate::bench_with_all_pairs! { Default::default(), ).unwrap(); - Pallet::::add_params_( - AddAccumulatorParams { - params: AccumulatorParameters { - curve_type: CurveType::Bls12381, - bytes: vec![3; MAX_PARAMS as usize].try_into().unwrap(), - label: Some(vec![1; MAX_LABEL as usize].try_into().unwrap()), - }, - nonce: 1u8.into() + WrappedActionWithNonce::::new(1u8.into(), AccumulatorOwner(did.into()), AddAccumulatorParams { + params: AccumulatorParameters { + curve_type: CurveType::Bls12381, + bytes: vec![3; MAX_PARAMS as usize].try_into().unwrap(), + label: Some(vec![1; MAX_LABEL as usize].try_into().unwrap()), }, - AccumulatorOwner(did.into()) - ).unwrap(); + nonce: 1u8.into() + }).execute::(|action, counter| Pallet::::add_params_(action.action, counter, AccumulatorOwner(did.into()))).unwrap(); let rem_params = RemoveAccumulatorParams { params_ref: (AccumulatorOwner(did.into()), 1u8.try_into().unwrap()), @@ -101,17 +98,14 @@ crate::bench_with_all_pairs! { Default::default(), ).unwrap(); - Pallet::::add_params_( - AddAccumulatorParams { - params: AccumulatorParameters { - curve_type: CurveType::Bls12381, - bytes: vec![3; MAX_PARAMS as usize].try_into().unwrap(), - label: Some(vec![1; MAX_LABEL as usize].try_into().unwrap()), - }, - nonce: 1u8.into() + WrappedActionWithNonce::::new(1u8.into(), AccumulatorOwner(did.into()), AddAccumulatorParams { + params: AccumulatorParameters { + curve_type: CurveType::Bls12381, + bytes: vec![3; MAX_PARAMS as usize].try_into().unwrap(), + label: Some(vec![1; MAX_LABEL as usize].try_into().unwrap()), }, - AccumulatorOwner(did.into()) - ).unwrap(); + nonce: 1u8.into() + },).execute::(|action, counter| Pallet::::add_params_(action.action, counter, AccumulatorOwner(did.into()))).unwrap(); let public_key = AccumulatorPublicKey { curve_type: CurveType::Bls12381, @@ -144,19 +138,22 @@ crate::bench_with_all_pairs! { Default::default(), ).unwrap(); - Pallet::::add_params_( - AddAccumulatorParams { - params: AccumulatorParameters { - curve_type: CurveType::Bls12381, - bytes: vec![3; MAX_PARAMS as usize].try_into().unwrap(), - label: Some(vec![1; MAX_LABEL as usize].try_into().unwrap()), - }, - nonce: 1u8.into() + WrappedActionWithNonce::::new(1u8.into(), AccumulatorOwner(did.into()), AddAccumulatorParams { + params: AccumulatorParameters { + curve_type: CurveType::Bls12381, + bytes: vec![3; MAX_PARAMS as usize].try_into().unwrap(), + label: Some(vec![1; MAX_LABEL as usize].try_into().unwrap()), + }, + nonce: 1u8.into() + }).execute::( + |WrappedActionWithNonce { action, .. }, accumulator| { + Pallet::::add_params_(action, accumulator, AccumulatorOwner(did.into())) }, - AccumulatorOwner(did.into()) ).unwrap(); - Pallet::::add_public_key_( + WrappedActionWithNonce::::new( + 1u8.into(), + AccumulatorOwner(did.into()), AddAccumulatorPublicKey { public_key: AccumulatorPublicKey { curve_type: CurveType::Bls12381, @@ -165,8 +162,10 @@ crate::bench_with_all_pairs! { params_ref: Some((AccumulatorOwner(did.into()), IncId::from(1u8))) }, nonce: 1u8.into() - }, - AccumulatorOwner(did.into()) + }).execute::( + |WrappedActionWithNonce { action, .. }, accumulator| { + Pallet::::add_public_key_(action, accumulator, AccumulatorOwner(did.into())) + } ).unwrap(); let rem_key = RemoveAccumulatorPublicKey { @@ -203,20 +202,22 @@ crate::bench_with_all_pairs! { let acc_id: AccumulatorId = AccumulatorId([1; 32]); - Pallet::::add_params_( - AddAccumulatorParams { - params: AccumulatorParameters { - curve_type: CurveType::Bls12381, - bytes: vec![3; MAX_PARAMS as usize].try_into().unwrap(), - label: Some(vec![1; MAX_LABEL as usize].try_into().unwrap()), - }, - nonce: 1u8.into() + WrappedActionWithNonce::::new(1u8.into(), AccumulatorOwner(did.into()), AddAccumulatorParams { + params: AccumulatorParameters { + curve_type: CurveType::Bls12381, + bytes: vec![3; MAX_PARAMS as usize].try_into().unwrap(), + label: Some(vec![1; MAX_LABEL as usize].try_into().unwrap()), + }, + nonce: 1u8.into() + }).execute::( + |WrappedActionWithNonce { action, .. }, accumulator| { + Pallet::::add_params_(action, accumulator, AccumulatorOwner(did.into())) }, - AccumulatorOwner(did.into()) ).unwrap(); - - Pallet::::add_public_key_( + WrappedActionWithNonce::::new( + 1u8.into(), + AccumulatorOwner(did.into()), AddAccumulatorPublicKey { public_key: AccumulatorPublicKey { curve_type: CurveType::Bls12381, @@ -225,8 +226,10 @@ crate::bench_with_all_pairs! { params_ref: Some((AccumulatorOwner(did.into()), IncId::from(1u8))) }, nonce: 1u8.into() - }, - AccumulatorOwner(did.into()) + }).execute::( + |WrappedActionWithNonce { action, .. }, accumulator| { + Pallet::::add_public_key_(action, accumulator, AccumulatorOwner(did.into())) + } ).unwrap(); let add_acc = AddAccumulator { @@ -269,7 +272,9 @@ crate::bench_with_all_pairs! { let acc_id: AccumulatorId = AccumulatorId([1; 32]); - Pallet::::add_params_( + WrappedActionWithNonce::::new( + 1u8.into(), + AccumulatorOwner(did.into()), AddAccumulatorParams { params: AccumulatorParameters { curve_type: CurveType::Bls12381, @@ -277,12 +282,13 @@ crate::bench_with_all_pairs! { label: Some(vec![1; MAX_LABEL as usize].try_into().unwrap()), }, nonce: 1u8.into() - }, - AccumulatorOwner(did.into()) - ).unwrap(); + } + ).execute::(|action, counters| Pallet::::add_params_(action.action, counters, AccumulatorOwner(did.into()))).unwrap(); - Pallet::::add_public_key_( + WrappedActionWithNonce::::new( + 1u8.into(), + AccumulatorOwner(did.into()), AddAccumulatorPublicKey { public_key: AccumulatorPublicKey { curve_type: CurveType::Bls12381, @@ -291,18 +297,14 @@ crate::bench_with_all_pairs! { params_ref: Some((AccumulatorOwner(did.into()), IncId::from(1u8))) }, nonce: 1u8.into() - }, - AccumulatorOwner(did.into()) - ).unwrap(); - Pallet::::add_accumulator_( - AddAccumulator { - id: acc_id, - accumulator, - nonce: 1u8.into() - }, - AccumulatorOwner(did.into()) - ).unwrap(); + } + ).execute::(|action, counters| Pallet::::add_public_key_(action.action, counters, AccumulatorOwner(did.into()))).unwrap(); + AddAccumulator { + id: acc_id, + accumulator, + nonce: 1u8.into() + }.execute::(|action, counters| Pallet::::add_accumulator_(action, counters, AccumulatorOwner(did.into()))).unwrap(); let new_accumulated = vec![3; a as usize]; let up_acc = UpdateAccumulator { @@ -340,7 +342,10 @@ crate::bench_with_all_pairs! { let acc_id: AccumulatorId = AccumulatorId([2; 32]); - Pallet::::add_params_( + + WrappedActionWithNonce::::new( + 1u8.into(), + AccumulatorOwner(did.into()), AddAccumulatorParams { params: AccumulatorParameters { curve_type: CurveType::Bls12381, @@ -348,12 +353,13 @@ crate::bench_with_all_pairs! { label: Some(vec![1; MAX_LABEL as usize].try_into().unwrap()), }, nonce: 1u8.into() - }, - AccumulatorOwner(did.into()) - ).unwrap(); + } + ).execute::(|action, counters| Pallet::::add_params_(action.action, counters, AccumulatorOwner(did.into()))).unwrap(); - Pallet::::add_public_key_( + WrappedActionWithNonce::::new( + 1u8.into(), + AccumulatorOwner(did.into()), AddAccumulatorPublicKey { public_key: AccumulatorPublicKey { curve_type: CurveType::Bls12381, @@ -362,18 +368,14 @@ crate::bench_with_all_pairs! { params_ref: Some((AccumulatorOwner(did.into()), IncId::from(1u8))) }, nonce: 1u8.into() - }, - AccumulatorOwner(did.into()) - ).unwrap(); + } + ).execute::(|action, counters| Pallet::::add_public_key_(action.action, counters, AccumulatorOwner(did.into()))).unwrap(); - Pallet::::add_accumulator_( - AddAccumulator { - id: acc_id, - accumulator, - nonce: 1u8.into() - }, - AccumulatorOwner(did.into()) - ).unwrap(); + AddAccumulator { + id: acc_id, + accumulator, + nonce: 1u8.into() + }.execute::(|action, counters| Pallet::::add_accumulator_(action, counters, AccumulatorOwner(did.into()))).unwrap(); let remove_acc = RemoveAccumulator { id: acc_id, diff --git a/pallets/core/src/modules/accumulator/impl.rs b/pallets/core/src/modules/accumulator/impl.rs index 4ddc2a85f..6326d80de 100644 --- a/pallets/core/src/modules/accumulator/impl.rs +++ b/pallets/core/src/modules/accumulator/impl.rs @@ -4,18 +4,18 @@ use crate::deposit_indexed_event; impl Pallet { pub(super) fn add_params_( AddAccumulatorParams { params, .. }: AddAccumulatorParams, + StoredAccumulatorOwnerCounters { params_counter, .. }: &mut StoredAccumulatorOwnerCounters, owner: AccumulatorOwner, ) -> DispatchResult { - let params_counter = - AccumulatorOwnerCounters::::mutate(owner, |counters| *counters.params_counter.inc()); - AccumulatorParams::::insert(owner, params_counter, params); + AccumulatorParams::::insert(owner, params_counter.inc(), params); - Self::deposit_event(Event::ParamsAdded(owner, params_counter)); + Self::deposit_event(Event::ParamsAdded(owner, *params_counter)); Ok(()) } pub(super) fn add_public_key_( AddAccumulatorPublicKey { public_key, .. }: AddAccumulatorPublicKey, + StoredAccumulatorOwnerCounters { key_counter, .. }: &mut StoredAccumulatorOwnerCounters, owner: AccumulatorOwner, ) -> DispatchResult { if let Some((acc_owner, params_id)) = public_key.params_ref { @@ -25,11 +25,9 @@ impl Pallet { ); } - let keys_counter = - AccumulatorOwnerCounters::::mutate(owner, |counters| *counters.key_counter.inc()); - AccumulatorKeys::insert(owner, keys_counter, public_key); + AccumulatorKeys::insert(owner, key_counter.inc(), public_key); - Self::deposit_event(Event::KeyAdded(owner, keys_counter)); + Self::deposit_event(Event::KeyAdded(owner, *key_counter)); Ok(()) } @@ -38,6 +36,7 @@ impl Pallet { params_ref: (did, counter), .. }: RemoveAccumulatorParams, + _: &mut StoredAccumulatorOwnerCounters, owner: AccumulatorOwner, ) -> DispatchResult { // Only the DID that added the param can remove it @@ -58,6 +57,7 @@ impl Pallet { key_ref: (did, counter), .. }: RemoveAccumulatorPublicKey, + _: &mut StoredAccumulatorOwnerCounters, owner: AccumulatorOwner, ) -> DispatchResult { ensure!(did == owner, Error::::NotAccumulatorOwner); @@ -76,6 +76,7 @@ impl Pallet { AddAccumulator { id, accumulator, .. }: AddAccumulator, + (): &mut (), owner: AccumulatorOwner, ) -> DispatchResult { ensure!( @@ -108,27 +109,20 @@ impl Pallet { new_accumulated, .. }: UpdateAccumulator, + accumulator: &mut AccumulatorWithUpdateInfo, owner: AccumulatorOwner, ) -> DispatchResult { - Accumulators::::try_mutate(id, |accumulator| -> DispatchResult { - let accumulator = accumulator - .as_mut() - .ok_or(Error::::AccumulatorDoesntExist)?; - - // Only the DID that added the accumulator can update it - ensure!( - *accumulator.accumulator.owner_did() == owner, - Error::::NotAccumulatorOwner - ); - - accumulator - .accumulator - .set_new_accumulated(new_accumulated.clone().0) - .map_err(|_| Error::::AccumulatedTooBig)?; - accumulator.last_updated_at = >::block_number(); + // Only the DID that added the accumulator can update it + ensure!( + *accumulator.accumulator.owner_did() == owner, + Error::::NotAccumulatorOwner + ); - Ok(()) - })?; + accumulator + .accumulator + .set_new_accumulated(new_accumulated.clone().0) + .map_err(|_| Error::::AccumulatedTooBig)?; + accumulator.last_updated_at = >::block_number(); // The event stores only the accumulated value which can be used by the verifier. // For witness update, that information is retrieved by looking at the block and parsing the extrinsic. @@ -138,9 +132,10 @@ impl Pallet { pub(super) fn remove_accumulator_( RemoveAccumulator { id, .. }: RemoveAccumulator, + accumulator: &mut Option>, signer: AccumulatorOwner, ) -> DispatchResult { - let accumulator = Accumulators::::get(id).ok_or(Error::::AccumulatorDoesntExist)?; + let accumulator = accumulator.take().unwrap(); // Only the DID that added the accumulator can remove it ensure!( diff --git a/pallets/core/src/modules/accumulator/mod.rs b/pallets/core/src/modules/accumulator/mod.rs index 480cb66b3..b9adb180b 100644 --- a/pallets/core/src/modules/accumulator/mod.rs +++ b/pallets/core/src/modules/accumulator/mod.rs @@ -1,8 +1,8 @@ use crate::{ - common::{self, signatures::ForSigType, CurveType}, + common::{self, signatures::ForSigType, CurveType, Signature}, did, - did::{Did, DidOrDidMethodKeySignature, SignedActionWithNonce}, - util::{Bytes, IncId}, + did::{Did, DidOrDidMethodKeySignature}, + util::{ActionWithNonce, Bytes, IncId, WrappedActionWithNonce}, }; pub use actions::*; use codec::{Decode, Encode, MaxEncodedLen}; @@ -58,7 +58,6 @@ pub mod pallet { ParamsDontExist, PublicKeyDoesntExist, AccumulatedTooBig, - AccumulatorDoesntExist, AccumulatorAlreadyExists, NotPublicKeyOwner, NotAccumulatorOwner, @@ -154,7 +153,13 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(params, signature).execute(Self::add_params_) + WrappedActionWithNonce::new(params.nonce(), signature.signer(), params) + .signed(signature) + .execute( + |WrappedActionWithNonce { action, .. }, accumulator, accumulator_owner| { + Self::add_params_(action, accumulator, accumulator_owner) + }, + ) } #[pallet::weight(SubstrateWeight::::add_public(public_key, signature))] @@ -165,7 +170,13 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(public_key, signature).execute(Self::add_public_key_) + WrappedActionWithNonce::new(public_key.nonce(), signature.signer(), public_key) + .signed(signature) + .execute( + |WrappedActionWithNonce { action, .. }, accumulator, accumulator_owner| { + Self::add_public_key_(action, accumulator, accumulator_owner) + }, + ) } #[pallet::weight(SubstrateWeight::::remove_params(remove, signature))] @@ -176,7 +187,13 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(remove, signature).execute(Self::remove_params_) + WrappedActionWithNonce::new(remove.nonce(), signature.signer(), remove) + .signed(signature) + .execute( + |WrappedActionWithNonce { action, .. }, accumulator, accumulator_owner| { + Self::remove_params_(action, accumulator, accumulator_owner) + }, + ) } #[pallet::weight(SubstrateWeight::::remove_public(remove, signature))] @@ -187,7 +204,13 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(remove, signature).execute(Self::remove_public_key_) + WrappedActionWithNonce::new(remove.nonce(), signature.signer(), remove) + .signed(signature) + .execute( + |WrappedActionWithNonce { action, .. }, accumulator, accumulator_owner| { + Self::remove_public_key_(action, accumulator, accumulator_owner) + }, + ) } /// Add a new accumulator with the initial accumulated value. Each accumulator has a unique id and it @@ -203,7 +226,9 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(add_accumulator, signature).execute(Self::add_accumulator_) + add_accumulator + .signed(signature) + .execute(Self::add_accumulator_) } /// Update an existing accumulator. The update contains the new accumulated value, the updates themselves @@ -219,7 +244,7 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(update, signature).execute(Self::update_accumulator_) + update.signed(signature).execute(Self::update_accumulator_) } #[pallet::weight(SubstrateWeight::::remove_accumulator(remove, signature))] @@ -230,7 +255,9 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(remove, signature).execute(Self::remove_accumulator_) + remove + .signed(signature) + .execute_removable(Self::remove_accumulator_) } } diff --git a/pallets/core/src/modules/accumulator/tests.rs b/pallets/core/src/modules/accumulator/tests.rs index b38070c13..7fd396160 100644 --- a/pallets/core/src/modules/accumulator/tests.rs +++ b/pallets/core/src/modules/accumulator/tests.rs @@ -1,5 +1,5 @@ use super::*; -use crate::tests::common::*; +use crate::{tests::common::*, util::ActionExecutionError}; use frame_support::assert_err; use sp_core::{Hasher, H256}; @@ -146,7 +146,7 @@ crate::did_or_did_method_key! { let sig = did_sig(&update_accum, &author_kp, author, 1); assert_err!( AccumMod::update_accumulator(Origin::signed(1), update_accum.clone(), sig), - Error::::AccumulatorDoesntExist + ActionExecutionError::NoEntity ); update_accum.id = id; diff --git a/pallets/core/src/modules/accumulator/types.rs b/pallets/core/src/modules/accumulator/types.rs index 93ce643b9..c90bae94f 100644 --- a/pallets/core/src/modules/accumulator/types.rs +++ b/pallets/core/src/modules/accumulator/types.rs @@ -2,9 +2,9 @@ use frame_support::{CloneNoBound, DebugNoBound, EqNoBound, PartialEqNoBound}; use super::*; use crate::{ - common::{Limits, TypesAndLimits}, - did::{AuthorizeTarget, DidKey, DidMethodKey, DidOrDidMethodKey}, - util::BoundedBytes, + common::{AuthorizeTarget, Limits, TypesAndLimits}, + did::{DidKey, DidMethodKey, DidOrDidMethodKey}, + util::{BoundedBytes, OptionExt, StorageRef}, }; pub type AccumParametersStorageKey = (AccumulatorOwner, IncId); @@ -32,9 +32,35 @@ pub struct AccumulatorOwner(pub DidOrDidMethodKey); crate::impl_wrapper!(AccumulatorOwner(DidOrDidMethodKey)); +impl StorageRef for AccumulatorId { + type Value = AccumulatorWithUpdateInfo; + + fn try_mutate_associated(self, f: F) -> Result + where + F: FnOnce(&mut Option>) -> Result, + { + Accumulators::::try_mutate_exists(self, f) + } +} + +impl AuthorizeTarget for AccumulatorOwner {} +impl AuthorizeTarget for AccumulatorOwner {} +impl AuthorizeTarget for AccumulatorOwner {} +impl AuthorizeTarget for AccumulatorOwner {} impl AuthorizeTarget<(), DidKey> for AccumulatorOwner {} impl AuthorizeTarget<(), DidMethodKey> for AccumulatorOwner {} +impl StorageRef for AccumulatorOwner { + type Value = StoredAccumulatorOwnerCounters; + + fn try_mutate_associated(self, f: F) -> Result + where + F: FnOnce(&mut Option) -> Result, + { + AccumulatorOwnerCounters::::try_mutate_exists(self, |entry| f(entry.initialized())) + } +} + #[derive( scale_info_derive::TypeInfo, Encode, diff --git a/pallets/core/src/modules/attest/mod.rs b/pallets/core/src/modules/attest/mod.rs index 385c8740e..d9ff32c5b 100644 --- a/pallets/core/src/modules/attest/mod.rs +++ b/pallets/core/src/modules/attest/mod.rs @@ -3,12 +3,9 @@ //! method by specifying an Iri. use crate::{ - common::{signatures::ForSigType, Limits, TypesAndLimits}, - did::{ - self, AuthorizeTarget, DidKey, DidMethodKey, DidOrDidMethodKey, DidOrDidMethodKeySignature, - SignedActionWithNonce, - }, - util::BoundedBytes, + common::{signatures::ForSigType, AuthorizeTarget, Limits, Signature, TypesAndLimits}, + did::{self, DidKey, DidMethodKey, DidOrDidMethodKey, DidOrDidMethodKeySignature}, + util::{ActionWithNonce, BoundedBytes, OptionExt, StorageRef, WrappedActionWithNonce}, }; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ @@ -37,8 +34,8 @@ pub type Iri = BoundedBytes<::MaxIriSize>; #[scale_info(omit_prefix)] pub struct Attester(pub DidOrDidMethodKey); -impl AuthorizeTarget<(), DidKey> for Attester {} -impl AuthorizeTarget<(), DidMethodKey> for Attester {} +impl AuthorizeTarget for Attester {} +impl AuthorizeTarget for Attester {} crate::impl_wrapper!(Attester(DidOrDidMethodKey)); @@ -67,6 +64,17 @@ pub struct Attestation { pub iri: Option>, } +impl StorageRef for Attester { + type Value = Attestation; + + fn try_mutate_associated(self, f: F) -> Result + where + F: FnOnce(&mut Option>) -> Result, + { + Attestations::::try_mutate_exists(self, |entry| f(entry.initialized())) + } +} + #[derive( Encode, Decode, scale_info_derive::TypeInfo, Clone, PartialEq, Eq, DebugNoBound, Default, )] @@ -149,20 +157,29 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(attests, signature).execute(Self::set_claim_) + let attester_action = + WrappedActionWithNonce::new(attests.nonce(), signature.signer(), attests); + attester_action.signed(signature).execute( + |WrappedActionWithNonce { action, .. }, attest, attester| { + Self::set_claim_(action, attest, attester) + }, + ) } } impl Pallet { fn set_claim_( SetAttestationClaim { attest, .. }: SetAttestationClaim, - attester: Attester, + current_attest: &mut Attestation, + _attester: Attester, ) -> DispatchResult { - let prev = Attestations::::get(attester); - ensure!(prev.priority < attest.priority, Error::::PriorityTooLow); + ensure!( + current_attest.priority < attest.priority, + Error::::PriorityTooLow + ); // execute - Attestations::insert(attester, &attest); + *current_attest = attest; Ok(()) } diff --git a/pallets/core/src/modules/blob/benchmarks.rs b/pallets/core/src/modules/blob/benchmarks.rs index 068a6ca35..7e454a362 100644 --- a/pallets/core/src/modules/blob/benchmarks.rs +++ b/pallets/core/src/modules/blob/benchmarks.rs @@ -1,5 +1,8 @@ use super::*; -use crate::{common::state_change::ToStateChange, did::UncheckedDidKey}; +use crate::{ + common::state_change::ToStateChange, + did::{DidSignature, UncheckedDidKey}, +}; use frame_benchmarking::{benchmarks, whitelisted_caller}; use frame_system::RawOrigin; use sp_runtime::traits::TryCollect; diff --git a/pallets/core/src/modules/blob/mod.rs b/pallets/core/src/modules/blob/mod.rs index a423567be..b0133c3d9 100644 --- a/pallets/core/src/modules/blob/mod.rs +++ b/pallets/core/src/modules/blob/mod.rs @@ -1,13 +1,9 @@ //! Generic immutable single-owner storage. use crate::{ - common::{signatures::ForSigType, Limits, TypesAndLimits}, - did, - did::{ - AuthorizeTarget, Did, DidKey, DidMethodKey, DidOrDidMethodKey, DidOrDidMethodKeySignature, - SignedActionWithNonce, - }, - util::BoundedBytes, + common::{signatures::ForSigType, AuthorizeTarget, Limits, TypesAndLimits}, + did::{self, Did, DidKey, DidMethodKey, DidOrDidMethodKey, DidOrDidMethodKeySignature}, + util::{ActionWithNonce, BoundedBytes}, }; use codec::{Decode, Encode, MaxEncodedLen}; use sp_std::fmt::Debug; @@ -118,12 +114,16 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(add_blob, signature).execute(Self::new_) + add_blob.signed(signature).execute(Self::new_) } } impl Pallet { - fn new_(AddBlob { blob, .. }: AddBlob, signer: BlobOwner) -> DispatchResult { + fn new_( + AddBlob { blob, .. }: AddBlob, + (): &mut (), + signer: BlobOwner, + ) -> DispatchResult { // check ensure!( !Blobs::::contains_key(blob.id), diff --git a/pallets/core/src/modules/did/base/did_method_key.rs b/pallets/core/src/modules/did/base/did_method_key.rs index efb033dc5..206982240 100644 --- a/pallets/core/src/modules/did/base/did_method_key.rs +++ b/pallets/core/src/modules/did/base/did_method_key.rs @@ -1,6 +1,7 @@ use crate::{ + common::AuthorizeTarget, did::*, - util::{StorageMapRef, WithNonce}, + util::{StorageRef, WithNonce}, }; use codec::{Decode, Encode, MaxEncodedLen}; use core::ops::{Index, RangeFull}; @@ -24,10 +25,15 @@ impl From for DidMethodKey { } } -impl StorageMapRef> for DidMethodKey { - type Key = Self; +impl StorageRef for DidMethodKey { type Value = WithNonce; - type Storage = DidMethodKeys; + + fn try_mutate_associated(self, f: F) -> Result + where + F: FnOnce(&mut Option>) -> Result, + { + DidMethodKeys::::try_mutate_exists(self, f) + } } impl AuthorizeTarget for DidMethodKey {} diff --git a/pallets/core/src/modules/did/base/mod.rs b/pallets/core/src/modules/did/base/mod.rs index 1026f9cb1..af7c373ac 100644 --- a/pallets/core/src/modules/did/base/mod.rs +++ b/pallets/core/src/modules/did/base/mod.rs @@ -1,4 +1,7 @@ -use crate::{common::TypesAndLimits, impl_wrapper}; +use crate::{ + common::{AuthorizeTarget, TypesAndLimits}, + impl_wrapper, +}; use codec::{Decode, Encode, MaxEncodedLen}; use sp_std::{ fmt::Debug, @@ -27,6 +30,37 @@ pub enum DidOrDidMethodKey { DidMethodKey(DidMethodKey), } +impl StorageRef for DidOrDidMethodKey { + type Value = WithNonce; + + fn try_mutate_associated(self, f: F) -> Result + where + F: FnOnce(&mut Option>) -> Result, + { + match self { + Self::Did(did) => did.try_mutate_associated(|details| { + details.update_with(|onchain_details: &mut Option>| { + let mut with_nonce = onchain_details + .as_ref() + .map(|details| WithNonce::new_with_nonce((), details.nonce)); + + let res = f(&mut with_nonce); + + *onchain_details = + with_nonce + .zip(onchain_details.take()) + .map(|(with_nonce, details)| { + WithNonce::new_with_nonce(details.into_data(), with_nonce.nonce) + }); + + res + }) + }), + Self::DidMethodKey(did_method_key) => did_method_key.try_mutate_associated(f), + } + } +} + impl From for DidOrDidMethodKey { fn from(did: Did) -> Self { Self::Did(did) @@ -39,11 +73,33 @@ impl From for DidOrDidMethodKey { } } +impl TryFrom for DidKey { + type Error = DidMethodKey; + + fn try_from(did_key_or_did_method_key: DidKeyOrDidMethodKey) -> Result { + match did_key_or_did_method_key { + DidKeyOrDidMethodKey::DidKey(did_key) => Ok(did_key), + DidKeyOrDidMethodKey::DidMethodKey(did_method_key) => Err(did_method_key), + } + } +} + +impl TryFrom for DidMethodKey { + type Error = DidKey; + + fn try_from(did_key_or_did_method_key: DidKeyOrDidMethodKey) -> Result { + match did_key_or_did_method_key { + DidKeyOrDidMethodKey::DidKey(did_key) => Err(did_key), + DidKeyOrDidMethodKey::DidMethodKey(did_method_key) => Ok(did_method_key), + } + } +} + impl TryFrom for Did { type Error = DidMethodKey; - fn try_from(did_or_did_key: DidOrDidMethodKey) -> Result { - match did_or_did_key { + fn try_from(did_or_did_method_key: DidOrDidMethodKey) -> Result { + match did_or_did_method_key { DidOrDidMethodKey::Did(did) => Ok(did), DidOrDidMethodKey::DidMethodKey(did_key) => Err(did_key), } diff --git a/pallets/core/src/modules/did/base/onchain.rs b/pallets/core/src/modules/did/base/onchain.rs index 87d94d212..d9528683e 100644 --- a/pallets/core/src/modules/did/base/onchain.rs +++ b/pallets/core/src/modules/did/base/onchain.rs @@ -2,7 +2,7 @@ use super::super::*; use crate::{ common::TypesAndLimits, deposit_indexed_event, - util::{StorageMapRef, WithNonce}, + util::{StorageRef, WithNonce}, }; /// Each on-chain DID is associated with a nonce that is incremented each time the DID does a @@ -42,10 +42,15 @@ impl TryFrom> for StoredOnChainDidDetails { } } -impl StorageMapRef> for Did { - type Key = Self; - type Value = StoredDidDetails; - type Storage = Dids; +impl StorageRef for Did { + type Value = StoredOnChainDidDetails; + + fn try_mutate_associated(self, f: F) -> Result + where + F: FnOnce(&mut Option>) -> Result, + { + Dids::::try_mutate_exists(self, |details| details.update_with(f)) + } } impl OnChainDidDetails { diff --git a/pallets/core/src/modules/did/base/signature.rs b/pallets/core/src/modules/did/base/signature.rs index a0a678c9e..fafe487a9 100644 --- a/pallets/core/src/modules/did/base/signature.rs +++ b/pallets/core/src/modules/did/base/signature.rs @@ -1,50 +1,63 @@ use core::marker::PhantomData; use super::super::*; -use crate::common::{DidMethodKeySigValue, ForSigType, SigValue, ToStateChange, Types}; +use crate::common::{ + Authorization, AuthorizeSignedAction, AuthorizeTarget, DidMethodKeySigValue, ForSigType, + SigValue, Signature, ToStateChange, +}; use frame_support::traits::Get; -/// Authorizes action performed by `Self` over supplied target using given key. -pub trait AuthorizeTarget { - fn ensure_authorizes_target(&self, _: &Key, _: &A) -> Result<(), Error> +impl AuthorizeTarget for Authorizer +where + Authorizer: AuthorizeTarget + + AuthorizeTarget + + Into + + Clone, +{ + fn ensure_authorizes_target( + &self, + key: &DidKeyOrDidMethodKey, + action: &A, + ) -> Result<(), Error> where A: Action, { - Ok(()) + match key { + DidKeyOrDidMethodKey::DidKey(did_key) => { + let self_did: Did = self + .clone() + .into() + .try_into() + .map_err(|_| Error::::ExpectedDid)?; + + self_did.ensure_authorizes_target(did_key, action)?; + self.ensure_authorizes_target(did_key, action) + } + DidKeyOrDidMethodKey::DidMethodKey(did_method_key) => { + let self_did_method_key: DidMethodKey = self + .clone() + .into() + .try_into() + .map_err(|_| Error::::ExpectedDidMethodKey)?; + + self_did_method_key.ensure_authorizes_target(did_method_key, action)?; + self.ensure_authorizes_target(did_method_key, action) + } + } } } -/// Successfully authorized signer along with its key. -pub struct Authorization { - pub signer: S, - pub key: K, -} - /// Either `DidKey` or `DidMethodKey`. pub enum DidKeyOrDidMethodKey { DidKey(DidKey), DidMethodKey(DidMethodKey), } -/// Signed entity. -pub trait Signed { - type Signer: Clone; - - fn signer(&self) -> Self::Signer; -} - -type AuthorizationResult = Result< - Option::Signer, >::Key>>, - Error, ->; - -/// Authorizes signed action. -pub trait AuthorizeSignedAction: Signed { - type Key; - - fn authorizes_signed_action(&self, action: &A) -> AuthorizationResult - where - A: ToStateChange; +impl AuthorizeSignedAction for S +where + S: Signature, + S::Signer: AuthorizeTarget, +{ } /// `DID`'s signature along with the used `DID`s key reference. @@ -55,7 +68,7 @@ pub trait AuthorizeSignedAction: Signed { #[scale_info(omit_prefix)] pub enum DidOrDidMethodKeySignature> { DidSignature(DidSignature), - DidKeySignature(DidKeySignature), + DidMethodKeySignature(DidKeySignature), #[codec(skip)] #[cfg_attr(feature = "serde", serde(skip))] __Marker(PhantomData), @@ -71,7 +84,7 @@ impl> From> for DidOrDidMethodKeySignature { fn from(sig: DidKeySignature) -> Self { - Self::DidKeySignature(sig) + Self::DidMethodKeySignature(sig) } } @@ -115,7 +128,7 @@ impl> ForSigType for DidOrDidMethodKeySignature { Self::DidSignature(sig) => { sig.weight_for_sig_type::(for_sr25519, for_ed25519, for_secp256k1) } - Self::DidKeySignature(sig) => sig + Self::DidMethodKeySignature(sig) => sig .weight_for_sig_type::(for_sr25519, for_ed25519, for_secp256k1) .saturating_sub(T::DbWeight::get().reads(1)), _ => unreachable!(), @@ -130,7 +143,9 @@ impl> ForSigType for DidOrDidMethodKeySignature { ) -> R { match self { Self::DidSignature(sig) => sig.for_sig_type(for_sr25519, for_ed25519, for_secp256k1), - Self::DidKeySignature(sig) => sig.for_sig_type(for_sr25519, for_ed25519, for_secp256k1), + Self::DidMethodKeySignature(sig) => { + sig.for_sig_type(for_sr25519, for_ed25519, for_secp256k1) + } _ => unreachable!(), } } @@ -162,151 +177,91 @@ pub struct DidKeySignature> { pub sig: DidMethodKeySigValue, } -impl + Clone> Signed for DidSignature { +impl + Clone> Signature for DidSignature { type Signer = D; + type Key = DidKey; fn signer(&self) -> D { self.did.clone() } -} -/// Verifies that `did`'s key with id `key_id` can either authenticate or control otherwise returns an error. -/// Then provided signature will be verified against the supplied public key and `true` returned for a valid signature. -impl + Clone, A: Action> AuthorizeSignedAction for DidSignature -where - D: AuthorizeTarget, -{ - type Key = DidKey; + fn key(&self) -> Option { + super::Pallet::::did_key(self.did.clone().into(), self.key_id) + } - fn authorizes_signed_action( + fn verify_raw_bytes( &self, - action: &A, - ) -> Result>, Error> - where - A: ToStateChange, - { - let raw_did = self.did.clone().into(); - let signer_pubkey = - Pallet::::did_key(raw_did, self.key_id).ok_or(Error::::NoKeyForDid)?; - let encoded_state_change = action.to_state_change().encode(); - - raw_did.ensure_authorizes_target(&signer_pubkey, action)?; - self.did.ensure_authorizes_target(&signer_pubkey, action)?; - - self.sig - .verify(&encoded_state_change, signer_pubkey.public_key()) - .map_err(Into::into) - .map(|yes| { - yes.then(|| Authorization { - signer: self.did.clone(), - key: signer_pubkey, - }) - }) + message: &[u8], + public_key: &Self::Key, + ) -> Result { + self.sig.verify(message, public_key.public_key()) } } -impl + Clone> Signed for DidKeySignature { +/// Verifies that `did`'s key with id `key_id` can either authenticate or control otherwise returns an error. +/// Then provided signature will be verified against the supplied public key and `true` returned for a valid signature. + +impl + Clone> Signature for DidKeySignature { type Signer = DK; + type Key = DidMethodKey; fn signer(&self) -> DK { self.did_key.clone() } -} -impl + Clone, A: Action> AuthorizeSignedAction for DidKeySignature -where - DK: AuthorizeTarget, -{ - type Key = DidMethodKey; + fn key(&self) -> Option { + Some(self.did_key.clone().into()) + } - fn authorizes_signed_action( + fn verify_raw_bytes( &self, - action: &A, - ) -> Result>, Error> - where - A: ToStateChange, - { - let signer_pubkey = self.did_key.clone().into(); - let encoded_state_change = action.to_state_change().encode(); - - signer_pubkey.ensure_authorizes_target(&signer_pubkey, action)?; - self.did_key - .ensure_authorizes_target(&signer_pubkey, action)?; - - self.sig - .verify(&encoded_state_change, &signer_pubkey) - .map_err(Into::into) - .map(|yes| { - yes.then(|| Authorization { - signer: self.did_key.clone(), - key: signer_pubkey, - }) - }) + message: &[u8], + public_key: &Self::Key, + ) -> Result { + self.sig.verify(message, public_key) } } -impl Signed for DidOrDidMethodKeySignature +impl Signature for DidOrDidMethodKeySignature where D: Into + From + Clone, { type Signer = D; + type Key = DidKeyOrDidMethodKey; fn signer(&self) -> Self::Signer { match self { - Self::DidKeySignature(sig) => DidOrDidMethodKey::DidMethodKey(sig.did_key).into(), - Self::DidSignature(sig) => DidOrDidMethodKey::Did(sig.did).into(), + Self::DidMethodKeySignature(sig) => { + DidOrDidMethodKey::DidMethodKey(sig.signer()).into() + } + Self::DidSignature(sig) => DidOrDidMethodKey::Did(sig.signer()).into(), Self::__Marker(_) => unreachable!(), } } -} -impl + From + Clone, A: Action> - AuthorizeSignedAction for DidOrDidMethodKeySignature -where - DidSignature: AuthorizeSignedAction, - DidKeySignature: - AuthorizeSignedAction, - Did: AuthorizeTarget, - DidMethodKey: AuthorizeTarget, - D: AuthorizeTarget + AuthorizeTarget, -{ - type Key = DidKeyOrDidMethodKey; + fn key(&self) -> Option { + match self { + Self::DidMethodKeySignature(sig) => DidKeyOrDidMethodKey::DidMethodKey(sig.key::()?), + Self::DidSignature(sig) => DidKeyOrDidMethodKey::DidKey(sig.key::()?), + Self::__Marker(_) => unreachable!(), + } + .into() + } - fn authorizes_signed_action( - &self, - action: &A, - ) -> Result>, Error> - where - A: ToStateChange, - { - let authorization = match self { - Self::DidSignature(sig) => sig.authorizes_signed_action(action)?.map( - |Authorization { signer, key, .. }| Authorization { - signer: D::from(DidOrDidMethodKey::Did(signer)), - key: DidKeyOrDidMethodKey::DidKey(key), - }, - ), - Self::DidKeySignature(sig) => sig.authorizes_signed_action(action)?.map( - |Authorization { signer, key, .. }| Authorization { - signer: DidOrDidMethodKey::DidMethodKey(signer).into(), - key: DidKeyOrDidMethodKey::DidMethodKey(key), - }, - ), - _ => None, - }; - - if let Some(Authorization { key, signer }) = authorization.as_ref() { - match key { - DidKeyOrDidMethodKey::DidKey(did_key) => { - signer.ensure_authorizes_target(did_key, action)? - } + fn verify_raw_bytes(&self, message: &[u8], key: &Self::Key) -> Result { + match self { + Self::DidSignature(sig) => match key { + DidKeyOrDidMethodKey::DidKey(did_key) => sig.verify_raw_bytes(message, did_key), + _ => Err(VerificationError::IncompatibleKey), + }, + Self::DidMethodKeySignature(sig) => match key { DidKeyOrDidMethodKey::DidMethodKey(did_method_key) => { - signer.ensure_authorizes_target(did_method_key, action)? + sig.verify_raw_bytes(message, did_method_key) } - } + _ => Err(VerificationError::IncompatibleKey), + }, + Self::__Marker(_) => unreachable!(), } - - Ok(authorization) } } @@ -325,122 +280,6 @@ impl> DidSignature { } } -pub struct SignedActionWithNonce -where - A: ActionWithNonce, -{ - action: A, - signature: S, - _marker: PhantomData, -} - -impl SignedActionWithNonce -where - A: ActionWithNonce, -{ - pub fn new(action: A, signature: S) -> Self { - Self { - action, - signature, - _marker: PhantomData, - } - } -} - -impl SignedActionWithNonce> -where - A: ActionWithNonce + ToStateChange, - D: Into - + AuthorizeTarget as AuthorizeSignedAction>::Key> - + Clone, - DidSignature: AuthorizeSignedAction, -{ - pub fn execute(self, f: F) -> Result - where - F: FnOnce(A, D) -> Result, - E: From + From + From>, - { - let SignedActionWithNonce { - action, signature, .. - } = self; - - ensure!( - signature.authorizes_signed_action(&action)?.is_some(), - Error::::InvalidSignature - ); - - WrappedActionWithNonce::::new(action.nonce(), signature.signer().into(), action) - .execute(|WrappedActionWithNonce { action, .. }, _| f(action, signature.signer())) - .map_err(Into::into) - } -} - -impl SignedActionWithNonce> -where - A: ActionWithNonce + ToStateChange, - D: Into - + AuthorizeTarget as AuthorizeSignedAction>::Key> - + Clone, - DidKeySignature: AuthorizeSignedAction, -{ - pub fn execute(self, f: F) -> Result - where - F: FnOnce(A, D) -> Result, - E: From + From + From>, - { - let SignedActionWithNonce { - action, signature, .. - } = self; - - ensure!( - signature.authorizes_signed_action(&action)?.is_some(), - Error::::InvalidSignature - ); - - WrappedActionWithNonce::::new( - action.nonce(), - signature.signer().into(), - action, - ) - .execute(|WrappedActionWithNonce { action, .. }, _| f(action, signature.signer())) - .map_err(Into::into) - } -} - -impl SignedActionWithNonce> -where - A: ActionWithNonce + ToStateChange, - D: Into + From + Clone, - D: AuthorizeTarget + AuthorizeTarget, - DidOrDidMethodKeySignature: AuthorizeSignedAction, -{ - pub fn execute(self, f: F) -> Result - where - F: FnOnce(A, D) -> Result, - E: From + From + From>, - { - let SignedActionWithNonce { - action, signature, .. - } = self; - - let Authorization { signer, .. } = signature - .authorizes_signed_action(&action)? - .ok_or(Error::::InvalidSignature)?; - - match signer.clone().into() { - DidOrDidMethodKey::Did(did) => { - WrappedActionWithNonce::::new(action.nonce(), did, action) - .execute(|WrappedActionWithNonce { action, .. }, _| f(action, signer)) - .map_err(Into::into) - } - DidOrDidMethodKey::DidMethodKey(did_key) => { - WrappedActionWithNonce::::new(action.nonce(), did_key, action) - .execute(|WrappedActionWithNonce { action, .. }, _| f(action, signer)) - .map_err(Into::into) - } - } - } -} impl SignedActionWithNonce> where A: ActionWithNonce + ToStateChange, @@ -450,7 +289,7 @@ where pub fn execute_from_controller(self, f: F) -> Result where F: FnOnce(A, &mut OnChainDidDetails) -> Result, - E: From + From + From>, + E: From + From + From>, { self.execute_removable_from_controller(|action, reference| { f(action, reference.as_mut().unwrap()) @@ -460,7 +299,7 @@ where pub fn execute_removable_from_controller(self, f: F) -> Result where F: FnOnce(A, &mut Option) -> Result, - E: From + From + From>, + E: From + From + From>, { let SignedActionWithNonce { action, signature, .. @@ -474,13 +313,13 @@ where DidOrDidMethodKey::Did(controller) => { if controller == action.target() { WrappedActionWithNonce::::new(action.nonce(), controller, action) - .execute(|WrappedActionWithNonce { action, .. }, reference| { - f(action, reference) - }) + .execute_and_increase_nonce( + |WrappedActionWithNonce { action, .. }, reference| f(action, reference), + ) .map_err(Into::into) } else { WrappedActionWithNonce::::new(action.nonce(), controller, action) - .execute(|WrappedActionWithNonce { action, .. }, _| { + .execute_and_increase_nonce(|WrappedActionWithNonce { action, .. }, _| { action.execute_without_increasing_nonce(|action, reference| { f(action, reference) }) @@ -495,7 +334,7 @@ where >::new( action.nonce(), controller, action ) - .execute(|WrappedActionWithNonce { action, .. }, _| { + .execute_and_increase_nonce(|WrappedActionWithNonce { action, .. }, _| { action.execute_without_increasing_nonce(|action, reference| f(action, reference)) }) .map_err(Into::into), diff --git a/pallets/core/src/modules/did/controllers.rs b/pallets/core/src/modules/did/controllers.rs index 0ae74f72e..a5a80a369 100644 --- a/pallets/core/src/modules/did/controllers.rs +++ b/pallets/core/src/modules/did/controllers.rs @@ -1,5 +1,5 @@ use super::*; -use crate::{deposit_indexed_event, impl_wrapper}; +use crate::{common::AuthorizeTarget, deposit_indexed_event, impl_wrapper}; /// `DID`'s controller. #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, Copy, Ord, PartialOrd, MaxEncodedLen)] diff --git a/pallets/core/src/modules/did/mod.rs b/pallets/core/src/modules/did/mod.rs index 9a9471a15..ec54a81db 100644 --- a/pallets/core/src/modules/did/mod.rs +++ b/pallets/core/src/modules/did/mod.rs @@ -111,10 +111,12 @@ pub mod pallet { ServiceEndpointDoesNotExist, KeyAgreementCantBeUsedForSigning, SigningKeyCantBeUsedForKeyAgreement, + ExpectedDid, + ExpectedDidMethodKey, } impl From for Error { - fn from(VerificationError::IncompatibleKey(_): VerificationError) -> Self { + fn from(VerificationError::IncompatibleKey: VerificationError) -> Self { Self::IncompatSigPubkey } } @@ -257,7 +259,7 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(keys, sig) + keys.signed(sig) .execute_from_controller(Self::add_keys_) .map_err(Into::into) } @@ -272,7 +274,7 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(keys, sig) + keys.signed(sig) .execute_from_controller(Self::remove_keys_) .map_err(Into::into) } @@ -289,7 +291,8 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(controllers, sig) + controllers + .signed(sig) .execute_from_controller(Self::add_controllers_) .map_err(Into::into) } @@ -305,7 +308,8 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(controllers, sig) + controllers + .signed(sig) .execute_from_controller(Self::remove_controllers_) .map_err(Into::into) } @@ -319,7 +323,8 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(service_endpoint, sig) + service_endpoint + .signed(sig) .execute_from_controller(Self::add_service_endpoint_) .map_err(Into::into) } @@ -333,7 +338,8 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(service_endpoint, sig) + service_endpoint + .signed(sig) .execute_from_controller(Self::remove_service_endpoint_) .map_err(Into::into) } @@ -349,7 +355,8 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(removal, sig) + removal + .signed(sig) .execute_removable_from_controller(Self::remove_onchain_did_) .map_err(Into::into) } diff --git a/pallets/core/src/modules/master/mod.rs b/pallets/core/src/modules/master/mod.rs index a0a6a5f86..7cd96a569 100644 --- a/pallets/core/src/modules/master/mod.rs +++ b/pallets/core/src/modules/master/mod.rs @@ -60,8 +60,8 @@ #[cfg(feature = "serde")] use crate::util::btree_set; use crate::{ - common::{signatures::ForSigType, Limits, Types}, - did::{self, Authorization, AuthorizeSignedAction, Did, DidSignature}, + common::{signatures::ForSigType, Authorization, AuthorizeSignedAction, Limits, Types}, + did::{self, Did, DidSignature}, util::WithNonce, }; use alloc::{boxed::Box, collections::BTreeMap, vec::Vec}; diff --git a/pallets/core/src/modules/offchain_signatures/benchmarks.rs b/pallets/core/src/modules/offchain_signatures/benchmarks.rs index b345b97db..168866f2b 100644 --- a/pallets/core/src/modules/offchain_signatures/benchmarks.rs +++ b/pallets/core/src/modules/offchain_signatures/benchmarks.rs @@ -2,7 +2,7 @@ use super::*; use crate::{ common::{CurveType, ToStateChange}, did::{Did, DidSignature, UncheckedDidKey}, - util::{BoundedBytes, IncId}, + util::{Action, BoundedBytes, IncId}, }; use frame_benchmarking::{benchmarks, whitelisted_caller}; use frame_system::RawOrigin; @@ -60,7 +60,9 @@ crate::bench_with_all_pairs! { Default::default(), ).unwrap(); - Pallet::::add_params_( + WrappedActionWithNonce::::new( + 1u8.into(), + SignatureParamsOwner(did.into()), AddOffchainSignatureParams { params: BBSPlusParameters::new( BoundedBytes::try_from(vec![1; MAX_LABEL as usize]).unwrap(), @@ -69,8 +71,7 @@ crate::bench_with_all_pairs! { ).into(), nonce: 1u8.into() }, - SignatureParamsOwner(did.into()) - ).unwrap(); + ).execute::(|action, entity| super::Pallet::::add_params_(action.action, entity, SignatureParamsOwner(did.into()))).unwrap(); let rem_params = RemoveOffchainSignatureParams { params_ref: (SignatureParamsOwner(did.into()), 1u8.into()), @@ -99,7 +100,9 @@ crate::bench_with_all_pairs! { Default::default(), ).unwrap(); - Pallet::::add_params_( + WrappedActionWithNonce::::new( + 1u8.into(), + SignatureParamsOwner(did.into()), AddOffchainSignatureParams { params: BBSPlusParameters::new( BoundedBytes::try_from(vec![1; MAX_LABEL as usize]).unwrap(), @@ -108,8 +111,7 @@ crate::bench_with_all_pairs! { ).into(), nonce: 1u8.into() }, - SignatureParamsOwner(did.into()) - ).unwrap(); + ).execute::(|action, entity| super::Pallet::::add_params_(action.action, entity, SignatureParamsOwner(did.into()))).unwrap(); let key: OffchainPublicKey = BBSPlusPublicKey::new( BoundedBytes::try_from(vec![0; b as usize]).unwrap(), @@ -141,7 +143,9 @@ crate::bench_with_all_pairs! { Default::default(), ).unwrap(); - Pallet::::add_params_( + WrappedActionWithNonce::::new( + 1u8.into(), + SignatureParamsOwner(did.into()), AddOffchainSignatureParams { params: BBSPlusParameters::new( BoundedBytes::try_from(vec![1; MAX_LABEL as usize]).unwrap(), @@ -150,8 +154,7 @@ crate::bench_with_all_pairs! { ).into(), nonce: 1u8.into() }, - SignatureParamsOwner(did.into()) - ).unwrap(); + ).execute::(|action, entity| super::Pallet::::add_params_(action.action, entity, SignatureParamsOwner(did.into()))).unwrap(); Pallet::::add_public_key_( AddOffchainSignaturePublicKey { diff --git a/pallets/core/src/modules/offchain_signatures/mod.rs b/pallets/core/src/modules/offchain_signatures/mod.rs index 9e68735d9..7a2462648 100644 --- a/pallets/core/src/modules/offchain_signatures/mod.rs +++ b/pallets/core/src/modules/offchain_signatures/mod.rs @@ -2,10 +2,10 @@ //! Currently can be either `BBS`, `BBS+` or `Pointcheval-Sanders`. use crate::{ - common::{self, signatures::ForSigType}, + common::{self, signatures::ForSigType, Signature}, did, - did::{Controller, Did, DidOrDidMethodKeySignature, OnDidRemoval, SignedActionWithNonce}, - util::IncId, + did::{Controller, Did, DidOrDidMethodKeySignature, OnDidRemoval}, + util::{ActionWithNonce, IncId, WrappedActionWithNonce}, }; use codec::{Decode, Encode}; use sp_std::prelude::*; @@ -72,9 +72,8 @@ pub mod pallet { #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); - /// Pair of counters where each is used to assign unique id to parameters and public keys - /// respectively. On adding new params or keys, corresponding counter is increased by 1 but - /// the counters don't decrease on removal + /// On adding new params, corresponding counter is increased by 1 but + /// the counters don't decrease on removal. #[pallet::storage] #[pallet::getter(fn did_params_counter)] pub type ParamsCounter = @@ -133,7 +132,11 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(params, signature).execute(Self::add_params_) + WrappedActionWithNonce::new(params.nonce(), signature.signer(), params) + .signed(signature) + .execute(|WrappedActionWithNonce { action, .. }, counter, actor| { + Self::add_params_(action, counter, actor) + }) } /// Add new offchain signature public key. Only the DID controller can add key and it should use the nonce from the DID module. @@ -146,7 +149,8 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(public_key, signature) + public_key + .signed(signature) .execute_from_controller(Self::add_public_key_) } @@ -158,7 +162,11 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(remove, signature).execute(Self::remove_params_) + WrappedActionWithNonce::new(remove.nonce(), signature.signer(), remove) + .signed(signature) + .execute(|WrappedActionWithNonce { action, .. }, counter, actor| { + Self::remove_params_(action, counter, actor) + }) } /// Remove existing offchain signature public key. Only the DID controller can remove key and it should use the nonce from the DID module. @@ -171,7 +179,8 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(remove, signature) + remove + .signed(signature) .execute_from_controller(Self::remove_public_key_) } } diff --git a/pallets/core/src/modules/offchain_signatures/params.rs b/pallets/core/src/modules/offchain_signatures/params.rs index 617fda976..a55c1ee74 100644 --- a/pallets/core/src/modules/offchain_signatures/params.rs +++ b/pallets/core/src/modules/offchain_signatures/params.rs @@ -1,8 +1,8 @@ use crate::{ - common::Limits, - did::{AuthorizeTarget, DidKey, DidMethodKey, DidOrDidMethodKey}, + common::{AuthorizeTarget, Limits}, + did::{DidKey, DidMethodKey, DidOrDidMethodKey}, offchain_signatures::schemes::*, - util::IncId, + util::{IncId, OptionExt, StorageRef}, }; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ensure, DebugNoBound}; @@ -24,8 +24,19 @@ pub struct SignatureParamsOwner(pub DidOrDidMethodKey); crate::impl_wrapper!(SignatureParamsOwner(DidOrDidMethodKey)); -impl AuthorizeTarget<(), DidKey> for SignatureParamsOwner {} -impl AuthorizeTarget<(), DidMethodKey> for SignatureParamsOwner {} +impl AuthorizeTarget for SignatureParamsOwner {} +impl AuthorizeTarget for SignatureParamsOwner {} + +impl StorageRef for SignatureParamsOwner { + type Value = IncId; + + fn try_mutate_associated(self, f: F) -> Result + where + F: FnOnce(&mut Option) -> Result, + { + ParamsCounter::::try_mutate_exists(self, |entry| f(entry.initialized())) + } +} pub type SignatureParamsStorageKey = (SignatureParamsOwner, IncId); pub type BBSPublicKeyWithParams = (BBSPublicKey, Option>); @@ -90,12 +101,12 @@ impl OffchainSignatureParams { impl Pallet { pub(super) fn add_params_( AddOffchainSignatureParams { params, .. }: AddOffchainSignatureParams, + params_counter: &mut IncId, signer: SignatureParamsOwner, ) -> DispatchResult { - let params_count = ParamsCounter::::mutate(signer, |counter| *counter.inc()); - SignatureParams::::insert(signer, params_count, params); + SignatureParams::::insert(signer, params_counter.inc(), params); - Self::deposit_event(Event::ParamsAdded(signer, params_count)); + Self::deposit_event(Event::ParamsAdded(signer, *params_counter)); Ok(()) } @@ -104,6 +115,7 @@ impl Pallet { params_ref: (did, counter), .. }: RemoveOffchainSignatureParams, + _: &mut IncId, owner: SignatureParamsOwner, ) -> DispatchResult { // Only the DID that added the param can it diff --git a/pallets/core/src/modules/offchain_signatures/tests.rs b/pallets/core/src/modules/offchain_signatures/tests.rs index 6e29d3d5a..ccdbc01aa 100644 --- a/pallets/core/src/modules/offchain_signatures/tests.rs +++ b/pallets/core/src/modules/offchain_signatures/tests.rs @@ -4,7 +4,7 @@ use crate::{ did::{base::*, tests::check_did_detail, AddControllers}, offchain_signatures, tests::common::*, - util::{ActionWithNonce, BoundedBytes}, + util::{Action, ActionWithNonce, BoundedBytes}, }; use alloc::collections::BTreeMap; use frame_support::assert_err; @@ -980,14 +980,15 @@ with_each_scheme! { run_to_block(35); - assert!(SignatureMod::add_params_( + ParamsCounter::::mutate(SignatureParamsOwner(author.into()), |counter| assert!(SignatureMod::add_params_( AddOffchainSignatureParams { params: params.clone().into(), nonce: next_nonce }, + counter, SignatureParamsOwner(author.into()) ) - .is_ok()); + .is_ok())); assert_eq!( ParamsCounter::::get(SignatureParamsOwner(author.into())), IncId::from(1u8) @@ -1007,7 +1008,7 @@ with_each_scheme! { nonce: did_detail.next_nonce().unwrap(), }; assert_eq!(did_detail.nonce + 1, ak.nonce); - assert!(ak.execute( + assert!(ak.execute_and_increase_nonce( |action, details| SignatureMod::add_public_key_(action, details.as_mut().unwrap()) ) .is_ok()); @@ -1026,7 +1027,7 @@ with_each_scheme! { nonce: did_detail.next_nonce().unwrap(), }; assert_eq!(did_detail.nonce + 1, ak.nonce); - assert!(ak.execute( + assert!(ak.execute_and_increase_nonce( |action, details| SignatureMod::add_public_key_(action, details.as_mut().unwrap()), ) @@ -1049,7 +1050,7 @@ with_each_scheme! { nonce: did_detail.next_nonce().unwrap(), }; assert_eq!(did_detail.nonce + 1, ak.nonce); - assert!(ak.execute( + assert!(ak.execute_and_increase_nonce( |action, details| SignatureMod::add_public_key_(action, details.as_mut().unwrap()), ) @@ -1070,14 +1071,13 @@ with_each_scheme! { run_to_block(70); let did_detail = DIDModule::onchain_did_details(&author).unwrap(); - assert!(SignatureMod::add_params_( - AddOffchainSignatureParams { - params: params_1.clone().into(), - nonce: did_detail.next_nonce().unwrap() - }, - SignatureParamsOwner(author.into()) + WrappedActionWithNonce::::new(0, SignatureParamsOwner(author.into()), AddOffchainSignatureParams { + params: params_1.clone().into(), + nonce: did_detail.next_nonce().unwrap() + }).execute::( + |action, counter| SignatureMod::add_params_(action.action, counter, SignatureParamsOwner(author.into())) ) - .is_ok()); + .unwrap(); assert_eq!( ParamsCounter::::get(SignatureParamsOwner(author.into())), IncId::from(2u8) @@ -1121,7 +1121,7 @@ with_each_scheme! { nonce: did_detail_1.next_nonce().unwrap(), }; assert_eq!(did_detail_1.nonce + 1, ak.nonce); - assert!(ak.execute( + assert!(ak.execute_and_increase_nonce( |action, details| SignatureMod::add_public_key_(action, details.as_mut().unwrap()), ) @@ -1134,14 +1134,15 @@ with_each_scheme! { run_to_block(90); let did_detail_1 = DIDModule::onchain_did_details(&author_1).unwrap(); - assert!(SignatureMod::add_params_( + ParamsCounter::::mutate(SignatureParamsOwner(author_1.into()), |counter| assert!(SignatureMod::add_params_( AddOffchainSignatureParams { params: params.clone().into(), nonce: did_detail_1.next_nonce().unwrap() }, + counter, SignatureParamsOwner(author_1.into()) ) - .is_ok()); + .is_ok())); assert_eq!( ParamsCounter::::get(SignatureParamsOwner(author_1.into())), IncId::from(1u8) @@ -1164,7 +1165,7 @@ with_each_scheme! { nonce: did_detail_1.next_nonce().unwrap(), }; assert_eq!(did_detail_1.nonce + 1, ak.nonce); - assert!(ak.execute( + assert!(ak.execute_and_increase_nonce( |action, details| SignatureMod::add_public_key_(action, details.as_mut().unwrap()), ) @@ -1224,30 +1225,26 @@ with_each_scheme! { None ); - SignatureMod::add_params_( - AddOffchainSignatureParams { - params: params.clone().into(), - nonce: 0, // Doesn't matter - }, - SignatureParamsOwner(author.into()), - ) - .unwrap(); - SignatureMod::add_params_( - AddOffchainSignatureParams { - params: params_1.clone().into(), - nonce: 0, // Doesn't matter - }, - SignatureParamsOwner(author_1.into()), - ) - .unwrap(); - SignatureMod::add_params_( - AddOffchainSignatureParams { - params: params_2.clone().into(), - nonce: 0, // Doesn't matter - }, - SignatureParamsOwner(author_1.into()), + WrappedActionWithNonce::::new(0, SignatureParamsOwner(author.into()), AddOffchainSignatureParams { + params: params.clone().into(), + nonce: 0, // Doesn't matter + }).execute::( + |action, counter| SignatureMod::add_params_(action.action, counter, SignatureParamsOwner(author.into())) ) .unwrap(); + WrappedActionWithNonce::::new(0, SignatureParamsOwner(author_1.into()), AddOffchainSignatureParams { + params: params_1.clone().into(), + nonce: 0, // Doesn't matter + }).execute::( + |action, counter| SignatureMod::add_params_(action.action, counter, SignatureParamsOwner(author_1.into())) + ).unwrap(); + + WrappedActionWithNonce::::new(0, SignatureParamsOwner(author_1.into()), AddOffchainSignatureParams { + params: params_2.clone().into(), + nonce: 0, // Doesn't matter + }).execute::( + |action, counter| SignatureMod::add_params_(action.action, counter, SignatureParamsOwner(author_1.into())) + ).unwrap(); assert_eq!( SignatureMod::did_params(&SignatureParamsOwner(author.into())).collect::>(), @@ -1274,7 +1271,7 @@ with_each_scheme! { did: author, nonce: did_detail.next_nonce().unwrap(), }; - assert!(ak.execute( + assert!(ak.execute_and_increase_nonce( |action, details| SignatureMod::add_public_key_(action, details.as_mut().unwrap()), ) @@ -1292,7 +1289,7 @@ with_each_scheme! { did: author_1, nonce: did_detail_1.next_nonce().unwrap(), }; - assert!(ak.execute( + assert!(ak.execute_and_increase_nonce( |action, details| SignatureMod::add_public_key_(action, details.as_mut().unwrap()), ) @@ -1310,7 +1307,7 @@ with_each_scheme! { did: author, nonce: did_detail.next_nonce().unwrap(), }; - assert!(ak.execute( + assert!(ak.execute_and_increase_nonce( |action, details| SignatureMod::add_public_key_(action, details.as_mut().unwrap()), ) diff --git a/pallets/core/src/modules/revoke/impl.rs b/pallets/core/src/modules/revoke/impl.rs index 0e066bab0..31dca713f 100644 --- a/pallets/core/src/modules/revoke/impl.rs +++ b/pallets/core/src/modules/revoke/impl.rs @@ -1,9 +1,5 @@ use super::*; -use crate::{ - common::{DidSignatureWithNonce, PolicyExecutionError}, - deposit_indexed_event, - util::{ActionWithNonce, UpdateWithNonceError}, -}; +use crate::deposit_indexed_event; impl Pallet { pub(super) fn new_registry_( @@ -70,66 +66,4 @@ impl Pallet { deposit_indexed_event!(RegistryRemoved(registry_id)); Ok(()) } - - /// Executes action over target registry providing a mutable reference if all checks succeed. - /// - /// Checks: - /// 1. Ensure that the `StatusListCredential` exists. - /// 2. Verify that `proof` authorizes `action` according to `policy`. - /// 3. Verify that the action is not a replayed payload by ensuring each provided controller nonce equals the last nonce plus 1. - /// - /// Returns a mutable reference to the underlying registry if the command is authorized, otherwise returns Err. - pub(crate) fn try_exec_action_over_registry( - f: F, - action: A, - proof: Vec>, - ) -> Result - where - F: FnOnce(A, &mut RevocationRegistry) -> Result, - A: Action, - WithNonce: ActionWithNonce + ToStateChange, - E: From> - + From - + From> - + From - + From, - { - Self::try_exec_removable_action_over_registry( - |action, reg| f(action, reg.as_mut().unwrap()), - action, - proof, - ) - } - - /// Executes action over target registry providing a mutable reference if all checks succeed. - /// - /// Unlike `try_exec_action_over_registry`, this action may result in a removal of a Registry, if the value under option - /// will be taken. - /// - /// Checks: - /// 1. Ensure that the `Registry` exists. - /// 2. Verify that `proof` authorizes `action` according to `policy`. - /// 3. Verify that the action is not a replayed payload by ensuring each provided controller nonce equals the last nonce plus 1. - /// - /// Returns a mutable reference to the underlying registry wrapped into an option if the command is authorized, - /// otherwise returns Err. - pub(crate) fn try_exec_removable_action_over_registry( - f: F, - action: A, - proof: Vec>, - ) -> Result - where - F: FnOnce(A, &mut Option>) -> Result, - A: Action, - WithNonce: ActionWithNonce + ToStateChange, - E: From> - + From - + From> - + From - + From, - { - Registries::try_mutate_exists(action.target(), |registry| { - Policy::try_exec_removable_action(registry, f, action, proof) - }) - } } diff --git a/pallets/core/src/modules/revoke/mod.rs b/pallets/core/src/modules/revoke/mod.rs index 4ba1e47d2..19da02412 100644 --- a/pallets/core/src/modules/revoke/mod.rs +++ b/pallets/core/src/modules/revoke/mod.rs @@ -1,12 +1,9 @@ #[cfg(feature = "serde")] use crate::util::hex; use crate::{ - common::{ - self, signatures::ForSigType, DidSignatureWithNonce, HasPolicy, Limits, Policy, - ToStateChange, - }, + common::{self, signatures::ForSigType, DidSignatureWithNonce, HasPolicy, Limits, Policy}, did::{self}, - util::{Action, NonceError, WithNonce}, + util::{Action, NonceError, StorageRef, WithNonce}, }; use alloc::collections::BTreeSet; use codec::{Decode, Encode, MaxEncodedLen}; @@ -44,6 +41,17 @@ impl Index for RevocationRegistryId { } } +impl StorageRef for RevocationRegistryId { + type Value = RevocationRegistry; + + fn try_mutate_associated(self, f: F) -> Result + where + F: FnOnce(&mut Option>) -> Result, + { + Registries::::try_mutate_exists(self, f) + } +} + crate::impl_wrapper!(RevocationRegistryId([u8; 32])); /// Points to a revocation which may or may not exist in a registry. @@ -213,7 +221,7 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - Self::try_exec_action_over_registry(Self::revoke_, revoke, proof) + revoke.execute(|action, registry| registry.execute(Self::revoke_, action, proof)) } /// Delete some revocations according to the `unrevoke` command. @@ -235,7 +243,7 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - Self::try_exec_action_over_registry(Self::unrevoke_, unrevoke, proof) + unrevoke.execute(|action, registry| registry.execute(Self::unrevoke_, action, proof)) } /// Delete an entire registry. Deletes all revocations within the registry, as well as @@ -259,7 +267,9 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - Self::try_exec_removable_action_over_registry(Self::remove_registry_, removal, proof) + removal.execute_removable(|action, registry| { + HasPolicy::execute_removable(registry, Self::remove_registry_, action, proof) + }) } } diff --git a/pallets/core/src/modules/revoke/tests.rs b/pallets/core/src/modules/revoke/tests.rs index 2185f44b7..e2b4235fc 100644 --- a/pallets/core/src/modules/revoke/tests.rs +++ b/pallets/core/src/modules/revoke/tests.rs @@ -56,7 +56,10 @@ pub fn check_nonce_increase(old_nonces: BTreeMap, signers: &[(Did, &sr /// Tests in this module are named after the errors they check. /// For example, `#[test] fn invalidpolicy` exercises the Error::InvalidPolicy. mod errors { - use crate::common::{PolicyExecutionError, PolicyValidationError}; + use crate::{ + common::{PolicyExecutionError, PolicyValidationError}, + util::ActionExecutionError, + }; // Cannot do `use super::*` as that would import `Call` as `Call` which conflicts with `Call` in `tests::common` use super::*; @@ -256,7 +259,7 @@ mod errors { let registry_id = RGA; - let noreg: Result<(), DispatchError> = Err(PolicyExecutionError::NoEntity.into()); + let noreg: Result<(), DispatchError> = Err(ActionExecutionError::NoEntity.into()); assert_eq!( RevoMod::revoke( @@ -753,11 +756,10 @@ mod test { let old_nonces = get_nonces(signers); let command = &rev; let proof = get_pauth(command, signers); - let res = RevoMod::try_exec_action_over_registry( - |_, _| Ok::<_, DispatchError>(()), - command.clone(), - proof, - ); + + let res = command.clone().execute(|action, registry| { + registry.execute(|_, _| Ok::<_, DispatchError>(()), action, proof) + }); assert_eq!(res.is_ok(), *expect_success); if *expect_success { diff --git a/pallets/core/src/modules/status_list_credential/benchmarks.rs b/pallets/core/src/modules/status_list_credential/benchmarks.rs index 664d252c1..f7ace837f 100644 --- a/pallets/core/src/modules/status_list_credential/benchmarks.rs +++ b/pallets/core/src/modules/status_list_credential/benchmarks.rs @@ -1,8 +1,8 @@ use super::*; use crate::{ - common::state_change::ToStateChange, + common::{state_change::ToStateChange, Policy}, did::{Did, DidSignature, UncheckedDidKey}, - util::BoundedBytes, + util::{BoundedBytes, WithNonce}, }; use frame_benchmarking::{benchmarks, whitelisted_caller}; use frame_system::RawOrigin; diff --git a/pallets/core/src/modules/status_list_credential/impl.rs b/pallets/core/src/modules/status_list_credential/impl.rs index 4da5b68ee..204bc4142 100644 --- a/pallets/core/src/modules/status_list_credential/impl.rs +++ b/pallets/core/src/modules/status_list_credential/impl.rs @@ -1,5 +1,3 @@ -use crate::util::{ActionWithNonce, UpdateWithNonceError}; - use super::*; impl Pallet { @@ -40,66 +38,4 @@ impl Pallet { deposit_indexed_event!(StatusListCredentialRemoved(id)); Ok(()) } - - /// Executes action over target `StatusListCredential` providing a mutable reference if all checks succeed. - /// - /// Checks: - /// 1. Ensure that the `StatusListCredential` exists. - /// 2. Verify that `proof` authorizes `action` according to `policy`. - /// 3. Verify that the action is not a replayed payload by ensuring each provided controller nonce equals the last nonce plus 1. - /// - /// Returns a mutable reference to the underlying StatusListCredential if the command is authorized, otherwise returns Err. - pub(crate) fn try_exec_action_over_status_list_credential( - f: F, - action: A, - proof: Vec>, - ) -> Result - where - F: FnOnce(A, &mut StatusListCredentialWithPolicy) -> Result, - A: Action, - WithNonce: ActionWithNonce + ToStateChange, - E: From> - + From - + From> - + From - + From, - { - Self::try_exec_removable_action_over_status_list_credential( - |action, reg| f(action, reg.as_mut().unwrap()), - action, - proof, - ) - } - - /// Executes action over target `StatusListCredential` providing a mutable reference if all checks succeed. - /// - /// Unlike `try_exec_action_over_status_list_credential`, this action may result in a removal of a `StatusListCredential`, if the value under option - /// will be taken. - /// - /// Checks: - /// 1. Ensure that the `StatusListCredential` exists. - /// 2. Verify that `proof` authorizes `action` according to `policy`. - /// 3. Verify that the action is not a replayed payload by ensuring each provided controller nonce equals the last nonce plus 1. - /// - /// Returns a mutable reference to the underlying `StatusListCredential` wrapped into an option if the command is authorized, - /// otherwise returns Err. - pub(crate) fn try_exec_removable_action_over_status_list_credential( - f: F, - action: A, - proof: Vec>, - ) -> Result - where - F: FnOnce(A, &mut Option>) -> Result, - A: Action, - WithNonce: ActionWithNonce + ToStateChange, - E: From> - + From - + From> - + From - + From, - { - StatusListCredentials::try_mutate_exists(action.target(), |credential| { - Policy::try_exec_removable_action(credential, f, action, proof) - }) - } } diff --git a/pallets/core/src/modules/status_list_credential/mod.rs b/pallets/core/src/modules/status_list_credential/mod.rs index 37cf21929..0786be84f 100644 --- a/pallets/core/src/modules/status_list_credential/mod.rs +++ b/pallets/core/src/modules/status_list_credential/mod.rs @@ -2,11 +2,9 @@ //! - [`RevocationList2020Credential`](https://w3c-ccg.github.io/vc-status-rl-2020/#revocationlist2020credential) //! - [`StatusList2021Credential`](https://www.w3.org/TR/vc-status-list/#statuslist2021credential). use crate::{ - common::{ - signatures::ForSigType, DidSignatureWithNonce, Policy, PolicyExecutionError, ToStateChange, - }, + common::{signatures::ForSigType, DidSignatureWithNonce, HasPolicy}, deposit_indexed_event, did, - util::{Action, NonceError, WithNonce}, + util::Action, }; use alloc::vec::*; use frame_support::pallet_prelude::*; @@ -101,11 +99,8 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - Self::try_exec_action_over_status_list_credential( - Self::update_, - update_credential, - proof, - ) + update_credential + .execute(|action, credential| credential.execute(Self::update_, action, proof)) } /// Removes `StatusListCredential` associated with the supplied identifier. @@ -117,11 +112,9 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - Self::try_exec_removable_action_over_status_list_credential( - Self::remove_, - remove_credential, - proof, - ) + remove_credential.execute_removable(|action, credential| { + HasPolicy::execute_removable(credential, Self::remove_, action, proof) + }) } } diff --git a/pallets/core/src/modules/status_list_credential/tests.rs b/pallets/core/src/modules/status_list_credential/tests.rs index 09f4ddb4f..6359f8266 100644 --- a/pallets/core/src/modules/status_list_credential/tests.rs +++ b/pallets/core/src/modules/status_list_credential/tests.rs @@ -5,7 +5,7 @@ use crate::{ common::{Policy, PolicyValidationError, ToStateChange}, did::Did, tests::common::*, - util::{Action, BoundedBytes, WithNonce}, + util::{Action, ActionExecutionError, BoundedBytes, WithNonce}, }; use alloc::collections::BTreeMap; use frame_support::{assert_noop, assert_ok}; @@ -112,11 +112,9 @@ fn ensure_auth() { }; let old_nonces = get_nonces(signers); let proof = get_pauth(&command, signers); - let res = Mod::try_exec_action_over_status_list_credential( - |_, _| Ok::<_, DispatchError>(()), - command.clone(), - proof, - ); + let res = command.clone().execute(|action, reg| { + reg.execute(|_, _| Ok::<_, DispatchError>(()), action, proof) + }); assert_eq!(res.is_ok(), expect_success); if expect_success { check_nonce_increase(old_nonces, signers); @@ -129,11 +127,9 @@ fn ensure_auth() { let old_nonces = get_nonces(signers); let proof = get_pauth(&command, signers); - let res = Mod::try_exec_action_over_status_list_credential( - |_, _| Ok::<_, DispatchError>(()), - command.clone(), - proof, - ); + let res = command.clone().execute(|action, reg| { + reg.execute(|_, _| Ok::<_, DispatchError>(()), action, proof) + }); assert_eq!(res.is_ok(), expect_success); if expect_success { @@ -333,7 +329,7 @@ fn remove_status_list_credential() { let auth = get_pauth(&remove, &[(did, &keypair)][..]); assert_noop!( Mod::remove(Origin::signed(ABBA), remove, auth), - PolicyExecutionError::NoEntity + ActionExecutionError::NoEntity ); }); } diff --git a/pallets/core/src/modules/status_list_credential/types.rs b/pallets/core/src/modules/status_list_credential/types.rs index 1e36401dc..3668a0e73 100644 --- a/pallets/core/src/modules/status_list_credential/types.rs +++ b/pallets/core/src/modules/status_list_credential/types.rs @@ -2,14 +2,14 @@ use crate::util::hex; use crate::{ common::{HasPolicy, Limits, Policy}, - util::BoundedBytes, + util::{BoundedBytes, StorageRef}, }; use codec::{Decode, Encode, MaxEncodedLen}; use core::fmt::Debug; use frame_support::{traits::Get, DebugNoBound, *}; use sp_runtime::DispatchResult; -use super::{Config, Error}; +use super::{Config, Error, StatusListCredentials}; /// Either [`RevocationList2020Credential`](https://w3c-ccg.github.io/vc-status-rl-2020/#revocationlist2020credential) /// or [`StatusList2021Credential`](https://www.w3.org/TR/vc-status-list/#statuslist2021credential). @@ -129,3 +129,14 @@ impl From> for StatusListCredential pub struct StatusListCredentialId(#[cfg_attr(feature = "serde", serde(with = "hex"))] pub [u8; 32]); crate::impl_wrapper!(StatusListCredentialId([u8; 32])); + +impl StorageRef for StatusListCredentialId { + type Value = StatusListCredentialWithPolicy; + + fn try_mutate_associated(self, f: F) -> Result + where + F: FnOnce(&mut Option>) -> Result, + { + StatusListCredentials::::try_mutate_exists(self, f) + } +} diff --git a/pallets/core/src/modules/trust_registry/benchmarks.rs b/pallets/core/src/modules/trust_registry/benchmarks.rs index 4ad02d4eb..6a05f56ff 100644 --- a/pallets/core/src/modules/trust_registry/benchmarks.rs +++ b/pallets/core/src/modules/trust_registry/benchmarks.rs @@ -2,13 +2,12 @@ use super::*; use crate::{ common::state_change::ToStateChange, did::{Did, DidSignature, UncheckedDidKey}, - util::{batch_update::*, BoundedBytes}, + util::{batch_update::*, Action, WrappedActionWithNonce}, }; use alloc::collections::BTreeMap; use frame_benchmarking::{benchmarks, whitelisted_caller}; use frame_system::RawOrigin; use scale_info::prelude::string::String; -use sp_core::U256; use sp_runtime::traits::TryCollect; #[cfg(not(feature = "std"))] use sp_std::prelude::*; @@ -72,12 +71,11 @@ crate::bench_with_all_pairs! { ).unwrap(); let id = [1u8; 32].into(); - let init_trust_registry = InitTrustRegistry { + WrappedActionWithNonce::::new(1u32.into(), Convener(did.into()), InitTrustRegistry { registry_id: TrustRegistryId(id), nonce: 1u32.into(), name: (0..10).map(|idx| (98 + idx) as u8 as char).collect::().try_into().unwrap() - }; - Pallet::::init_trust_registry_(init_trust_registry, Convener(did.into())).unwrap(); + }).execute::(|action, set| Pallet::::init_trust_registry_(action.action, set, Convener(did.into()))).unwrap(); let schemas: BTreeMap<_, _> = (0..s) .map(|idx| @@ -135,7 +133,7 @@ crate::bench_with_all_pairs! { nonce: 1u32.into(), name: (0..10).map(|idx| (98 + idx) as u8 as char).collect::().try_into().unwrap() }; - Pallet::::init_trust_registry_(init_trust_registry, Convener(did.into())).unwrap(); + WrappedActionWithNonce::::new(1u32.into(), Convener(did.into()), init_trust_registry.clone()).execute::(|action, set| Pallet::::init_trust_registry_(action.action, set, Convener(did.into()))).unwrap(); let mut schemas: BTreeMap<_, _> = (0..s) .map(|idx| @@ -158,14 +156,11 @@ crate::bench_with_all_pairs! { ) ).collect(); - Pallet::::add_schema_metadata_( - AddSchemaMetadata { - registry_id: TrustRegistryId(id), - schemas: schemas.clone(), - nonce: 2u32.into() - }, - Convener(did.into()) - ).unwrap(); + AddSchemaMetadata { + registry_id: TrustRegistryId(id), + schemas: schemas.clone(), + nonce: 2u32.into() + }.execute(|action, set| Pallet::::add_schema_metadata_(action, set, Convener(did.into()))).unwrap(); let update_issuers = schemas.keys().map( |schema_id| { @@ -238,7 +233,7 @@ crate::bench_with_all_pairs! { nonce: 1u32.into(), name: (0..10).map(|idx| (98 + idx) as u8 as char).collect::().try_into().unwrap() }; - Pallet::::init_trust_registry_(init_trust_registry.clone(), Convener(did.into())).unwrap(); + WrappedActionWithNonce::::new(1u32.into(), Convener(did.into()), init_trust_registry.clone()).execute::(|action, set| Pallet::::init_trust_registry_(action.action, set, Convener(did.into()))).unwrap(); let delegated = DelegatedIssuers((0..i).map(|idx| Issuer(Did([idx as u8; 32]).into())).try_collect().unwrap()); @@ -286,7 +281,7 @@ crate::bench_with_all_pairs! { nonce: 1u32.into(), name: (0..10).map(|idx| (98 + idx) as u8 as char).collect::().try_into().unwrap() }; - Pallet::::init_trust_registry_(init_trust_registry.clone(), Convener(did.into())).unwrap(); + WrappedActionWithNonce::::new(1u32.into(), Convener(did.into()), init_trust_registry.clone()).execute::(|action, set| Pallet::::init_trust_registry_(action.action, set, Convener(did.into()))).unwrap(); let issuers: Vec<_> = (0..i).map(|idx| Issuer(Did([idx as u8; 32]).into())).collect(); @@ -331,7 +326,7 @@ crate::bench_with_all_pairs! { nonce: 1u32.into(), name: (0..10).map(|idx| (98 + idx) as u8 as char).collect::().try_into().unwrap() }; - Pallet::::init_trust_registry_(init_trust_registry.clone(), Convener(did.into())).unwrap(); + WrappedActionWithNonce::::new(1u32.into(), Convener(did.into()), init_trust_registry.clone()).execute::(|action, set| Pallet::::init_trust_registry_(action.action, set, Convener(did.into()))).unwrap(); let issuers: Vec<_> = (0..i).map(|idx| Issuer(Did([idx as u8; 32]).into())).collect(); diff --git a/pallets/core/src/modules/trust_registry/impl.rs b/pallets/core/src/modules/trust_registry/impl.rs index 94dd015a1..f83a7376c 100644 --- a/pallets/core/src/modules/trust_registry/impl.rs +++ b/pallets/core/src/modules/trust_registry/impl.rs @@ -12,6 +12,7 @@ impl Pallet { InitTrustRegistry { registry_id, name, .. }: InitTrustRegistry, + registries: &mut TrustRegistryIdSet, convener: Convener, ) -> DispatchResult { TrustRegistriesInfo::::try_mutate(registry_id, |info| { @@ -21,12 +22,10 @@ impl Pallet { } } - ConvenerTrustRegistries::::try_mutate(convener, |registry| { - registry - .try_insert(registry_id) - .map(drop) - .map_err(|_| Error::::TooManyRegistries) - }) + registries + .try_insert(registry_id) + .map(drop) + .map_err(|_| Error::::TooManyRegistries) })?; deposit_indexed_event!(TrustRegistryInitialized(registry_id)); @@ -39,9 +38,10 @@ impl Pallet { schemas, .. }: AddSchemaMetadata, + registry_info: &mut TrustRegistryInfo, convener: Convener, ) -> DispatchResult { - convener.ensure_controls::(registry_id)?; + convener.ensure_controls::(registry_info)?; for schema_id in schemas.keys() { ensure!( @@ -84,6 +84,7 @@ impl Pallet { schemas, .. }: UpdateSchemaMetadata, + registry_info: &mut TrustRegistryInfo, actor: ConvenerOrIssuerOrVerifier, ) -> Result<(u32, u32, u32), DispatchError> { let (verifiers_len, issuers_len) = @@ -93,7 +94,7 @@ impl Pallet { TrustRegistrySchemasMetadata::::get(schema_id, registry_id) .ok_or(Error::::SchemaMetadataDoesntExist)?; - if Convener(*actor).is_convener_for::(registry_id) { + if Convener(*actor).ensure_controls::(registry_info).is_ok() { update.ensure_valid(&Convener(*actor), &schema_metadata)?; } else { update.ensure_valid(&MaybeIssuerOrVerifier(*actor), &schema_metadata)?; @@ -148,6 +149,7 @@ impl Pallet { delegated, .. }: UpdateDelegatedIssuers, + _: &mut TrustRegistryInfo, issuer: Issuer, ) -> DispatchResult { ensure!( @@ -171,9 +173,10 @@ impl Pallet { issuers, .. }: SuspendIssuers, - actor: Convener, + registry_info: &mut TrustRegistryInfo, + convener: Convener, ) -> DispatchResult { - actor.ensure_controls::(registry_id)?; + convener.ensure_controls::(registry_info)?; for issuer in &issuers { ensure!( @@ -203,9 +206,10 @@ impl Pallet { issuers, .. }: UnsuspendIssuers, - actor: Convener, + registry_info: &mut TrustRegistryInfo, + convener: Convener, ) -> DispatchResult { - actor.ensure_controls::(registry_id)?; + convener.ensure_controls::(registry_info)?; for issuer in &issuers { ensure!( diff --git a/pallets/core/src/modules/trust_registry/mod.rs b/pallets/core/src/modules/trust_registry/mod.rs index 776721065..49501f494 100644 --- a/pallets/core/src/modules/trust_registry/mod.rs +++ b/pallets/core/src/modules/trust_registry/mod.rs @@ -1,10 +1,10 @@ //! Dock Trust Registry. use crate::{ - common::ForSigType, + common::{ForSigType, Signature}, deposit_indexed_event, - did::{self, DidOrDidMethodKeySignature, SignedActionWithNonce}, - util::{BoundedKeyValue, SetOrModify}, + did::{self, DidOrDidMethodKeySignature}, + util::{ActionWithNonce, BoundedKeyValue, SetOrModify, WrappedActionWithNonce}, }; use frame_support::{pallet_prelude::*, weights::PostDispatchInfo}; @@ -161,7 +161,17 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(init_trust_registry, sig).execute(Self::init_trust_registry_) + WrappedActionWithNonce::new( + init_trust_registry.nonce(), + sig.signer(), + init_trust_registry, + ) + .signed(sig) + .execute( + |WrappedActionWithNonce { action, .. }, registries, convener| { + Self::init_trust_registry_(action, registries, convener) + }, + ) } /// Adds a new schema metadata entry (entries). @@ -174,7 +184,9 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(add_schema_metadata, sig).execute(Self::add_schema_metadata_) + add_schema_metadata + .signed(sig) + .execute(Self::add_schema_metadata_) } /// Updates the schema metadata entry (entries) with the supplied identifier(s). @@ -189,7 +201,8 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { ensure_signed(origin)?; - let (ver, iss, schem) = SignedActionWithNonce::new(update_schema_metadata, sig.clone()) + let (ver, iss, schem) = update_schema_metadata + .signed(sig.clone()) .execute(Self::update_schema_metadata_)?; let actual_weight = sig.weight_for_sig_type::( @@ -213,7 +226,8 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(update_delegated_issuers, sig) + update_delegated_issuers + .signed(sig) .execute(Self::update_delegated_issuers_) } @@ -226,7 +240,7 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(suspend_issuers, sig).execute(Self::suspend_issuers_) + suspend_issuers.signed(sig).execute(Self::suspend_issuers_) } /// Unsuspends given `Issuer`s. @@ -238,7 +252,9 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - SignedActionWithNonce::new(unsuspend_issuers, sig).execute(Self::unsuspend_issuers_) + unsuspend_issuers + .signed(sig) + .execute(Self::unsuspend_issuers_) } } } diff --git a/pallets/core/src/modules/trust_registry/tests.rs b/pallets/core/src/modules/trust_registry/tests.rs index 7dc304aef..34ee76a67 100644 --- a/pallets/core/src/modules/trust_registry/tests.rs +++ b/pallets/core/src/modules/trust_registry/tests.rs @@ -4,7 +4,9 @@ use super::*; use crate::{ did::base::*, tests::common::*, - util::{AddOrRemoveOrModify, MultiTargetUpdate, OnlyExistent, SetOrModify, UpdateError}, + util::{ + Action, AddOrRemoveOrModify, MultiTargetUpdate, OnlyExistent, SetOrModify, UpdateError, + }, }; use alloc::collections::{BTreeMap, BTreeSet}; use frame_support::{assert_noop, assert_ok}; @@ -139,7 +141,7 @@ crate::did_or_did_method_key! { }; let alice = 1u64; - Mod::init_trust_registry_(init_trust_registry.clone(), Convener(convener.into())).unwrap(); + WrappedActionWithNonce::::new(2, Convener(convener.into()), init_trust_registry.clone()).execute::(|action, set| Mod::init_trust_registry_(action.action, set, Convener(convener.into()))).unwrap(); let schemas: BTreeMap<_, _> = [( TrustRegistrySchemaId(rand::random()), @@ -181,7 +183,7 @@ crate::did_or_did_method_key! { nonce: 3, }; - Mod::add_schema_metadata_(add_schema_metadata, Convener(convener.into())).unwrap(); + add_schema_metadata.execute(|action, reg| Mod::add_schema_metadata_(action, reg, Convener(convener.into()))).unwrap(); let suspend_issuers = SuspendIssuers { issuers: schemas @@ -271,7 +273,11 @@ crate::did_or_did_method_key! { }; let alice = 1u64; - Mod::init_trust_registry_(init_trust_registry.clone(), Convener(convener.into())).unwrap(); + WrappedActionWithNonce::::new( + init_trust_registry.nonce(), + Convener(convener.into()), + init_trust_registry.clone(), + ).execute::(|action, reg| Mod::init_trust_registry_(action.action, reg, Convener(convener.into()))).unwrap(); let delegated = DelegatedIssuers( (0..10) @@ -578,17 +584,16 @@ crate::did_or_did_method_key! { TrustRegistrySchemaMetadata, >| { assert_noop!( - Mod::update_schema_metadata_( - update.clone(), + update.clone().execute(|action, reg| Mod::update_schema_metadata_(action, reg, ConvenerOrIssuerOrVerifier(verifier.into()) - ), + )), UpdateError::InvalidActor ); - assert_ok!(Mod::update_schema_metadata_( - update, + assert_ok!(update.execute(|action, reg| Mod::update_schema_metadata_(action, reg, + ConvenerOrIssuerOrVerifier(issuer.into()) - )); + ))); let schema = schemas.get_mut(&schema_ids[0]).unwrap(); let issuer = schema.issuers.get_mut(&Issuer(issuer.into())).unwrap(); @@ -631,17 +636,15 @@ crate::did_or_did_method_key! { TrustRegistrySchemaMetadata, >| { assert_noop!( - Mod::update_schema_metadata_( - update.clone(), + update.clone().execute(|action, reg| Mod::update_schema_metadata_(action, reg, ConvenerOrIssuerOrVerifier(issuer.into()) - ), + )), UpdateError::InvalidActor ); assert_noop!( - Mod::update_schema_metadata_( - update, + update.execute(|action, reg| Mod::update_schema_metadata_(action, reg, ConvenerOrIssuerOrVerifier(verifier.into()) - ), + )), UpdateError::InvalidActor ); }, @@ -671,17 +674,15 @@ crate::did_or_did_method_key! { TrustRegistrySchemaMetadata, >| { assert_noop!( - Mod::update_schema_metadata_( - update.clone(), + update.clone().execute(|action, reg| Mod::update_schema_metadata_(action, reg, ConvenerOrIssuerOrVerifier(issuer.into()) - ), + )), UpdateError::InvalidActor ); assert_noop!( - Mod::update_schema_metadata_( - update, + update.execute(|action, reg| Mod::update_schema_metadata_(action, reg, ConvenerOrIssuerOrVerifier(verifier.into()) - ), + )), UpdateError::InvalidActor ); }, @@ -724,10 +725,10 @@ crate::did_or_did_method_key! { TrustRegistrySchemaId, TrustRegistrySchemaMetadata, >| { - assert_ok!(Mod::update_schema_metadata_( - update, + assert_ok!(update.execute(|action, reg| Mod::update_schema_metadata_(action, reg, + ConvenerOrIssuerOrVerifier(convener.into()) - ),); + )),); let schema = schemas.get_mut(&schema_ids[0]).unwrap(); let key = *schema.issuers.keys().nth(2).unwrap(); @@ -762,17 +763,15 @@ crate::did_or_did_method_key! { TrustRegistrySchemaMetadata, >| { assert_noop!( - Mod::update_schema_metadata_( - update.clone(), + update.clone().execute(|action, reg| Mod::update_schema_metadata_(action, reg, ConvenerOrIssuerOrVerifier(random_did.into()) - ), + )), UpdateError::InvalidActor ); assert_noop!( - Mod::update_schema_metadata_( - update, + update.execute(|action, reg| Mod::update_schema_metadata_(action, reg, ConvenerOrIssuerOrVerifier(convener.into()) - ), + )), UpdateError::DoesntExist ); }, @@ -827,10 +826,9 @@ crate::did_or_did_method_key! { TrustRegistrySchemaMetadata, >| { assert_noop!( - Mod::update_schema_metadata_( - update.clone(), + update.clone().execute(|action, reg| Mod::update_schema_metadata_(action, reg, ConvenerOrIssuerOrVerifier(issuer.into()) - ), + )), UpdateError::InvalidActor ); @@ -841,17 +839,16 @@ crate::did_or_did_method_key! { .unwrap()) .into(); assert_noop!( - Mod::update_schema_metadata_( - update.clone(), + update.clone().execute(|action, reg| Mod::update_schema_metadata_(action, reg, ConvenerOrIssuerOrVerifier(issuer_3) - ), + )), UpdateError::InvalidActor ); - assert_ok!(Mod::update_schema_metadata_( - update, + assert_ok!(update.execute(|action, reg| Mod::update_schema_metadata_(action, reg, + ConvenerOrIssuerOrVerifier(convener.into()) - ),); + )),); schema_1 .issuers @@ -896,17 +893,15 @@ crate::did_or_did_method_key! { TrustRegistrySchemaMetadata, >| { assert_noop!( - Mod::update_schema_metadata_( - update.clone(), + update.clone().execute(|action, reg| Mod::update_schema_metadata_(action, reg, ConvenerOrIssuerOrVerifier(issuer.into()) - ), + )), UpdateError::InvalidActor ); assert_noop!( - Mod::update_schema_metadata_( - update, + update.execute(|action, reg| Mod::update_schema_metadata_(action, reg, ConvenerOrIssuerOrVerifier(verifier.into()) - ), + )), UpdateError::InvalidActor ); }, @@ -936,10 +931,9 @@ crate::did_or_did_method_key! { TrustRegistrySchemaMetadata, >| { assert_noop!( - Mod::update_schema_metadata_( - update, + update.execute(|action, reg| Mod::update_schema_metadata_(action, reg, ConvenerOrIssuerOrVerifier(issuer.into()) - ), + )), UpdateError::CapacityOverflow ); }, @@ -971,10 +965,9 @@ crate::did_or_did_method_key! { TrustRegistrySchemaMetadata, >| { assert_noop!( - Mod::update_schema_metadata_( - update, + update.execute(|action, reg| Mod::update_schema_metadata_(action, reg, ConvenerOrIssuerOrVerifier(convener.into()) - ), + )), UpdateError::CapacityOverflow ); }, @@ -1004,10 +997,9 @@ crate::did_or_did_method_key! { TrustRegistrySchemaMetadata, >| { assert_ok!( - Mod::update_schema_metadata_( - update, - ConvenerOrIssuerOrVerifier(issuer.into()) - ), + update.execute( + |action, registry| Mod::update_schema_metadata_(action, registry, ConvenerOrIssuerOrVerifier(issuer.into())) + ) ); let schema_0 = schemas.get_mut(&schema_ids[0]).unwrap(); @@ -1043,16 +1035,15 @@ crate::did_or_did_method_key! { TrustRegistrySchemaMetadata, >| { assert_noop!( - Mod::update_schema_metadata_( - update.clone(), + update.clone().execute(|action, reg| Mod::update_schema_metadata_(action, reg, ConvenerOrIssuerOrVerifier(issuer.into()) - ), + )), UpdateError::InvalidActor ); - assert_ok!(Mod::update_schema_metadata_( - update, + assert_ok!(update.execute(|action, reg| Mod::update_schema_metadata_(action, reg, + ConvenerOrIssuerOrVerifier(verifier.into()) - )); + ))); let schema = schemas.get_mut(&schema_ids[0]).unwrap(); schema.verifiers.remove(&Verifier(verifier.into())); @@ -1077,16 +1068,15 @@ crate::did_or_did_method_key! { TrustRegistrySchemaMetadata, >| { assert_noop!( - Mod::update_schema_metadata_( - update.clone(), + update.clone().execute(|action, reg| Mod::update_schema_metadata_(action, reg, ConvenerOrIssuerOrVerifier(verifier.into()) - ), + )), UpdateError::InvalidActor ); - assert_ok!(Mod::update_schema_metadata_( - update, + assert_ok!(update.execute(|action, reg| Mod::update_schema_metadata_(action, reg, + ConvenerOrIssuerOrVerifier(convener.into()) - )); + ))); let schema = schemas.get_mut(&schema_ids[0]).unwrap(); schema @@ -1110,16 +1100,15 @@ crate::did_or_did_method_key! { TrustRegistrySchemaMetadata, >| { assert_noop!( - Mod::update_schema_metadata_( - update.clone(), + update.clone().execute(|action, reg| Mod::update_schema_metadata_(action, reg, ConvenerOrIssuerOrVerifier(issuer.into()) - ), + )), UpdateError::InvalidActor ); - assert_ok!(Mod::update_schema_metadata_( - update, + assert_ok!(update.execute(|action, reg| Mod::update_schema_metadata_(action, reg, + ConvenerOrIssuerOrVerifier(convener.into()) - )); + ))); let schema = schemas.get_mut(&schema_ids[0]).unwrap(); *schema.verifiers = Default::default(); diff --git a/pallets/core/src/modules/trust_registry/types.rs b/pallets/core/src/modules/trust_registry/types.rs index c00acf2d6..b4aa9693d 100644 --- a/pallets/core/src/modules/trust_registry/types.rs +++ b/pallets/core/src/modules/trust_registry/types.rs @@ -1,11 +1,11 @@ -use super::{Config, Error}; +use super::{Config, ConvenerTrustRegistries, Error, TrustRegistriesInfo}; #[cfg(feature = "serde")] use crate::util::{btree_map, btree_set, hex}; use crate::{ - common::Limits, - did::{AuthorizeTarget, DidKey, DidMethodKey, DidOrDidMethodKey}, + common::{AuthorizeTarget, Limits}, + did::{DidKey, DidMethodKey, DidOrDidMethodKey}, impl_wrapper, - util::{batch_update::*, BoundedKeyValue}, + util::{batch_update::*, BoundedKeyValue, OptionExt, StorageRef}, }; use alloc::collections::BTreeMap; use codec::{Decode, Encode, MaxEncodedLen}; @@ -14,8 +14,6 @@ use frame_support::*; use sp_std::prelude::*; use utils::BoundedString; -use super::TrustRegistriesInfo; - /// Trust registry `Convener`'s `DID`. #[derive(Encode, Decode, Clone, Debug, Copy, PartialEq, Eq, Ord, PartialOrd, MaxEncodedLen)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -26,9 +24,20 @@ pub struct Convener(pub DidOrDidMethodKey); impl_wrapper!(Convener(DidOrDidMethodKey)); -impl AuthorizeTarget<(), DidKey> for Convener {} +impl StorageRef for Convener { + type Value = TrustRegistryIdSet; + + fn try_mutate_associated(self, f: F) -> Result + where + F: FnOnce(&mut Option>) -> Result, + { + ConvenerTrustRegistries::::try_mutate_exists(self, |entry| f(entry.initialized())) + } +} + +impl AuthorizeTarget for Convener {} impl AuthorizeTarget for Convener {} -impl AuthorizeTarget<(), DidMethodKey> for Convener {} +impl AuthorizeTarget for Convener {} impl AuthorizeTarget for Convener {} /// Maybe an `Issuer` or `Verifier` but definitely not a `Convener`. @@ -42,18 +51,11 @@ pub struct MaybeIssuerOrVerifier(pub DidOrDidMethodKey); impl_wrapper!(MaybeIssuerOrVerifier(DidOrDidMethodKey)); impl Convener { - pub fn is_convener_for(&self, registry_id: TrustRegistryId) -> bool { - TrustRegistriesInfo::::get(registry_id) - .map_or(false, |TrustRegistryInfo { convener, .. }| { - &convener == self - }) - } - - pub fn ensure_controls(&self, registry_id: TrustRegistryId) -> Result<(), Error> { - ensure!( - self.is_convener_for::(registry_id), - Error::::NotAConvener - ); + pub fn ensure_controls( + &self, + TrustRegistryInfo { convener, .. }: &TrustRegistryInfo, + ) -> Result<(), Error> { + ensure!(convener == self, Error::::NotAConvener); Ok(()) } @@ -471,6 +473,17 @@ pub struct TrustRegistryId(#[cfg_attr(feature = "serde", serde(with = "hex"))] p crate::impl_wrapper!(TrustRegistryId([u8; 32])); +impl StorageRef for TrustRegistryId { + type Value = TrustRegistryInfo; + + fn try_mutate_associated(self, f: F) -> Result + where + F: FnOnce(&mut Option>) -> Result, + { + TrustRegistriesInfo::::try_mutate_exists(self, f) + } +} + /// Unique identifier for the `Trust Registry`. #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, Copy, Ord, PartialOrd, MaxEncodedLen)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/pallets/core/src/util/action.rs b/pallets/core/src/util/action.rs index 9798eb3c5..abb21095a 100644 --- a/pallets/core/src/util/action.rs +++ b/pallets/core/src/util/action.rs @@ -1,8 +1,8 @@ -use codec::FullCodec; -use frame_support::{ensure, StorageMap}; +use core::marker::PhantomData; +use frame_support::ensure; use sp_runtime::DispatchError; -use crate::common::Types; +use crate::{common::Types, util::OptionExt}; use super::{NonceError, WithNonce}; @@ -21,6 +21,46 @@ pub trait Action: Sized { fn is_empty(&self) -> bool { self.len() == 0 } + + /// Executes an action providing a mutable reference to the option containing a value associated with the target. + fn execute(self, f: F) -> Result + where + F: FnOnce(Self, &mut >::Value) -> Result, + E: From + From, + Self::Target: StorageRef, + { + ensure!(!self.is_empty(), ActionExecutionError::EmptyPayload); + + self.target().try_mutate_associated(|data_opt| { + ensure!(data_opt.is_some(), ActionExecutionError::NoEntity); + + data_opt.update_with(|opt| { + ensure!(opt.is_some(), ActionExecutionError::ConversionError); + + f(self, opt.as_mut().unwrap()) + }) + }) + } + + /// Executes an action providing a mutable reference to the value associated with the target. + fn execute_removable(self, f: F) -> Result + where + F: FnOnce(Self, &mut Option<>::Value>) -> Result, + E: From + From, + Self::Target: StorageRef, + { + ensure!(!self.is_empty(), ActionExecutionError::EmptyPayload); + + self.target().try_mutate_associated(|data_opt| { + ensure!(data_opt.is_some(), ActionExecutionError::NoEntity); + + data_opt.update_with(|opt| { + ensure!(opt.is_some(), ActionExecutionError::ConversionError); + + f(self, opt) + }) + }) + } } /// Describes an action fpwith nonce which can be performed on some `Target` @@ -28,69 +68,111 @@ pub trait ActionWithNonce: Action { /// Returns action's nonce. fn nonce(&self) -> T::BlockNumber; - fn execute(self, f: F) -> Result + /// Executes an action providing a mutable reference to the option containing a value associated with the target. + /// In case of a successful result, the nonce will be increased. + fn execute_and_increase_nonce(self, f: F) -> Result where F: FnOnce(Self, &mut Option) -> Result, - E: From + From, - WithNonce: TryFrom<>>::Value> - + Into<>>::Value>, - Self::Target: StorageMapRef>, + E: From + From, + Self::Target: StorageRef>, { - ensure!(!self.is_empty(), UpdateWithNonceError::EmptyPayload); + ensure!(!self.is_empty(), ActionExecutionError::EmptyPayload); - let key: >>::Key = self.target().into(); + self.target().try_mutate_associated(|details_opt| { + ensure!(details_opt.is_some(), ActionExecutionError::NoEntity); - >>::Storage::try_mutate_exists( - key, - |details_opt| { - WithNonce::try_update_opt_with(details_opt, self.nonce(), |data_opt| { - f(self, data_opt) + details_opt + .update_with(|opt| { + WithNonce::try_update_opt_with(opt, self.nonce(), |data_opt| f(self, data_opt)) }) - .ok_or(UpdateWithNonceError::EntityDoesntExist)? - }, - ) + .ok_or(ActionExecutionError::ConversionError)? + }) } + /// Executes an action providing a mutable reference to the value associated with the target. + /// In case of a successful result, the nonce will be increased. fn execute_without_increasing_nonce(self, f: F) -> Result where F: FnOnce(Self, &mut Option) -> Result, - E: From, - WithNonce: TryFrom<>>::Value> - + Into<>>::Value>, - Self::Target: StorageMapRef>, + E: From, + WithNonce: TryFrom<>::Value> + + Into<>::Value>, + Self::Target: StorageRef>, { - ensure!(!self.is_empty(), UpdateWithNonceError::EmptyPayload); + ensure!(!self.is_empty(), ActionExecutionError::EmptyPayload); - let key: >>::Key = self.target().into(); + self.target().try_mutate_associated(|details_opt| { + ensure!(details_opt.is_some(), ActionExecutionError::NoEntity); - >>::Storage::try_mutate_exists( - key, - |details_opt| { - WithNonce::try_update_opt_without_increasing_nonce_with(details_opt, |data_opt| { - f(self, data_opt) + details_opt + .update_with(|opt| { + WithNonce::try_update_opt_without_increasing_nonce_with(opt, |data_opt| { + f(self, data_opt) + }) }) - .ok_or(UpdateWithNonceError::EntityDoesntExist)? - }, - ) + .ok_or(ActionExecutionError::ConversionError)? + }) + } + + fn signed(self, signature: S) -> SignedActionWithNonce { + SignedActionWithNonce::new(self, signature) } } -pub enum UpdateWithNonceError { - EntityDoesntExist, +pub enum ActionExecutionError { + NoEntity, EmptyPayload, + ConversionError, } -impl From for DispatchError { - fn from(error: UpdateWithNonceError) -> Self { +impl From for DispatchError { + fn from(error: ActionExecutionError) -> Self { match error { - UpdateWithNonceError::EntityDoesntExist => DispatchError::Other("Entity doesn't exist"), - UpdateWithNonceError::EmptyPayload => DispatchError::Other("Payload is empty"), + ActionExecutionError::NoEntity => DispatchError::Other("Entity doesn't exist"), + ActionExecutionError::EmptyPayload => DispatchError::Other("Payload is empty"), + ActionExecutionError::ConversionError => DispatchError::Other("Conversion failed"), + } + } +} + +pub struct SignedActionWithNonce +where + A: ActionWithNonce, +{ + pub action: A, + pub signature: S, + _marker: PhantomData, +} + +impl SignedActionWithNonce +where + A: ActionWithNonce, +{ + pub fn new(action: A, signature: S) -> Self { + Self { + action, + signature, + _marker: PhantomData, } } } -pub trait StorageMapRef: Sized { - type Key: From + FullCodec; - type Value: From + TryInto + FullCodec; - type Storage: StorageMap; +/// Allows mutating a value associated with `Self`. +pub trait StorageRef: Sized { + type Value; + + fn try_mutate_associated(self, f: F) -> Result + where + F: FnOnce(&mut Option) -> Result; +} + +impl StorageRef for () { + type Value = (); + + fn try_mutate_associated(self, f: F) -> Result + where + F: FnOnce(&mut Option<()>) -> Result, + { + f(&mut Some(())) + } } diff --git a/pallets/core/src/util/mod.rs b/pallets/core/src/util/mod.rs index 593db247a..0fa4fad71 100644 --- a/pallets/core/src/util/mod.rs +++ b/pallets/core/src/util/mod.rs @@ -8,6 +8,8 @@ pub mod bytes; pub mod hex; pub mod inc_id; pub mod macros; +pub mod option_ext; +pub mod types; pub mod with_nonce; pub mod wrapped_action_with_nonce; @@ -21,5 +23,7 @@ pub use bytes::*; pub use hex::*; pub use inc_id::*; pub use macros::*; +pub use option_ext::*; +pub use types::*; pub use with_nonce::*; pub use wrapped_action_with_nonce::*; diff --git a/pallets/core/src/util/option_ext.rs b/pallets/core/src/util/option_ext.rs new file mode 100644 index 000000000..f7a7d5c88 --- /dev/null +++ b/pallets/core/src/util/option_ext.rs @@ -0,0 +1,39 @@ +pub trait OptionExt { + fn update_with(&mut self, f: F) -> R + where + F: FnOnce(&mut Option) -> R, + V: TryInto, + S: Into; + + fn initialized(&mut self) -> &mut Self + where + V: Default; +} + +impl OptionExt for Option { + fn update_with(&mut self, f: F) -> R + where + F: FnOnce(&mut Option) -> R, + V: TryInto, + S: Into, + { + let mut entity = self.take().and_then(|opt| opt.try_into().ok()); + + let res = f(&mut entity); + + *self = entity.map(Into::into); + + res + } + + fn initialized(&mut self) -> &mut Self + where + V: Default, + { + if self.is_none() { + self.replace(Default::default()); + } + + self + } +} diff --git a/pallets/core/src/util/types.rs b/pallets/core/src/util/types.rs new file mode 100644 index 000000000..9fe5900a2 --- /dev/null +++ b/pallets/core/src/util/types.rs @@ -0,0 +1,34 @@ +use core::fmt::Debug; +use frame_support::pallet_prelude::*; +use sp_runtime::traits::*; + +/// Defines associated types used by `dock-core`. +pub trait Types: Clone + Eq { + type BlockNumber: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + AtLeast32BitUnsigned + + Default + + Bounded + + Copy + + sp_std::hash::Hash + + sp_std::str::FromStr + + MaybeMallocSizeOf + + MaxEncodedLen + + TypeInfo; + + type AccountId: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + Ord + + MaxEncodedLen; +} + +impl Types for T { + type BlockNumber = T::BlockNumber; + type AccountId = T::AccountId; +} diff --git a/pallets/core/src/util/with_nonce.rs b/pallets/core/src/util/with_nonce.rs index e6d93948b..92e7b7316 100644 --- a/pallets/core/src/util/with_nonce.rs +++ b/pallets/core/src/util/with_nonce.rs @@ -1,6 +1,6 @@ use codec::{Decode, Encode, MaxEncodedLen}; use sp_runtime::{traits::CheckedAdd, DispatchError}; -use sp_std::{convert::TryInto, fmt::Debug}; +use sp_std::fmt::Debug; use crate::common::Types; @@ -105,18 +105,16 @@ impl WithNonce { /// If supplied value is `Some(_)`, attempts to increase current nonce - succeeds if provided nonce is equal to /// current nonce plus 1, otherwise returns an error. If value is `None`, `None` will be returned. - pub fn try_update_opt_with( - this_opt: &mut Option, + pub fn try_update_opt_with( + this_opt: &mut Option, nonce: T::BlockNumber, f: F, ) -> Option> where F: FnOnce(&mut Option) -> Result, E: From, - S: TryInto, - Self: Into, { - let mut mapped_opt = match this_opt.take()?.try_into().ok().map(|mut this| { + let mut mapped_opt = match this_opt.take().map(|mut this| { this.try_update(nonce) .map(drop) .map(|()| this) @@ -126,29 +124,26 @@ impl WithNonce { Ok(this) => Some(this), }; - let res = - Self::try_update_opt_without_increasing_nonce_with::(&mut mapped_opt, f); - *this_opt = mapped_opt.map(Into::into); + let res = Self::try_update_opt_without_increasing_nonce_with(&mut mapped_opt, f); + *this_opt = mapped_opt; res } /// If supplied value is `Some(_)`, will update given entity without increasing nonce. - pub fn try_update_opt_without_increasing_nonce_with( - this_opt: &mut Option, + pub fn try_update_opt_without_increasing_nonce_with( + this_opt: &mut Option, f: F, ) -> Option> where F: FnOnce(&mut Option) -> Result, - S: TryInto, - Self: Into, { - let this = this_opt.take()?.try_into().ok()?; + let this = this_opt.take()?; let Self { data, nonce } = this; let mut data_opt = Some(data); - let res = (f)(&mut data_opt).map_err(Into::into); - *this_opt = data_opt.map(|data| Self { data, nonce }.into()); + let res: Result = (f)(&mut data_opt).map_err(Into::into); + *this_opt = data_opt.map(|data| Self { data, nonce }); Some(res) } diff --git a/pallets/core/src/util/wrapped_action_with_nonce.rs b/pallets/core/src/util/wrapped_action_with_nonce.rs index 1e0485eeb..476f23699 100644 --- a/pallets/core/src/util/wrapped_action_with_nonce.rs +++ b/pallets/core/src/util/wrapped_action_with_nonce.rs @@ -1,4 +1,4 @@ -use crate::common::Types; +use crate::common::{ToStateChange, Types, TypesAndLimits}; use super::{Action, ActionWithNonce}; use codec::{Decode, Encode}; @@ -40,3 +40,12 @@ impl ActionWithNonce for WrappedActionWithNon self.nonce } } + +impl ToStateChange for WrappedActionWithNonce +where + A: ToStateChange, +{ + fn to_state_change(&self) -> crate::common::StateChange<'_, T> { + self.action.to_state_change() + } +}