1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
#![cfg_attr(not(feature = "std"), no_std)]

//! A pallet that defines a system to register and update names
//! in a Substrate chain.  This provides (roughly) the functionality
//! of the [Namecoin](https://www.namecoin.org/) blockchain.
//!
//! The core concept is that of a *name*.  This is some identifier (the exact
//! type can be configured through the module's [`Trait`](Trait)),
//! e.g. a human-readable name as string.  Each name is unique, and has an
//! associated value and owner.  Everyone can read the database of names, but
//! only the owner can make changes to it.  This typically means changing the
//! value to publish some data with the name, but the owner can also transfer
//! names to a different owner.
//!
//! Names are given out on a *first come, first serve* basis.  Each name that
//! is not yet registered (and valid for the system) can be registered by
//! any account (which may incur a fee for registration, and then maybe also
//! for updates to the name).  Once registered, the name is owned by the
//! account that first registered it.
//!
//! After a certain number of blocks, names may expire and become usable again.
//! By updating a name before the expiration, the current owner can keep
//! ownership.
//!
//! The `names` module defines basic extrinsics to perform name operations
//! ([register](Module::update) / [update](Module::update) /
//! [transfer](Module::transfer) names) and [events](RawEvent) corresponding to
//! changes in the name database.  But if custom logic needs to be applied in
//! addition by the runtime, it may use the exposed functions
//! [`check_assuming_signed`](Module::check_assuming_signed) and
//! [`execute`](Module::execute) directly.  The name database can be accessed
//! from external code by using [`lookup`](Module::lookup).

use frame_support::{
    decl_module, decl_storage, decl_event, ensure,
    dispatch::DispatchResult, dispatch::fmt::Debug,
    traits::{Currency, ExistenceRequirement, WithdrawReason, WithdrawReasons},
};
use codec::{Decode, Encode, FullCodec};
use system::ensure_signed;
use sp_runtime::traits::CheckedSub;
use core::cmp::max;

/// The pallet's configuration trait.
pub trait Trait: system::Trait {

    /// Type for names.
    type Name: Clone + Debug + Default + Eq + FullCodec;
    /// Type for values associated to names.
    type Value: Clone + Debug + Default + Eq + FullCodec;

    /// Type for currency operations (in order to pay for names).
    type Currency: Currency<Self::AccountId>;

    /// The overarching event type.
    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;

    /// Computes and returns the currency fee the sender has to pay for
    /// a certain operation.  If `None` is returned, it means that the
    /// operation is invalid (e.g. the name is too short).
    fn get_name_fee(op: &Operation<Self>)
        -> Option<<Self::Currency as Currency<Self::AccountId>>::Balance>;

    /// For a given name operation, computes the number of blocks before the
    /// name will expire again.  If `None` is returned, then the name will
    /// never expire.
    fn get_expiration(op: &Operation<Self>) -> Option<Self::BlockNumber>;

    /// "Takes ownership" of the fee paid for a name operation.  This
    /// function can just do nothing to effectively burn the fee, it may
    /// deposit it to a developer account, or it may give it out to miners.
    fn deposit_fee(value: <Self::Currency as Currency<Self::AccountId>>::NegativeImbalance);

}

/// All data stored with a name in the database.
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Clone, Decode, Encode, Eq, PartialEq)]
pub struct NameData<T: Trait> {
    /// The name's associated value.
    pub value: T::Value,
    /// The name's current owner.
    pub owner: T::AccountId,
    /// The block number when the name expires or `None` if it does not expire.
    pub expiration: Option<T::BlockNumber>,
}

/// Type of a name operation.
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Eq, PartialEq)]
pub enum OperationType {
    /// This operation registers a name that does not exist yet.
    Registration,
    /// This operation updates an existing name.
    Update,
}

