如何用CasperLabs设计一个简单合约,以及CasperLa

2020-10-17  本文已影响0人  十万灯佛

本教程介绍了如何设计一个简单的合约,该合约创建一个存储CLType值的密钥。此示例将向您展示如何存储u64,字符串,帐户哈希或U512值。该代码位于:https://github.com/CasperLabs/casperlabs-kv-storage

本教程还将提供一些有关如何使用Casperlabs智能合约DSL以及合约标头如何工作的见解。

合约

让我们从了解合约本身的结构开始。在这里,我们使用casperlabs_contract宏创建合约并将其命名kvstorage_contract。这是合约软件包的存储名称。我们看到的下一个宏是casperlabs_constructor,因为键值协定实际上本质上是无状态的,所以不需要初始化。但是,由于casperlabs_constructor是必需元素,因此我们仅创建一个空函数。

#[casperlabs_contract]

mod kvstorage_contract {

#[casperlabs_constructor]

fn init() {}

#[casperlabs_method]

fn store_u64(name: String, value: u64) {

set_key(name.as_str(), value);

}

#[casperlabs_method]

fn get_u64(name: String) -> u64 {

key(name.as_str())

}

#[casperlabs_method]

fn get_string(name: String) -> String {

key(name.as_str())

}

#[casperlabs_method]

fn store_u512(name: String, value: U512) {

set_key(name.as_str(), value);

}

#[casperlabs_method]

fn store_string(name: String, value: String) {

set_key(name.as_str(), value);

}

#[casperlabs_method]

fn store_account_hash(name: String, value: AccountHash) {

set_key(name.as_str(), value);

}

fn key<T: FromBytes + CLTyped>(name: &str) -> T {

let key = runtime::get_key(name)

.unwrap_or_revert()

.try_into()

.unwrap_or_revert();

storage::read(key).unwrap_or_revert().unwrap_or_revert()

}

fn set_key<T: ToBytes + CLTyped>(name: &str, value: T) {

match runtime::get_key(name) {

Some(key) => {

let key_ref = key.try_into().unwrap_or_revert();

storage::write(key_ref, value);

}

None => {

let key = storage::new_uref(value).into();

runtime::put_key(name, key);

}

}

}

}

测试合约

CasperLabs Contracts SDK支持智能合约的本地测试。本教程将介绍如何测试u64键值功能。这也可以很容易地适用于其他类型。

为了测试合约,必须存储值,并且必须部署合约。这是这些步骤的一些示例代码:

impl KVstorageContract{

pub fn deploy() -> Self {

// build the test context with the account for the deploy

let mut context = TestContextBuilder::new()

.with_account(TEST_ACCOUNT, U512::from(128_000_000))

.build();

// specify the session code & build the deploy 

let session_code = Code::from("contract.wasm");

let session = SessionBuilder::new(session_code, runtime_args! {})

.with_address(TEST_ACCOUNT)

.with_authorization_keys(&[TEST_ACCOUNT])

.build();

context.run(session);

let kvstorage_hash = Self::contract_hash(&context, 

KV_STORAGE_HASH);

Self {

context,

kvstorage_hash,

}

}

// query the contract hash after the deploy is complete

pub fn contract_hash(context: &TestContext, name: &str) -> Hash {

context

.query(TEST_ACCOUNT, &[name])

.unwrap_or_else(|_| panic!("{} contract not found", name))

.into_t()

.unwrap_or_else(|_| panic!("{} is not a type Contract.", 

name))

}

// store the u_64 value in the global state

pub fn call_store_u64(&mut self, name: String, value: u64) {

let code = Code::Hash(self.kvstorage_hash, "store_u64".to_string

());

let args = runtime_args! {

"name" => name,

"value" => value,

};

let session = SessionBuilder::new(code, args)

.with_address(TEST_ACCOUNT)

.with_authorization_keys(&[TEST_ACCOUNT])

.build();

self.context.run(session);

}

}

写单元测试

有了这些功能,就可以开始为合约编写测试了。

