Browse Source

refactoring payment proof

ignazio 3 years ago
parent
commit
870d5fa30d

+ 0 - 60
runtime-modules/content/src/lemma.rs

@@ -1,60 +0,0 @@
-/// lemma structure for the merkle proof path:
-/// `(leaf, path)` with
-/// `path = [ item1 item2 ... itemN ]` and `item = (hash, side)`
-/// `leaf` is the initial value whose proof membership is to be established
-/// item contains the hash value required for the proof together with the side, that is the provided
-/// hash is to the left or to the right to the current computed hash during verification.
-use codec::{Decode, Encode};
-use core::fmt::Debug;
-
-#[cfg(feature = "std")]
-pub use serde::{Deserialize, Serialize};
-
-use sp_runtime::traits::Hash;
-use sp_std::vec::Vec;
-
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug)]
-pub enum Side {
-    Left,
-    Right,
-}
-
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
-pub struct LemmaItem<HashOutput> {
-    pub hash: HashOutput,
-    pub side: Side,
-}
-
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
-pub struct MerkleProof<Algorithm: Hash, Value> {
-    pub leaf: Value,
-    pub path: Vec<LemmaItem<Algorithm::Output>>,
-}
-
-pub trait CommitmentProof<Algorithm: Hash> {
-    fn verify(&self, root: Algorithm::Output) -> bool;
-}
-
-impl<Algorithm: Hash, Value> CommitmentProof<Algorithm> for MerkleProof<Algorithm, Value>
-where
-    Value: Encode + Decode,
-{
-    fn verify(&self, root: Algorithm::Output) -> bool {
-        let init_hash = <Algorithm as sp_runtime::traits::Hash>::hash(&self.leaf.encode());
-        let candidate = self
-            .path
-            .iter()
-            .fold(init_hash, |hash_v, el| match el.side {
-                Side::Right => {
-                    <Algorithm as sp_runtime::traits::Hash>::hash(&[hash_v, el.hash].encode())
-                }
-                Side::Left => {
-                    <Algorithm as sp_runtime::traits::Hash>::hash(&[el.hash, hash_v].encode())
-                }
-            });
-        candidate == root
-    }
-}

+ 56 - 21
runtime-modules/content/src/lib.rs

@@ -24,14 +24,12 @@
 mod tests;
 use core::marker::PhantomData;
 mod errors;
-mod lemma;
 mod permissions;
 
 use sp_std::cmp::max;
 use sp_std::mem::size_of;
 
 pub use errors::*;
-pub use lemma::*;
 pub use permissions::*;
 
 use codec::Codec;
@@ -336,9 +334,6 @@ pub type ChannelOwnershipTransferRequest<T> = ChannelOwnershipTransferRequestRec
     <T as frame_system::Trait>::AccountId,
 >;
 
-// hash value type used for payment validation
-pub type HashValue<T> = <T as frame_system::Trait>::Hash;
-
 /// Information about channel being created.
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
@@ -660,6 +655,34 @@ impl<ParentPostId> Default for PostTypeRecord<ParentPostId> {
 
 pub type PostType<T> = PostTypeRecord<<T as Trait>::PostId>;
 
+/// Side used to construct hash values during merkle proof verification
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug)]
+pub enum Side {
+    Left,
+    Right,
+}
+
+impl Default for Side {
+    fn default() -> Self {
+        Side::Right
+    }
+}
+
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
+/// Element used in for channel payout
+pub struct ProofElementRecord<Hash, Side> {
+    // Node hash
+    hash: Hash,
+
+    // side in which *self* must be adjoined during proof verification
+    side: Side,
+}
+
+// alias for the proof element
+pub type ProofElement<T> = ProofElementRecord<<T as frame_system::Trait>::Hash, Side>;
+
 /// An enum in order to differenciate between post author and moderator / owner
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
@@ -716,8 +739,6 @@ pub type PullPayment<T> = PullPaymentElement<
     <T as frame_system::Trait>::Hash,
 >;
 
