Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
olegnn committed Oct 22, 2023
1 parent 48bdea2 commit 5c0388b
Show file tree
Hide file tree
Showing 45 changed files with 1,162 additions and 969 deletions.
58 changes: 58 additions & 0 deletions pallets/core/src/common/authorization.rs
Original file line number Diff line number Diff line change
@@ -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<Target, Key> {
fn ensure_authorizes_target<T: crate::did::Config, A>(
&self,
_: &Key,
_: &A,
) -> Result<(), crate::did::Error<T>>
where
A: Action<Target = Target>,
{
Ok(())
}
}

type AuthorizationResult<T, S> = Result<
Option<Authorization<<S as Signature>::Signer, <S as Signature>::Key>>,
crate::did::Error<T>,
>;

/// Authorizes signed action.
pub trait AuthorizeSignedAction<A: Action>: Signature
where
Self::Signer: AuthorizeTarget<A::Target, Self::Key>,
{
fn authorizes_signed_action<T: crate::did::Config>(
&self,
action: &A,
) -> AuthorizationResult<T, Self>
where
A: ToStateChange<T>,
{
let signer_pubkey = self.key::<T>().ok_or(did::Error::<T>::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<S, K> {
pub signer: S,
pub key: K,
}
4 changes: 4 additions & 0 deletions pallets/core/src/common/mod.rs
Original file line number Diff line number Diff line change
@@ -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::*;
Expand Down
154 changes: 94 additions & 60 deletions pallets/core/src/common/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -121,72 +119,24 @@ impl<T: Limits> Policy<T> {
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<V, S, F, R, E>(
entity: &mut Option<V>,
f: F,
action: S,
proof: Vec<DidSignatureWithNonce<T>>,
) -> Result<R, E>
where
T: crate::did::Config,
V: HasPolicy<T>,
F: FnOnce(S, &mut Option<V>) -> Result<R, E>,
WithNonce<T, S>: ActionWithNonce<T> + ToStateChange<T>,
Did: AuthorizeTarget<<WithNonce<T, S> as Action>::Target, DidKey>,
DidMethodKey: AuthorizeTarget<<WithNonce<T, S> as Action>::Target, DidMethodKey>,
E: From<PolicyExecutionError>
+ From<did::Error<T>>
+ From<NonceError>
+ From<UpdateWithNonceError>,
{
// 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<T: did::Config, A, R, E, D>(
action: A,
data: &mut Option<D>,
f: impl FnOnce(A, &mut Option<D>) -> Result<R, E>,
data: &mut D,
f: impl FnOnce(A, &mut D) -> Result<R, E>,
proof: &mut impl Iterator<Item = DidSignatureWithNonce<T>>,
) -> Result<R, E>
where
E: From<UpdateWithNonceError> + From<NonceError> + From<did::Error<T>>,
E: From<ActionExecutionError> + From<NonceError> + From<did::Error<T>>,
WithNonce<T, A>: ActionWithNonce<T> + ToStateChange<T>,
Did: AuthorizeTarget<<WithNonce<T, A> as Action>::Target, DidKey>,
DidMethodKey: AuthorizeTarget<<WithNonce<T, A> as Action>::Target, DidMethodKey>,
<WithNonce<T, A> as Action>::Target: StorageRef<T>,
{
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)
}
Expand All @@ -209,9 +159,93 @@ impl<T> AuthorizeTarget<T, DidMethodKey> for PolicyExecutor {}
pub type DidSignatureWithNonce<T> = WithNonce<T, DidOrDidMethodKeySignature<PolicyExecutor>>;

/// Denotes an entity which has an associated `Policy`.
pub trait HasPolicy<T: Limits> {
pub trait HasPolicy<T: Limits>: Sized {
/// Returns underlying `Policy`.
fn policy(&self) -> &Policy<T>;

/// 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<A, F, R, E>(
&mut self,
f: F,
action: A,
proof: Vec<DidSignatureWithNonce<T>>,
) -> Result<R, E>
where
T: crate::did::Config,
F: FnOnce(A, &mut Self) -> Result<R, E>,
WithNonce<T, A>: ActionWithNonce<T> + ToStateChange<T>,
<WithNonce<T, A> as Action>::Target: StorageRef<T>,
E: From<PolicyExecutionError>
+ From<did::Error<T>>
+ From<NonceError>
+ From<ActionExecutionError>,
{
// 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<A, F, R, E>(
this_opt: &mut Option<Self>,
f: F,
action: A,
proof: Vec<DidSignatureWithNonce<T>>,
) -> Result<R, E>
where
T: crate::did::Config,
F: FnOnce(A, &mut Option<Self>) -> Result<R, E>,
WithNonce<T, A>: ActionWithNonce<T> + ToStateChange<T>,
<WithNonce<T, A> as Action>::Target: StorageRef<T>,
E: From<PolicyExecutionError>
+ From<did::Error<T>>
+ From<NonceError>
+ From<ActionExecutionError>,
{
// 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.
Expand Down
18 changes: 15 additions & 3 deletions pallets/core/src/common/signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: crate::did::Config>(&self) -> Option<Self::Key>;

fn verify_raw_bytes(&self, message: &[u8], key: &Self::Key) -> Result<bool, VerificationError>;
}

#[derive(PartialEq, Eq, Encode, Decode, Clone, Debug, Default)]
pub struct SigTypes<V> {
sr: V,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -166,7 +178,7 @@ pub enum SigValue {

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VerificationError {
IncompatibleKey(PublicKey),
IncompatibleKey,
}

impl SigValue {
Expand Down Expand Up @@ -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)
Expand Down
51 changes: 51 additions & 0 deletions pallets/core/src/common/signed_action.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use crate::{
common::{Authorization, AuthorizeSignedAction, AuthorizeTarget, ToStateChange},
did::*,
util::{action::*, with_nonce::*, WrappedActionWithNonce},
};
use core::ops::Deref;

impl<T: Config, A, Sig> SignedActionWithNonce<T, A, Sig>
where
A: ActionWithNonce<T> + ToStateChange<T>,
Sig: AuthorizeSignedAction<A>,
Sig::Signer: AuthorizeTarget<A::Target, Sig::Key> + 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<F, S, R, E>(self, f: F) -> Result<R, E>
where
F: FnOnce(A, &mut <A::Target as StorageRef<T>>::Value, Sig::Signer) -> Result<R, E>,
E: From<ActionExecutionError> + From<NonceError> + From<Error<T>>,
A::Target: StorageRef<T>,
<Sig::Signer as Deref>::Target: StorageRef<T, Value = WithNonce<T, S>> + 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<F, S, R, E>(self, f: F) -> Result<R, E>
where
F: FnOnce(A, &mut Option<<A::Target as StorageRef<T>>::Value>, Sig::Signer) -> Result<R, E>,
E: From<ActionExecutionError> + From<NonceError> + From<Error<T>>,
A::Target: StorageRef<T>,
<Sig::Signer as Deref>::Target: StorageRef<T, Value = WithNonce<T, S>> + Clone,
{
let SignedActionWithNonce {
action, signature, ..
} = self;

let Authorization { signer, .. } = signature
.authorizes_signed_action(&action)?
.ok_or(Error::<T>::InvalidSignature)?;

WrappedActionWithNonce::<T, _, _>::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)
}
}
Loading

0 comments on commit 5c0388b

Please sign in to comment.