mod tests {

#[test]

fn should_store_u64() {

const KEY_NAME: &str = "test_u64";

let mut kv_storage = KVstorageContract::deploy();

let name = String::from("test_u64");

let value: u64 = 1;

kv_storage.call_store_u64(name, value);

let check: u64 = kv_storage.query_contract(&KEY_NAME).unwrap();

assert_eq!(value, check);

}

// A test to check whether the value is updated

#[test]

fn should_update_u64() {

const KEY_NAME: &str = "testu64";

let mut kv_storage = KVstorageContract::deploy();

let original_value: u64 = 1;

let updated_value: u64 = 2;

kv_storage.call_store_u64(KEY_NAME.to_string(), original_value);

kv_storage.call_store_u64(KEY_NAME.to_string(), updated_value);

let value: u64 = kv_storage.query_contract(&KEY_NAME).unwrap();

assert_eq!(value, 2);

}

}

在本地运行

如果您已使用cargo-casperlabs设置了合约,则可以在本地运行单元测试。指南中提供了设置SDK的步骤。

cargo test -p tests

部署到Testnet并与合约进行交互

有一个独立的python cli应用程序,可用于kvstorage合约。使用测试网时,请在CLarity中创建一个帐户,然后使用水龙头为该帐户注资。下载私钥并使用该密钥对部署进行签名。也可以使用python客户端创建密钥。

**请注意,此客户是专为此合约设计的。**

部署合约

第一步实际上是将编译的wasm部署到网络,如果您使用的是python kv-client,则必须使用command deploy_kv_storage_contract。部署合约后,客户端将检索合约会话哈希以及部署合约的区块哈希。

python cli.py deploy_kv_storage_contract -f 

"29acb007dfa4f92fa5155cc2f3ae008b4ff234acf95b00c649e2eb77447f47ca" -p 

"../../kvkey.private.key" -c "../target/wasm32-unknown-

unknown/release/contract.wasm" -b True

调用入口点并设置一个值部署合约后,我们可以创建另一个部署,该部署将调用合约中的一个入口点。要调用入口点,您必须首先知道入口点的名称和会话哈希,这是我们从上一步中检索到的。

kv客户端有四个不同的命令来设置u64,String,U512和AccountHash的键值。

python cli.py insert_u64 -f 

"29acb007dfa4f92fa5155cc2f3ae008b4ff234acf95b00c649e2eb77447f47ca" -p 

"../../kvkey.private.key" -s 

"0e82027493b88db434e85f82f6bcf48a30e0c1db15cf55fb87b73461b8aef20b" -k 

"test" -v 1 -b True

查询链上合约

合约可以在不同的上下文中执行。在此示例中,部署合约时,它在aContract而不是a的上下文中运行Session。这意味着所有存储的密钥都不存储在帐户哈希下,而是存储在合约的上下文中。因此,当我们查询以检索键下的值时,我们实际上是在查询 AccountHash/kvstorage_contract/<key-name>而不仅仅是AccountHash/<key-name>。

读取值非常简单,在插入值之后,该命令将检索存储该值的块哈希。使用该块哈希值和该read-key命令,您可以轻松地检索和计算先前存储在命名键下的值。

python cli.py read_key -bh 

"cb08a634c9bbea695fbd92e2ddbeec6fe6a374db807b36fea35077a9c1d720df" -p 

"29acb007dfa4f92fa5155cc2f3ae008b4ff234acf95b00c649e2eb77447f47ca" -k 

"test"

有关kv-client的更多信息,可通过以下--help命令获得。有关客户端可用的每个命令的详细信息。

注意:通过使用简单的时间延迟从链中检索会话哈希,如果处理部署所花费的时间比预期的长,则kv-client可能会出错并不会检索会话哈希。在这种情况下,您可以使用python casperlabs_client检索会话哈希。

您必须首先找到包含您的部署的块的块哈希。拥有必要的块哈希后,即可使用python 

shell检索会话哈希

Import casperlabs_client

client = casperlabs_client.CasperLabsClient(‘deploy.casperlabs.io’, 

40401)

Session_code = client.queryState(<block-hash>, <account-hash>, 

“kvstorage_contract_hash”,’address’)

Session_hash = session_code.cl_value.value.bytes_value.hex()

作者郑重申明:截至发文时,作者与文中提及项目皆不存在任何利益关系。

上一篇 下一篇

猜你喜欢

热点阅读