
本教程介绍了如何设计一个简单的合约,该合约创建一个存储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()
作者郑重申明:截至发文时,作者与文中提及项目皆不存在任何利益关系。
网友评论