/// All data necessary to actually perform a name operation.
///
/// This is returned by the
/// [validation function](Module::check_assuming_signed), and can then
/// be passed to the [execution function](Module::execute) if a runtime wants
/// to do its own logic in addition.
///
/// A reference to an `Operation` struct is also passed to the [`Trait`](Trait)
/// functions that need to determine e.g. the name fee for the operation.
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Eq, PartialEq)]
pub struct Operation<T: Trait> {
    /// Type of this operation.
    pub operation: OperationType,
    /// The name being operated on.
    pub name: T::Name,
    /// The value that is being associated to the name.
    pub value: T::Value,

    /// The sender of the name (who pays the name fee).
    sender: T::AccountId,
    /// The owner it is sent to.
    recipient: T::AccountId,

    /// The name fee to pay.
    fee: <T::Currency as Currency<T::AccountId>>::Balance,
}

decl_storage! {
    trait Store for Module<T: Trait> as TemplateModule {
        /// The main mapping from names to [associated data](NameData).
        Names get(lookup): map T::Name => Option<NameData<T>>;
        /// All names (as both the second key and the value) that may expire at
        /// the given block height (first key).  We use this so we can
        /// efficiently process expirations whenever we process a new block.
        /// When names are updated, they are not removed from here, though --
        /// so a name's expiration value in the core database overrules this
        /// index.
        Expirations: double_map T::BlockNumber, blake2_256(T::Name) => T::Name;
    }
}

decl_module! {
    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
        fn deposit_event() = default;

        /// Tries to update a name with a given value.
        ///
        /// If the name does not exist yet, it will be created.  If the name
        /// exists, then only the current owner can update it.
        pub fn update(origin, name: T::Name, value: T::Value) -> DispatchResult {
            let who = ensure_signed(origin)?;
            let data = Self::check_assuming_signed(who, name, Some(value), None)?;
            Self::execute(data)?;
            Ok(())
        }

        /// Tries to transfer a name to a given recipient.
        ///
        /// If the name does not exist, it will be registered directly to them
        /// with a [default value](std::default::Default).
        pub fn transfer(origin, name: T::Name, recipient: T::AccountId) -> DispatchResult {
            let who = ensure_signed(origin)?;
            let data = Self::check_assuming_signed(who, name, None, Some(recipient))?;
            Self::execute(data)?;
            Ok(())
        }

        /// Processes all names logic required before executing extrinsics
        /// of a given block.  In concrete terms, this function makes sure that
        /// names expired in the current block will be removed from the
        /// database.
        fn on_initialize(h: T::BlockNumber) {
            Self::expire_names(h);
        }

    }
}

impl<T: Trait> Module<T> {

    /// Returns a withdraw reasons value for the fee payment.
    fn withdraw_reasons() -> WithdrawReasons {
        let mut res = WithdrawReasons::none();
        res.set(WithdrawReason::Fee);
        res
    }

    /// Checks if a name operation is valid, assuming that we already know
    /// it was signed by the given account.
    ///
    /// Value and recipient are optional.  If the value is missing, we use the
    /// existing value or the [default value](std::default::Default) if the
    /// name does not exist yet.  If the recipient is missing, we set it to
    /// the `sender` account.
    ///
    /// This function returns either an error if the operation is not valid,
    /// or the [data](Operation) that should be passed to
    /// [`execute`](Module::execute) later on if the transaction is valid.
    pub fn check_assuming_signed(sender: T::AccountId, name: T::Name,
                                 value: Option<T::Value>,
                                 recipient: Option<T::AccountId>) -> Result<Operation<T>, &'static str> {
        let (typ, old_value) = match <Names<T>>::get(&name) {
            None => (OperationType::Registration, T::Value::default()),
            Some(data) => {
                ensure!(sender == data.owner, "non-owner name update");
                (OperationType::Update, data.value)
            },
        };

        let value = match value {
            None => old_value,
            Some(new_value) => new_value,
        };
        let recipient = match recipient {
            None => sender.clone(),
            Some(new_recipient) => new_recipient,
        };

        let mut op = Operation::<T> {
            operation: typ,
            name: name,
            value: value,
            sender: sender,
            recipient: recipient,
            fee: <T::Currency as Currency<T::AccountId>>::Balance::default(),
        };
        op.fee = match T::get_name_fee(&op) {
            None => return Err("operation violates name policy"),
            Some(f) => f,
        };

