Как писать в хранилище Substrate с помощью Substrate API Client?

Моя цель - записать значение в карту хранилища Substrate с помощью https://github.com/scs/substrate-api-client/. Моя карта хранилища, определенная в цепочке субстратов, выглядит так:

use frame_support::{decl_module, decl_storage, dispatch::result::Result, ensure, StorageMap};
use frame_system::ensure_signed;
use sp_runtime::DispatchError;

// pub trait Trait: balances::Trait {}
pub trait Trait: pallet_balances::Trait {}

decl_storage! {
    trait Store for Module<T: Trait> as KittyStorage {
        // Value: map T::Hash => Option<T::AccountId>;
        // TODO: check whether this is the appropriate datatype(hash).
        Value: map hasher(blake2_256) T::Hash => Option<T::AccountId>;
        // Balances: map hasher(blake2_256) (T::AssetId, T::AccountId) => T::Balance;
    }
}

decl_module! {
    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
        fn set_value(origin, value: T::Hash) -> Result<(), DispatchError> {
            let sender = ensure_signed(origin)?;
            ensure!(!<Value<T>>::contains_key(value), "key already exists");
            <Value<T>>::insert(value, sender);
            Ok(())
        }
    }
}

Приведенная выше карта хранения находится по адресу:

substrate/bin/node/runtime/src/substratekitties.rs

Ожидаемый результат - успешная запись значения в хранилище субстрата. Как и я, успешно:

Однако при использовании substrate-api-client чтобы выполнить ту же задачу, я получаю следующую ошибку:

[2020-04-03T05:14:12Z ERROR substrate_api_client::rpc::client] ERROR: Object({"code": Number(1010), "data": String("BadProof"), "message": String("Invalid Transaction")})

Я попытался написать собственный внешний пример в substrate-api-client. Вот как я составляю внешнее:

let xt = compose_extrinsic!(
        api.clone(),
        "Substratekitties",
        "set_value",
        "0x0000000000000000000000000000000000000000000000000000000000000002"
    );

Это минимальный код, необходимый для воспроизведения ошибки:

/*
    Copyright 2019 Supercomputing Systems AG
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

//! This examples shows how to use the compose_extrinsic macro to create an extrinsic for any (custom)
//! module, whereas the desired module and call are supplied as a string.

use clap::{load_yaml, App};
use keyring::AccountKeyring;
use sp_core::crypto::Pair;
// use substrate_api_client::extrinsic::xt_primitives::UncheckedExtrinsicV4;
use substrate_api_client::{
    compose_extrinsic, extrinsic::xt_primitives::UncheckedExtrinsicV4, Api,
};

// use crate::{compose_extrinsic, Api};

fn main() {
    env_logger::init();
    let url = get_node_url_from_cli();
    // initialize api and set the signer (sender) that is used to sign the extrinsics
    let from = AccountKeyring::Alice.pair();
    let mut api = Api::new(format!("ws://{}", url)).set_signer(from);
    // let signer = AccountKeyring::Alice.pair();
    // api.signer = Some(signer);
    // the names are given as strings
    let xt = compose_extrinsic!(
        api.clone(),
        "Substratekitties",
        "set_value",
        "0x0000000000000000000000000000000000000000000000000000000000000002"
    );
    println!("[+] Composed Extrinsic:\n {:?}\n", xt);
    // send and watch extrinsic until finalized
    let signer = AccountKeyring::Alice.pair();
    api.signer = Some(signer);
    let tx_hash = api.send_extrinsic(xt.hex_encode()).unwrap();
    println!("[+] Transaction got finalized. Hash: {:?}", tx_hash);
}

pub fn get_node_url_from_cli() -> String {
    let yml = load_yaml!("../../src/examples/cli.yml");
    let matches = App::from_yaml(yml).get_matches();

    let node_ip = matches.value_of("node-server").unwrap_or("127.0.0.1");
    let node_port = matches.value_of("node-port").unwrap_or("9944");
    let url = format!("{}:{}", node_ip, node_port);
    println!("Interacting with node on {}\n", url);
    url
}

Приведенный выше код находится в файле: example_writing_file_hash.rs а дерево это:

substrate-api-client/src/examples/example_writing_file_hash.rs

тогда как полная кодовая база доступна здесь.

Обновление 1

Согласно user13207835 "s ответ, я попытался объявить мое содержание как хэш, но не удалось. PS Я новичок в Rust, Substrate.

let file_hash: Hash = "0x0000000000000000000000000000000000000000000000000000000000000002";

Получил ошибку:

error[E0308]: mismatched types; expected struct `primitive_types::H256`, found `&str`

Я понимаю эту ошибку, но не знаю, как объявить указанное выше значение как Hash как предложено в ответе.

1 ответ

Решение

Вы объявляете свою функцию как fn set_value(origin, value: T::Hash). Таким образом, вы должны пройтиHash к compose_extrinsic!макрос, поскольку он просто кодирует аргументы по мере их передачи. Он не знает, что"0x...2"это хеш. Следовательно, он будет кодировать его как строку.

Следовательно, вы должны передавать что-то, необработанные данные которого закодированы так же, какHashпредставление в вашем узле. Есть два варианта:

  • Просто используйте массив [u8, 32].
  • Используйте Хеш из одного из примитивов, ящик из подложки
Другие вопросы по тегам