1
// Copyright (C) Moondance Labs Ltd.
2
// This file is part of Tanssi.
3

            
4
// Tanssi is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Tanssi is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
16

            
17
//! Commitment recorder pallet.
18
//!
19
//! A pallet to record outbound message commitment.
20
//!
21

            
22
#![cfg_attr(not(feature = "std"), no_std)]
23

            
24
pub use pallet::*;
25

            
26
#[frame_support::pallet]
27
pub mod pallet {
28
    use snowbridge_merkle_tree::{merkle_proof, MerkleProof};
29
    use {
30
        frame_support::pallet_prelude::*, snowbridge_merkle_tree::merkle_root, sp_core::H256,
31
        sp_runtime::traits::Hash, sp_runtime::Vec,
32
    };
33

            
34
    /// The current storage version.
35
    const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
36

            
37
    /// Configure the pallet by specifying the parameters and types on which it depends.
38
    #[pallet::config]
39
    pub trait Config: frame_system::Config {
40
        type Hashing: Hash<Output = H256>;
41
    }
42

            
43
    #[pallet::pallet]
44
    #[pallet::storage_version(STORAGE_VERSION)]
45
    pub struct Pallet<T>(_);
46

            
47
    #[pallet::event]
48
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
49
    pub enum Event<T: Config> {
50
        NewCommitmentRootRecorded { commitment: H256 },
51
        CommitmentRootRead { commitment: H256 },
52
    }
53

            
54
    /// Message commitment from last block (v1).
55
    /// This will be set only when there are messages to relay.
56
    #[pallet::storage]
57
    #[pallet::unbounded]
58
    pub type RecordedCommitment<T: Config> = StorageValue<_, (H256, Vec<H256>), OptionQuery>;
59

            
60
    /// Message commitment from last block (v2).
61
    /// This will be set only when there are messages to relay.
62
    #[pallet::storage]
63
    #[pallet::unbounded]
64
    pub type RecordedCommitmentV2<T: Config> = StorageValue<_, (H256, Vec<H256>), OptionQuery>;
65

            
66
    impl<T: Config> Pallet<T> {
67
8
        fn get_combined_leaves(
68
8
            leaves_v1: Option<Vec<H256>>,
69
8
            leaves_v2: Option<Vec<H256>>,
70
8
        ) -> Option<Vec<H256>> {
71
8
            match (leaves_v1, leaves_v2) {
72
                (None, None) => None,
73
3
                (Some(leaves_v1), None) => Some(leaves_v1),
74
1
                (None, Some(leaves_v2)) => Some(leaves_v2),
75
4
                (Some(mut leaves_v1), Some(mut leaves_v2)) => {
76
4
                    leaves_v1.append(&mut leaves_v2);
77
4
                    Some(leaves_v1)
78
                }
79
            }
80
8
        }
81

            
82
10694
        pub fn take_commitment_root() -> Option<H256> {
83
10694
            let v1 = RecordedCommitment::<T>::take();
84
10694
            let v2 = RecordedCommitmentV2::<T>::take();
85
10694
            match (v1, v2) {
86
2
                (Some((_root_v1, leaves_v1)), Some((_root_v2, leaves_v2))) => {
87
2
                    let combined_leaves =
88
2
                        Self::get_combined_leaves(Some(leaves_v1), Some(leaves_v2));
89
2
                    let root = merkle_root::<<T as Config>::Hashing, _>(
90
2
                        combined_leaves.unwrap_or_default().into_iter(),
91
                    );
92
2
                    Pallet::<T>::deposit_event(Event::<T>::CommitmentRootRead { commitment: root });
93
2
                    Some(root)
94
                }
95
1
                (Some((root, _leaves)), None) => {
96
                    // Only v1, return v1 root directly no need to recompute root
97
1
                    Pallet::<T>::deposit_event(Event::<T>::CommitmentRootRead { commitment: root });
98
1
                    Some(root)
99
                }
100
1
                (None, Some((root, _leaves))) => {
101
                    // Only v2, return v2 root directly no need to recompute root
102
1
                    Pallet::<T>::deposit_event(Event::<T>::CommitmentRootRead { commitment: root });
103
1
                    Some(root)
104
                }
105
10690
                (None, None) => None,
106
            }
107
10694
        }
108

            
109
8
        pub fn record_commitment_root(commitment: H256, message_leaves: Vec<H256>) {
110
8
            RecordedCommitment::<T>::put((commitment, message_leaves));
111
8
            Pallet::<T>::deposit_event(Event::<T>::NewCommitmentRootRecorded { commitment });
112
8
        }
113

            
114
6
        pub fn record_commitment_root_v2(commitment: H256, message_leaves: Vec<H256>) {
115
6
            RecordedCommitmentV2::<T>::put((commitment, message_leaves));
116
6
            Pallet::<T>::deposit_event(Event::<T>::NewCommitmentRootRecorded { commitment });
117
6
        }
118

            
119
4
        pub fn prove_message_v1(leaf_index: u64) -> Option<MerkleProof> {
120
4
            let v1_exists = RecordedCommitment::<T>::exists();
121
4
            if !v1_exists {
122
1
                None
123
            } else {
124
3
                Self::prove_message(leaf_index)
125
            }
126
4
        }
127

            
128
4
        pub fn prove_message_v2(leaf_index: u64) -> Option<MerkleProof> {
129
4
            let v2_exists = RecordedCommitmentV2::<T>::exists();
130
4
            if !v2_exists {
131
1
                return None;
132
3
            }
133

            
134
3
            let v1 = RecordedCommitment::<T>::get();
135
3
            if let Some((_, leaves)) = v1 {
136
2
                let maybe_combined_index = (leaves.len() as u64).checked_add(leaf_index);
137
2
                if let Some(combined_index) = maybe_combined_index {
138
2
                    Self::prove_message(combined_index)
139
                } else {
140
                    // Overflow
141
                    None
142
                }
143
            } else {
144
1
                Self::prove_message(leaf_index)
145
            }
146
4
        }
147

            
148
6
        fn prove_message(combined_leaf_index: u64) -> Option<MerkleProof> {
149
6
            let v1 = RecordedCommitment::<T>::get();
150
6
            let v2 = RecordedCommitmentV2::<T>::get();
151
6
            match (v1, v2) {
152
                (None, None) => None,
153
6
                (v1_data, v2_data) => {
154
6
                    let combined_leaves = Self::get_combined_leaves(
155
6
                        v1_data.map(|(_root, leaves)| leaves),
156
6
                        v2_data.map(|(_root, leaves)| leaves),
157
                    );
158

            
159
6
                    if let Some(combined_leaves) = combined_leaves {
160
6
                        let proof = merkle_proof::<<T as Config>::Hashing, _>(
161
6
                            combined_leaves.into_iter(),
162
6
                            combined_leaf_index,
163
                        );
164
6
                        Some(proof)
165
                    } else {
166
                        None
167
                    }
168
                }
169
            }
170
6
        }
171
    }
172
}