-pub type PullPaymentProof<T> = MerkleProof<<T as frame_system::Trait>::Hashing, PullPayment<T>>;
-
 decl_storage! {
     trait Store for Module<T: Trait> as Content {
         pub ChannelById get(fn channel_by_id): map hasher(blake2_128_concat) T::ChannelId => Channel<T>;
@@ -1942,24 +1963,22 @@ decl_module! {
         #[weight = 10_000_000] // TODO: adjust Weight
         pub fn claim_channel_reward(
             origin,
-            proof: PullPaymentProof<T>,
             actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
+            proof: Vec<ProofElement<T>>,
+            item: PullPayment<T>,
         ) -> DispatchResult {
-            let elem = &proof.leaf;
-            let channel = Self::ensure_channel_validity(&elem.channel_id)?;
+            let channel = Self::ensure_channel_validity(&item.channel_id)?;
 
+            ensure!(channel.reward_account.is_some(), Error::<T>::RewardAccountNotFoundInChannel);
             ensure_actor_authorized_to_claim_payment::<T>(origin, &actor, &channel.owner)?;
 
-            let cashout = elem
+            let cashout = item
                 .amount_earned
                 .saturating_sub(channel.prior_cumulative_cashout);
 
-            ensure!(<MaxRewardAllowed<T>>::get() > elem.amount_earned, Error::<T>::TotalRewardLimitExceeded);
+            ensure!(<MaxRewardAllowed<T>>::get() > item.amount_earned, Error::<T>::TotalRewardLimitExceeded);
             ensure!(<MinCashoutAllowed<T>>::get() < cashout, Error::<T>::InsufficientCashoutAmount);
-
-            ensure!(proof.verify(<Commitment<T>>::get()), Error::<T>::PaymentProofVerificationFailed);
-
-            ensure!(channel.reward_account.is_some(), Error::<T>::RewardAccountNotFoundInChannel);
+            Self::verify_proof(&proof, &item)?;
 
             //
             // == MUTATION SAFE ==
@@ -1967,12 +1986,12 @@ decl_module! {
 
             Self::transfer_reward(cashout, &channel.reward_account.unwrap());
             ChannelById::<T>::mutate(
-                &elem.channel_id,
+                &item.channel_id,
                 |channel| channel.prior_cumulative_cashout =
-                    channel.prior_cumulative_cashout.saturating_add(elem.amount_earned)
+                    channel.prior_cumulative_cashout.saturating_add(item.amount_earned)
             );
 
-            Self::deposit_event(RawEvent::ChannelRewardUpdated(elem.amount_earned, elem.channel_id));
+            Self::deposit_event(RawEvent::ChannelRewardUpdated(item.amount_earned, item.channel_id));
 
             Ok(())
         }
@@ -2243,6 +2262,22 @@ impl<T: Trait> Module<T> {
         Ok(())
     }
 
+    fn verify_proof(proof: &[ProofElement<T>], item: &PullPayment<T>) -> DispatchResult {
+        let candidate_root = proof.iter().fold(
+            <T as frame_system::Trait>::Hashing::hash_of(item),
+            |hash_v, el| match el.side {
+                Side::Right => <T as frame_system::Trait>::Hashing::hash_of(&[hash_v, el.hash]),
+                Side::Left => <T as frame_system::Trait>::Hashing::hash_of(&[el.hash, hash_v]),
+            },
+        );
+        ensure!(
+            candidate_root == Commitment::<T>::get(),
+            Error::<T>::PaymentProofVerificationFailed
+        );
+
+        Ok(())
+    }
+
     // Reset Videos and Channels on runtime upgrade but preserving next ids and categories.
     pub fn on_runtime_upgrade() {
         // setting final index triggers migration
@@ -2284,7 +2319,7 @@ decl_event!(
         MemberId = <T as MembershipTypes>::MemberId,
         ReactionId = <T as Trait>::ReactionId,
         ModeratorSet = BTreeSet<<T as MembershipTypes>::MemberId>,
-        HashValue = <T as frame_system::Trait>::Hash,
+        Hash = <T as frame_system::Trait>::Hash,
         Balance = BalanceOf<T>,
     {
         // Curators
@@ -2397,7 +2432,7 @@ decl_event!(
         ModeratorSetUpdated(ChannelId, ModeratorSet),
 
         // Rewards
-        CommitmentUpdated(HashValue),
+        CommitmentUpdated(Hash),
         ChannelRewardUpdated(Balance, ChannelId),
         MaxRewardUpdated(Balance),
         MinCashoutUpdated(Balance),

+ 14 - 5
runtime-modules/content/src/tests/fixtures.rs

@@ -4,6 +4,7 @@ use crate::*;
 use frame_support::assert_ok;
 use frame_support::traits::Currency;
 use sp_std::cmp::min;
+use sp_std::iter::{IntoIterator, Iterator};
 
 // fixtures
 pub struct CreateChannelFixture {
@@ -1217,7 +1218,7 @@ impl UpdateMinCashoutFixture {
 
 pub struct UpdateCommitmentValueFixture {
     sender: AccountId,
-    new_commitment: HashValue<Test>,
+    new_commitment: <Test as frame_system::Trait>::Hash,
 }
 
 impl UpdateCommitmentValueFixture {
@@ -1253,6 +1254,13 @@ impl UpdateCommitmentValueFixture {
     }
 }
 
+pub struct ClaimChannelReward {
+    sender: AccountId,
+    actor: ContentActor<CuratorGroupId, CuratorId, MemberId>,
+    proof: Vec<ProofElement<Test>>,
+    itme: PullPayment<Test>,
+}
+
 // helper functions
 pub fn increase_account_balance_helper(account_id: u64, balance: u64) {
     let _ = Balances::<Test>::deposit_creating(&account_id, balance.into());
@@ -1481,9 +1489,10 @@ fn index_path_helper(len: usize, index: usize) -> Vec<IndexItem> {
     }
     return path;
 }
+
 fn generate_merkle_root_helper<E: Encode>(
     collection: &[E],
-) -> Result<Vec<HashValue<Test>>, &'static str> {
+) -> Result<Vec<HashOutput>, &'static str> {
     // generates merkle root from the ordered sequence collection.
     // The resulting vector is structured as follows: elements in range
     // [0..collection.len()) will be the tree leaves (layer 0), elements in range
@@ -1530,13 +1539,13 @@ fn generate_merkle_root_helper<E: Encode>(
 fn build_merkle_path_helper<E: Encode + Clone>(
     collection: &[E],
     idx: usize,
-    merkle_tree: &[HashValue<Test>],
-) -> Vec<LemmaItemTest> {
+    merkle_tree: &[<Test as frame_system::Trait>::Hash],
+) -> Vec<ProofElement<Test>> {
     // builds the actual merkle path with the hashes needed for the proof
     let index_path = index_path_helper(collection.len(), idx + 1);
     index_path
         .iter()
-        .map(|idx_item| LemmaItemTest {
+        .map(|idx_item| ProofElement::<Test> {
             hash: merkle_tree[idx_item.index - 1],
             side: idx_item.side,
         })

+ 1 - 2
runtime-modules/content/src/tests/mock.rs

@@ -21,9 +21,8 @@ pub type Content = Module<Test>;
 pub type CollectiveFlip = randomness_collective_flip::Module<Test>;
 
 /// Type aliases
+pub type HashOutput = <Test as frame_system::Trait>::Hash;
 pub type Hashing = <Test as frame_system::Trait>::Hashing;
-pub type LemmaItemTest = LemmaItem<HashValue<Test>>;
-pub type Proof<Value> = MerkleProof<Hashing, Value>;
 pub type AccountId = <Test as frame_system::Trait>::AccountId;
 pub type VideoId = <Test as Trait>::VideoId;
 pub type PostId = <Test as Trait>::PostId;