Как писать в хранилище 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].
- Используйте Хеш из одного из примитивов, ящик из подложки