Как записать в хранилище Substrate с помощью Substrate API Client? - PullRequest
0 голосов
/ 03 апреля 2020

Моя цель - записать значение в карту хранения Substrate, используя subst-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

Ожидаемый результат заключается в успешной записи значения в хранение субстрата. Как и во внешнем интерфейсе, успешно: Frontend of the custom storage module added in the Substrate

Однако при использовании 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")})

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

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 answer , я пытался объявить свой контент как ha sh, но не смог. PS Я новичок в Rust, Substrate.

let file_hash: Hash = "0x0000000000000000000000000000000000000000000000000000000000000002";

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

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

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

1 Ответ

1 голос
/ 03 апреля 2020

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

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

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