Как иметь неизменяемую карту "ключ-значение" в Substrate?

Я пишу key: value StorageMap в substrate. Я хочу сделать его неизменным, чтобы ключ записывался, если он не существует, но если он существует:

i) если значение совпадает с сохраненным, хорошо ii) сделать транзакцию недействительной.

Я написал следующий runtime код:

use support::{decl_module, decl_storage, dispatch::Result, StorageMap};
use system::ensure_signed;

pub trait Trait: balances::Trait {}

decl_storage! {
    trait Store for Module<T: Trait> as KittyStorage {
        Value: map u64 => T::AccountId;
    }
}

decl_module! {
    pub struct Module<T: Trait> for enum Call where origin: T::Origin {

        fn set_value(origin, value: u64) -> Result {
            let sender = ensure_signed(origin)?;

            <Value<T>>::insert(value, sender);

            Ok(())
        }
    }
}

официальное руководство говорит о мутации ключа следующим образом:

/// Mutate the value under a key.
fn mutate<R, F: FnOnce(&mut Self::Query) -> R, S: Storage>(key: &K, f: F, storage: &S) -> R;

Итак, как мне сделать мой key:value неизменным? Должен ли я написать свой StorageMap? Если да, то где мне разместить этот код?

Примечание: я новичок как в подложке, так и в ржавчине.


person Aviral Srivastava    schedule 22.02.2020    source источник


Ответы (1)


Я хочу сделать его неизменным, чтобы ключ записывался, если он не существует, но если он существует:

i) если значение совпадает с сохраненным, хорошо ii) аннулировать транзакцию.

Вы можете использовать exists / _ 2_ api для элемента хранилища, и вам следует возможно, будет еще более явным, если использовать Option.

Итак, взяв код, который вы написали, вы бы изменили его следующим образом:

use support::{decl_module, decl_storage, dispatch::Result, ensure, StorageMap};
use system::ensure_signed;

pub trait Trait: balances::Trait {}

decl_storage! {
    trait Store for Module<T: Trait> as KittyStorage {
        Value: map u64 => Option<T::AccountId>;
    }
}

decl_module! {
    pub struct Module<T: Trait> for enum Call where origin: T::Origin {

        fn set_value(origin, value: u64) -> Result {
            let sender = ensure_signed(origin)?;

            ensure!(!<Value<T>>::contains_key(value), "key already exists");
            <Value<T>>::insert(value, sender);

            Ok(())
        }
    }
}

Поскольку вы используете здесь Option, вы также можете прочитать значение и проверить, является ли оно Some(value) или None, а затем в результате получить ошибку или продолжить.

Что вы не можете сделать, так это по-настоящему сделать значение неизменяемым в хранилище, чтобы весь код знал, что значение не следует изменять. Вам нужно будет написать логику, чтобы заранее проверять наличие значения, чтобы вы не меняли его.

person Shawn Tabrizi    schedule 22.02.2020
comment
Обратите внимание, что в Substrate v1.0 API был exists, но недавно он был изменен на contains_key. Если вы следуете руководству v1.0, вам следует использовать exists. - person Shawn Tabrizi; 22.02.2020
comment
Привет, Шон, я не полностью понял следующую строку: What you cannot do is truly make the value immutable in storage such that all code would know not to change the value. You would need to write logic to check ahead of time the value is there so that you don't change it. В коде, который вы предоставили, вызов ensure! действительно проверяет, что значение не существует. Разве это не гарантирует, что никакое значение не будет перезаписано? - person Aviral Srivastava; 23.02.2020
comment
Проверка ensure действительно проверяет, есть ли уже значение в хранилище. Мы написали логику так, что если есть значение, внешнее завершается раньше, и никакие значения не обновляются. Однако, если вы написали больше функций, которые могли бы изменить это значение, вам также нужно будет проверить эти функции. - person Shawn Tabrizi; 23.02.2020
comment
Понятно! Спасибо :) - person Aviral Srivastava; 23.02.2020