简体   繁体   English

如何使用 Substrate API Client 写入 Substrate 的存储?

[英]How to write in the storage of Substrate using Substrate API Client?

My goal is to write a value in the storage map of Substrate using substrate-api-client .我的目标是使用substrate-api-client在 Substrate 的存储 map 中写入一个值。 My storage map, defined in the Substrate chain, looks like this:我在 Substrate 链中定义的存储 map 如下所示:

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(())
        }
    }
}

The above storage map is located at:上述存储map位于:

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

The expected result is to successfully write a value on the Substrate's storage.预期的结果是成功地在 Substrate 的存储上写入一个值。 As I do in the frontend, successfully:正如我在前端所做的那样,成功: Substrate 中添加的自定义存储模块的前端

However, while using the substrate-api-client to do the same task, I am getting the following error:但是,在使用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")})

I have tried to write a custom extrinsic example in the substrate-api-client .我试图在substrate-api-client中编写一个自定义的外部示例。 This is how I am composing the extrinsic:这就是我编写外在的方式:

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

This is the minimal code required to reproduce the error:这是重现错误所需的最少代码:

/*
    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
}

The above code is located in the file: example_writing_file_hash.rs and the tree is:上面的代码位于文件中: example_writing_file_hash.rs ,树是:

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

whereas the complete codebase is available here .而完整的代码库可在此处获得。

Update 1更新 1

As per user13207835 's answer , I tried declaring my content as a hash but failed.根据user13207835回答,我尝试将我的内容声明为 hash 但失败了。 PS I am a beginner in Rust, Substrate. PS我是Rust,Substrate的初学者。

let file_hash: Hash = "0x0000000000000000000000000000000000000000000000000000000000000002";

Got the error:得到错误:

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

I understand this error, though I am unaware of how to declare the above value as Hash as suggested in the answer.我理解这个错误,但我不知道如何按照答案中的建议将上述值声明为Hash

You declare your function as fn set_value(origin, value: T::Hash) .您将 function 声明为fn set_value(origin, value: T::Hash) Thus, you must pass a Hash to the compose_extrinsic!因此,您必须将Hash传递给compose_extrinsic! macro as it does simply encode the arguments as they are passed.宏,因为它只是在传递 arguments 时对其进行编码。 It does not know that "0x...2" is a hash.它不知道"0x...2"是hash。 Hence, it will encode it as a string.因此,它会将其编码为字符串。

Therefore, you should be passing a something, whose raw data is encoded the same as the Hash representation in your node.因此,您应该传递一个东西,其原始数据的编码与您节点中的Hash表示相同。 There are two options:有两种选择:

  • Just use an [u8, 32] array.只需使用 [u8, 32] 数组。
  • Use the Hash from one of the primitives crate from substrate使用来自基板的原语板条箱之一的 Hash

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM