|
| 1 | +// This file is part of Webb. |
| 2 | + |
| 3 | +// Copyright (C) 2021 Webb Technologies Inc. |
| 4 | +// SPDX-License-Identifier: Apache-2.0 |
| 5 | + |
| 6 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 7 | +// you may not use this file except in compliance with the License. |
| 8 | +// You may obtain a copy of the License at |
| 9 | +// |
| 10 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | +// |
| 12 | +// Unless required by applicable law or agreed to in writing, software |
| 13 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 14 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | +// See the License for the specific language governing permissions and |
| 16 | +// limitations under the License. |
| 17 | + |
| 18 | +//! # Anchor Handler Module |
| 19 | +//! |
| 20 | +//! A module for executing the creation and modification of anchors. |
| 21 | +//! |
| 22 | +//! ## Overview |
| 23 | +//! |
| 24 | +//! The anchor-handler module provides functionality for the following: |
| 25 | +//! |
| 26 | +//! * The creation of anchors from proposals |
| 27 | +//! * Updating existing anchors from proposals |
| 28 | +//! |
| 29 | +//! ## Interface |
| 30 | +//! |
| 31 | +//! ### Permissioned Functions |
| 32 | +//! |
| 33 | +//! * `execute_vanchor_create_proposal`: Creates a vanchor from successfully voted on proposal. This |
| 34 | +//! method requires the `origin` to be [T::BridgeOrigin]. |
| 35 | +//! * `execute_vanchor_update_proposal`: Adds/Updates a vanchor from successfully voted on proposal. |
| 36 | +//! This method requires the `origin` to be [T::BridgeOrigin]. |
| 37 | +//! |
| 38 | +//! ## Related Modules |
| 39 | +//! |
| 40 | +//! * VAnchor pallet |
| 41 | +//! * Linkable-tree pallet |
| 42 | +
|
| 43 | +// Ensure we're `no_std` when compiling for Wasm. |
| 44 | +#![cfg_attr(not(feature = "std"), no_std)] |
| 45 | + |
| 46 | +#[cfg(test)] |
| 47 | +pub mod mock_bridge; |
| 48 | +#[cfg(test)] |
| 49 | +mod tests_bridge; |
| 50 | + |
| 51 | +#[cfg(test)] |
| 52 | +pub mod mock_signature_bridge; |
| 53 | +#[cfg(test)] |
| 54 | +mod tests_signature_bridge; |
| 55 | + |
| 56 | +use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, traits::EnsureOrigin}; |
| 57 | +use frame_system::pallet_prelude::OriginFor; |
| 58 | +use pallet_linkable_tree::types::EdgeMetadata; |
| 59 | +use pallet_vanchor::{BalanceOf as VAnchorBalanceOf, CurrencyIdOf as VAnchorCurrencyIdOf}; |
| 60 | +use webb_primitives::{ |
| 61 | + traits::vanchor::{VAnchorConfig, VAnchorInspector, VAnchorInterface}, |
| 62 | + ResourceId, |
| 63 | +}; |
| 64 | +pub mod types; |
| 65 | +use types::*; |
| 66 | + |
| 67 | +pub use pallet::*; |
| 68 | + |
| 69 | +#[frame_support::pallet] |
| 70 | +pub mod pallet { |
| 71 | + use super::*; |
| 72 | + use frame_support::{dispatch::DispatchResultWithPostInfo, pallet_prelude::*}; |
| 73 | + use frame_system::pallet_prelude::*; |
| 74 | + use pallet_linkable_tree::types::EdgeMetadata; |
| 75 | + use pallet_vanchor::VAnchorConfigration; |
| 76 | + |
| 77 | + #[pallet::pallet] |
| 78 | + #[pallet::generate_store(pub(super) trait Store)] |
| 79 | + #[pallet::without_storage_info] |
| 80 | + pub struct Pallet<T, I = ()>(_); |
| 81 | + |
| 82 | + #[pallet::config] |
| 83 | + /// The module configuration trait. |
| 84 | + pub trait Config<I: 'static = ()>: frame_system::Config + pallet_vanchor::Config<I> { |
| 85 | + /// The overarching event type. |
| 86 | + type Event: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::Event>; |
| 87 | + |
| 88 | + type BridgeOrigin: EnsureOrigin<Self::Origin, Success = Self::AccountId>; |
| 89 | + |
| 90 | + /// VAnchor Interface |
| 91 | + type VAnchor: VAnchorInterface<VAnchorConfigration<Self, I>> |
| 92 | + + VAnchorInspector<VAnchorConfigration<Self, I>>; |
| 93 | + } |
| 94 | + |
| 95 | + /// The map of trees to their anchor metadata |
| 96 | + #[pallet::storage] |
| 97 | + #[pallet::getter(fn anchor_list)] |
| 98 | + pub type AnchorList<T: Config<I>, I: 'static = ()> = |
| 99 | + StorageMap<_, Blake2_128Concat, ResourceId, T::TreeId, ValueQuery>; |
| 100 | + |
| 101 | + #[pallet::storage] |
| 102 | + #[pallet::getter(fn update_records)] |
| 103 | + /// sourceChainID => nonce => Update Record |
| 104 | + pub type UpdateRecords<T: Config<I>, I: 'static = ()> = StorageDoubleMap< |
| 105 | + _, |
| 106 | + Blake2_128Concat, |
| 107 | + T::ChainId, |
| 108 | + Blake2_128Concat, |
| 109 | + u64, |
| 110 | + UpdateRecord<T::TreeId, ResourceId, T::ChainId, T::Element, T::LeafIndex>, |
| 111 | + ValueQuery, |
| 112 | + >; |
| 113 | + |
| 114 | + #[pallet::storage] |
| 115 | + #[pallet::getter(fn counts)] |
| 116 | + /// The number of updates |
| 117 | + pub(super) type Counts<T: Config<I>, I: 'static = ()> = |
| 118 | + StorageMap<_, Blake2_128Concat, T::ChainId, u64, ValueQuery>; |
| 119 | + |
| 120 | + #[pallet::event] |
| 121 | + #[pallet::generate_deposit(pub(super) fn deposit_event)] |
| 122 | + pub enum Event<T: Config<I>, I: 'static = ()> { |
| 123 | + AnchorCreated, |
| 124 | + AnchorEdgeAdded, |
| 125 | + AnchorEdgeUpdated, |
| 126 | + } |
| 127 | + |
| 128 | + #[pallet::error] |
| 129 | + pub enum Error<T, I = ()> { |
| 130 | + /// Access violation. |
| 131 | + InvalidPermissions, |
| 132 | + // Anchor handler already exists for specified resource Id. |
| 133 | + ResourceIsAlreadyAnchored, |
| 134 | + // Anchor handler doesn't exist for specified resoure Id. |
| 135 | + AnchorHandlerNotFound, |
| 136 | + // Source chain Id is not registered. |
| 137 | + SourceChainIdNotFound, |
| 138 | + /// Storage overflowed. |
| 139 | + StorageOverflow, |
| 140 | + } |
| 141 | + |
| 142 | + #[pallet::hooks] |
| 143 | + impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {} |
| 144 | + |
| 145 | + #[pallet::call] |
| 146 | + impl<T: Config<I>, I: 'static> Pallet<T, I> { |
| 147 | + /// This will be called by bridge when proposal to create a |
| 148 | + /// vanchor has been successfully voted on. |
| 149 | + #[pallet::weight(195_000_000)] |
| 150 | + pub fn execute_vanchor_create_proposal( |
| 151 | + origin: OriginFor<T>, |
| 152 | + src_chain_id: T::ChainId, |
| 153 | + r_id: ResourceId, |
| 154 | + max_edges: u32, |
| 155 | + tree_depth: u8, |
| 156 | + asset: VAnchorCurrencyIdOf<T, I>, |
| 157 | + ) -> DispatchResultWithPostInfo { |
| 158 | + T::BridgeOrigin::ensure_origin(origin)?; |
| 159 | + Self::create_vanchor(src_chain_id, r_id, max_edges, tree_depth, asset) |
| 160 | + } |
| 161 | + |
| 162 | + /// This will be called by bridge when proposal to add/update edge of a |
| 163 | + /// vanchor has been successfully voted on. |
| 164 | + #[pallet::weight(195_000_000)] |
| 165 | + pub fn execute_vanchor_update_proposal( |
| 166 | + origin: OriginFor<T>, |
| 167 | + r_id: ResourceId, |
| 168 | + vanchor_metadata: EdgeMetadata<T::ChainId, T::Element, T::LeafIndex>, |
| 169 | + ) -> DispatchResultWithPostInfo { |
| 170 | + T::BridgeOrigin::ensure_origin(origin)?; |
| 171 | + Self::update_vanchor(r_id, vanchor_metadata) |
| 172 | + } |
| 173 | + |
| 174 | + // TODO: Add configurable limit proposal executors for VAnchors |
| 175 | + } |
| 176 | +} |
| 177 | + |
| 178 | +impl<T: Config<I>, I: 'static> VAnchorConfig for Pallet<T, I> { |
| 179 | + type AccountId = T::AccountId; |
| 180 | + type Balance = VAnchorBalanceOf<T, I>; |
| 181 | + type Amount = i128; |
| 182 | + type ChainId = T::ChainId; |
| 183 | + type CurrencyId = VAnchorCurrencyIdOf<T, I>; |
| 184 | + type Element = T::Element; |
| 185 | + type LeafIndex = T::LeafIndex; |
| 186 | + type TreeId = T::TreeId; |
| 187 | +} |
| 188 | + |
| 189 | +impl<T: Config<I>, I: 'static> Pallet<T, I> { |
| 190 | + fn create_vanchor( |
| 191 | + src_chain_id: T::ChainId, |
| 192 | + r_id: ResourceId, |
| 193 | + max_edges: u32, |
| 194 | + tree_depth: u8, |
| 195 | + asset: VAnchorCurrencyIdOf<T, I>, |
| 196 | + ) -> DispatchResultWithPostInfo { |
| 197 | + ensure!(!AnchorList::<T, I>::contains_key(r_id), Error::<T, I>::ResourceIsAlreadyAnchored); |
| 198 | + let tree_id = T::VAnchor::create(None, tree_depth, max_edges, asset)?; |
| 199 | + AnchorList::<T, I>::insert(r_id, tree_id); |
| 200 | + Counts::<T, I>::insert(src_chain_id, 0); |
| 201 | + Self::deposit_event(Event::AnchorCreated); |
| 202 | + Ok(().into()) |
| 203 | + } |
| 204 | + |
| 205 | + fn update_vanchor( |
| 206 | + r_id: ResourceId, |
| 207 | + anchor_metadata: EdgeMetadata<T::ChainId, T::Element, T::LeafIndex>, |
| 208 | + ) -> DispatchResultWithPostInfo { |
| 209 | + let tree_id = |
| 210 | + AnchorList::<T, I>::try_get(r_id).map_err(|_| Error::<T, I>::AnchorHandlerNotFound)?; |
| 211 | + let (src_chain_id, merkle_root, latest_leaf_index, target) = ( |
| 212 | + anchor_metadata.src_chain_id, |
| 213 | + anchor_metadata.root, |
| 214 | + anchor_metadata.latest_leaf_index, |
| 215 | + anchor_metadata.target, |
| 216 | + ); |
| 217 | + |
| 218 | + if T::VAnchor::has_edge(tree_id, src_chain_id) { |
| 219 | + T::VAnchor::update_edge(tree_id, src_chain_id, merkle_root, latest_leaf_index, target)?; |
| 220 | + Self::deposit_event(Event::AnchorEdgeUpdated); |
| 221 | + } else { |
| 222 | + T::VAnchor::add_edge(tree_id, src_chain_id, merkle_root, latest_leaf_index, target)?; |
| 223 | + Self::deposit_event(Event::AnchorEdgeAdded); |
| 224 | + } |
| 225 | + let nonce = Counts::<T, I>::try_get(src_chain_id) |
| 226 | + .map_err(|_| Error::<T, I>::SourceChainIdNotFound)?; |
| 227 | + let record = UpdateRecord { tree_id, resource_id: r_id, edge_metadata: anchor_metadata }; |
| 228 | + UpdateRecords::<T, I>::insert(src_chain_id, nonce, record); |
| 229 | + Counts::<T, I>::mutate(src_chain_id, |val| -> DispatchResultWithPostInfo { |
| 230 | + *val = val.checked_add(1).ok_or(Error::<T, I>::StorageOverflow)?; |
| 231 | + Ok(().into()) |
| 232 | + }) |
| 233 | + } |
| 234 | +} |
0 commit comments