        /* Make sure that we can withdraw the name fee from the sender account.
           Note that ensure_can_withdraw does not by itself verify the
           amount against the free balance, but just that the new balance
           satisfies all locks in place.  Thus we have to do that ourselves.  */
        let new_balance = match T::Currency::free_balance(&op.sender).checked_sub(&op.fee) {
            None => return Err("insufficient balance for name fee"),
            Some(b) => b,
        };
        match T::Currency::ensure_can_withdraw(&op.sender, op.fee, Self::withdraw_reasons(), new_balance) {
            Err(_) => return Err("cannot withdraw name fee from sender"),
            Ok(_) => (),
        }

        Ok(op)
    }

    /// Executes the state change (and fires events) for a given
    /// [name operation](Operation).
    ///
    /// This should be called after
    /// [`check_assuming_signed`](Module::check_assuming_signed) (passing its
    /// result), and when potential other checks have been done as well.
    ///
    /// This function may actually fail (return an error) if the fee withdrawal
    /// is not possible.  This can happen if some funds were spent externally
    /// between the call to
    /// [`check_assuming_signed`](Module::check_assuming_signed) and this
    /// function.  If that happens, then `execute` will be a noop.
    pub fn execute(op: Operation<T>) -> DispatchResult {
        /* As the very first step, handle the name fee.  This makes sure
           that if withdrawal fails, it will not cause any other changes.  */
        let imbalance = T::Currency::withdraw(&op.sender, op.fee,
                                              Self::withdraw_reasons(),
                                              ExistenceRequirement::AllowDeath)?;
        T::deposit_fee(imbalance);

        let expiration_blocks = T::get_expiration(&op);
        let expiration_height = match expiration_blocks {
            None => None,
            Some(b) => {
                /* In the strange case that we are told to use zero blocks for
                   expiration, make it at least one.  This ensures that we will
                   actually expire the name in the next block, and not end up
                   with an index entry from the past that will stick around
                   forever.  */
                let b = max(b, T::BlockNumber::from(1));
                Some(system::Module::<T>::block_number() + b)
            },
        };

        let data = NameData::<T> {
            value: op.value,
            owner: op.recipient,
            expiration: expiration_height,
        };

        <Names<T>>::insert(&op.name, &data);
        if let Some(h) = expiration_height {
            <Expirations<T>>::insert(h, &op.name, &op.name);
        }

        match op.operation {
            OperationType::Registration => {
                Self::deposit_event(RawEvent::NameRegistered(op.name.clone()));
            },
            OperationType::Update => (),
        }
        Self::deposit_event(RawEvent::NameUpdated(op.name, data));

        Ok(())
    }

    /// Processes all name expirations for the given block number.
    fn expire_names(h: T::BlockNumber) {
        for nm in <Expirations<T>>::iter_prefix(h) {
            if let Some(data) = <Names<T>>::get(&nm) {
                match data.expiration {
                    None => (),
                    Some(expiration_height) => {
                        /* Whenever we store an expiration height in a name,
                           it is guaranteed to be larger than the current
                           block height.  And when the block height increases,
                           we first of all remove all names that expire at
                           that height.  This means that the name's expiration
                           height will always be not less than h.  */
                        assert!(expiration_height >= h);
                        if expiration_height <= h {
                            <Names<T>>::remove(&nm);
                            Self::deposit_event(RawEvent::NameExpired(nm));
                        }
                    },
                }
            }
        }
        <Expirations<T>>::remove_prefix(h);
    }

}

decl_event!(
    pub enum Event<T> where Name = <T as Trait>::Name, NameData = NameData<T> {
        /// Event when a name is newly created.
        NameRegistered(Name),
        /// Event when a name is updated (or created).
        NameUpdated(Name, NameData),
        /// Event when a name expires and is removed from the database.
        NameExpired(Name),
    }
);

/// Module with unit tests.
#[cfg(test)]
mod tests;