Проблемы с использованием u8 в субстратах и ​​чернилах

Я пытаюсь добавить простой u8 в свой модуль времени выполнения субстрата:

decl_storage! {
    trait Store for Module<T: Trait> as TemplateModule {
        MyByte: u8;
    }
}

Однако я получаю сообщение об ошибке компилятора, что он не реализует Encode или Decode Parity Codec:

error[E0277]: the trait bound `u8: _IMPL_DECODE_FOR_Event::_parity_codec::Encode` is not satisfied
  --> /Users/shawntabrizi/Documents/GitHub/substrate-package/substrate-node-template/runtime/src/template.rs:23:1
   |
23 | / decl_storage! {
24 | |     trait Store for Module<T: Trait> as TemplateModule {
25 | |         MyByte: u8;
26 | |     }
27 | | }
   | |_^ the trait `_IMPL_DECODE_FOR_Event::_parity_codec::Encode` is not implemented for `u8`

Аналогичная проблема возникает, когда я пытаюсь сохранить u8 в смарт-контракте субстрата, используя чернила !:

contract! {
    struct MyContract {
        value: storage::Value<u8>,
    }
    ...
}

Ошибка:

error[E0277]: the trait bound `u8: parity_codec::codec::Encode` is not satisfied
  --> src/lib.rs:26:1
   |
26 | / contract! {
27 | |     struct MyContract {
28 | |         value: storage::Value<u8>,
29 | |     }
...  |
49 | |     }
50 | | }
   | |_^ the trait `parity_codec::codec::Encode` is not implemented for `u8`

Почему это так и что я могу сделать, чтобы решить эту проблему?


person Shawn Tabrizi    schedule 09.05.2019    source источник


Ответы (1)


Сегодня parity_codec не поддерживает кодирование u8 во избежание конфликта типов, поскольку Vec<u8> является частным случаем Vec<T>.

См .: https://github.com/paritytech/parity-codec/issues/47 < / а>

гавофёрк:

Потому что в противном случае две кодировки: Vec<u8> и Vec<T: Codec> конфликтуют.

Возможно, в будущем это можно будет исправить с помощью дополнительных функций Rust, но сейчас вам нужно будет хранить свои отдельные байты как [u8; 1] и работать с этим типом.


Модуль среды выполнения субстрата

Одно хакерское решение для модуля среды выполнения субстрата выглядит примерно так:

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

pub trait Trait: system::Trait {
    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
}

type U8 = [u8; 1];

decl_storage! {
    trait Store for Module<T: Trait> as TemplateModule {
        MyByte get(my_byte): U8;
    }
}

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

        fn deposit_event<T>() = default;

        pub fn set_my_byte(origin, input: U8) -> Result {
            let who = ensure_signed(origin)?;

            <MyByte<T>>::put(input);

            Self::deposit_event(RawEvent::MyByteStored(input, who));
            Ok(())
        }

        pub fn add_to_byte(origin, input: U8) -> Result {
            let who = ensure_signed(origin)?;

            let my_byte = Self::my_byte()[0];
            let my_new_byte = my_byte.checked_add(input[0]).ok_or("Overflow")?;

            <MyByte<T>>::put([my_new_byte]);
            Self::deposit_event(RawEvent::MyByteStored([my_new_byte], who));
            Ok(())
        }
    }
}

decl_event!(
    pub enum Event<T> where AccountId = <T as system::Trait>::AccountId {
        MyByteStored(U8, AccountId),
    }
);

Где мы присваиваем новый тип type U8 = [u8; 1];. Выбор нашего нового имени типа важен, поскольку он обманывает пользовательский интерфейс Polkadot, чтобы обрабатывать это значение просто как u8 для любых полей ввода / вывода, которые оно генерирует. Если вы попытаетесь использовать настраиваемый тип, например type Byte = [u8; 1], пользовательский интерфейс попросит вас импортировать определение этого настраиваемого типа. Если вы попытаетесь использовать [u8; 1] напрямую, пользовательский интерфейс Polkadot не будет знать, как отображать ввод / вывод этого значения.

Кроме того, на момент написания этого сообщения макрос decl_event! имеет проблему с внесением [u8; 1] непосредственно из-за сопоставления с образцом.

Обратите внимание, что вам нужно будет обращаться с этим типом как с массивом, когда вы его используете. add_to_byte() показывает пример этого. Итак, в конечном итоге вам нужно извлечь первый элемент массива для извлечения байта, и вам нужно обернуть свой байт в массив, чтобы установить U8:

let my_byte = Self::my_byte()[0];
...
<MyByte<T>>::put([my_new_byte]);

Другие решения могут включать использование других типов, которые изначально поддерживаются, например Vec<u8> или u16, и выполнение соответствующих проверок в вашей среде выполнения, чтобы они рассматривались как один u8, но пользовательский интерфейс не будет знать лучше.


Смарт-контракты субстрата

Я пока не нашел отличного решения для ink!, но вы сможете использовать [u8; 1] непосредственно во всем своем коде. Опять же, вам нужно будет рассматривать его как массив для геттеров и сеттеров. Но при создании ABI вам нужно будет вручную изменить экземпляры [u8; 1] на u8, чтобы заставить пользовательский интерфейс делать то, что вы хотите.

person Shawn Tabrizi    schedule 09.05.2019