如何在本体网络上为WebAssembly编写智能合约? 第2部分:C ++

图片

在本文中,我们将看两个如何使用基于本体区块链网络的WASM用C ++编写智能合约的示例。 今天,在测试模式下稳定运行了几个月之后, Ontology在主网络上启动了WASM ,它可以轻松,以较低的成本将具有复杂业务逻辑的dApp合同转移到区块链上,从而显着丰富了dApp生态系统。

本体论Wasm还支持使用Rust语言创建智能合约,您可以在此处阅读有关内容。

以下是智能合约的两个示例:首先,写下“ Hello world!”。 然后创建一个虚拟货币信封,可以将其作为礼物发送给朋友。

使用C ++开发WASM合同



例子1. Hello World


让我们从Hello World开始:

#include<ontiolib/ontio.hpp> #include<stdio.h> using namespace ontio; class hello:public contract { public: using contract::contract: void sayHello(){ printf("hello world!"); } }; ONTIO_DISPATCH(hello, (sayHello)); 

合同创建


Ontology Wasm CDT编译器包含一个入口点和解析参数,因此开发人员无需重新定义输入方法。 此外,要编写服务逻辑,您可以调用智能合约的API方法。

 ONTIO_DISPATCH(hello, (sayHello)); 

在上面的示例中,到目前为止,我们仅支持sayHello:

 printf("hello world!"); 

“你好,世界!” 将显示在调试节点日志中。 直接编写智能合约时,printf仅可用于调试,因为智能合约本身包含更多功能命令。

智能合约API


本体论Wasm提供以下API与服务器区块链进行交互:

图片

示例2:钱信封


现在,让我们来看一个使用Wasm智能合约API的更复杂的示例。

在此示例中,我们将编写一个虚拟货币信封,红色信封(hongbao)的类似物是中国微信使者的流行功能,它使您可以在聊天中向朋友汇款。 用户收到一个红色信封形式的消息,打开该消息,这笔钱将自动记入帐户余额中。

作为智能合约的一部分,用户可以使用该合约使用虚拟货币信封将ONT,ONG或OEP-4令牌发送给他们的朋友,然后朋友可以将令牌转移到他们的区块链钱包中。

准备创建合同


首先,创建源合同文件,并将其命名为redEnvelope.cpp。 接下来,我们需要此合同的三个API:

  • createRedEnvelope :创建一个钱信封
  • queryEnvelope :请求信封信息
  • ClaimEnvelope :打开一个信封并收到钱

 #include<ontiolib/ontio.hpp> using namespace ontio; class redEnvlope: public contract{ } ONTIO_DISPATCH(redEnvlope, (createRedEnvlope)(queryEnvlope)(claimEnvelope)); 

现在我们需要保存键值。 在智能合约中,数据作为键值存储在合约的上下文中,我们需要在KEY数据中添加前缀以用于后续请求。

在下面,我们定义了三个将要使用的前缀:

 std::string rePrefix = "RE_PREFIX_"; std::string sentPrefix = "SENT_COUNT_"; std::string claimPrefix = "CLAIM_PREFIX_"; 

由于合同同时支持ONT和ONG这两个本体令牌,因此我们可以提前确定其合同地址。 与标准智能合约不同,Ontology自己的合约地址是固定的,并且不是从合约的哈希中得出的。

 address ONTAddress = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; address ONGAddress = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}; 

接下来,您需要在合同中保存有关已使用令牌的信息:合同令牌的地址,信封的总数和信封的数量。

 struct receiveRecord{ address account; //User address asset amount; //Received amount ONTLIB_SERIALIZE(receiveRecord,(account)(amount)) }; struct envlopeStruct{ address tokenAddress; //Token asset address asset totalAmount; //Total amount asset totalPackageCount; //Total number of red envelope asset remainAmount; //Remaining amount asset remainPackageCount; //Remaining number of red envelope std::vector<struct receiveRecord> records; //Received records ONTLIB_SERIALIZE( envlopeStruct, (tokenAddress)(totalAmount)(totalPackageCount)(remainAmount)(remainPackageCount)(records) ) }; 

以下是本体论Wasm CDT定义的宏操作,该操作用于数据结构化之前的序列化。

 ONTLIB_SERIALIZE(receiveRecord,(account)(amount)) 

创建信封


现在,我们已经完成了必要的准备工作,我们将开始API逻辑的开发。

1.在创建货币信封时,必须指出所有者的地址,信封的数量和数量以及代币的地址:

 bool createRedEnvlope(address owner,asset packcount, asset amount,address tokenAddr ){ return true; } 

2.检查所有者的签名,否则我们将回滚(回滚交易)并退出:

 ontio_assert(check_witness(owner),"checkwitness failed"); 

注意 :ontio_assert(expr,errormsg):false expr返回错误并退出合同。

3.如果在信封中使用了ONT令牌,请务必记住,ONT不会分段(至少1个ONT)。 然后,货币信封的总金额必须大于或等于令牌数量,以确保每个信封中至少有1个ONT:

 if (isONTToken(tokenAddr)){ ontio_assert(amount >= packcount,"ont amount should greater than packcount"); } 

4.接下来,我们为信封持有人确定他发送的货币信封总数:

 key sentkey = make_key(sentPrefix,owner.tohexstring()); asset sentcount = 0; storage_get(sentkey,sentcount); sentcount += 1; storage_put(sentkey,sentcount); 

5.生成信封的哈希-标记该信封的标识符:

 H256 hash ; hash256(make_key(owner,sentcount),hash) ; key rekey = make_key(rePrefix,hash256ToHexstring(hash)); 

6.我们将代币转换为合同。 我们使用self_address()命令找出当前正在执行的合同的地址,然后根据令牌的类型将分配的令牌数量转移到合同中:

 address selfaddr = self_address(); if (isONTToken(tokenAddr)){ bool result = ont::transfer(owner,selfaddr ,amount); ontio_assert(result,"transfer native token failed!"); }else if (isONGToken(tokenAddr)){ bool result = ong::transfer(owner,selfaddr ,amount); ontio_assert(result,"transfer native token failed!"); }else{ std::vector<char> params = pack(std::string("transfer"),owner,selfaddr,amount); bool res; call_contract(tokenAddr,params, res ); ontio_assert(res,"transfer oep4 token failed!"); } 

注1:对于ONT和ONG,Ontology Wasm CDT提供了ont :: transfer API来传输令牌; OEP-4令牌必须使用常规的跨合同呼叫方法发送。

注意2:合约地址可以像普通的钱包地址一样接受任何类型的令牌。 但是,合同地址是由编译后的二进制哈希生成的,因此没有对应的私钥,因此无法使用合同令牌。 如果尚未设置私钥,则将无法管理这些令牌。

7.将有关合同的信息保存在数据仓库中:

 struct envlopeStruct es ; es.tokenAddress = tokenAddr; es.totalAmount = amount; es.totalPackageCount = packcount; es.remainAmount = amount; es.remainPackageCount = packcount; es.records = {}; storage_put(rekey, es); 

8.发送有关信封创建的通知。 这是用于调用智能合约的异步过程,该合约还将发送有关执行结果的通知。 执行格式可以由合同的作者确定。

 char buffer [100]; sprintf(buffer, "{\"states\":[\"%s\", \"%s\", \"%s\"]}","createEnvlope",owner.tohexstring().c_str(),hash256ToHexstring(hash).c_str()); notify(buffer); return true; 

万岁,钱袋子已经快准备好了。 现在,让我们看看如何请求信封信息。

查询信封(查询


请求的逻辑很简单,您只需要从数据存储中获取信息和格式,然后输出:

 std::string queryEnvlope(std::string hash){ key rekey = make_key(rePrefix,hash); struct envlopeStruct es; storage_get(rekey,es); return formatEnvlope(es); } 

注意:对于只读智能合约操作(例如查询),您可以通过pre-exec查看结果。 与常规合同通话不同,高级执行人员不需要钱包签名,因此不需要ONG中的佣金。 完成此操作后,如果其他用户有信封哈希(信封ID),则现在可以申请信封。

接收信封


现在,在此阶段,我们已经成功地将令牌转移到了智能合约中,以便您的朋友可以在信封中成功申领份额,您需要向他们发送信封标识符(哈希)。

1.要收到信封,您必须输入帐户地址和信封的哈希值:

 bool claimEnvlope(address account, std::string hash){ return true; } 

2.接下来,合同将验证您的帐户签名,以确保您是帐户所有者。 每个帐户只能申请一次信封:

 ontio_assert(check_witness(account),"checkwitness failed"); key claimkey = make_key(claimPrefix,hash,account); asset claimed = 0 ; storage_get(claimkey,claimed); ontio_assert(claimed == 0,"you have claimed this envlope!"); 

3.根据从商店收到的哈希信息检查是否收到信封:

 key rekey = make_key(rePrefix,hash); struct envlopeStruct es; storage_get(rekey,es); ontio_assert(es.remainAmount > 0, "the envlope has been claimed over!"); ontio_assert(es.remainPackageCount > 0, "the envlope has been claimed over!"); 

4.创建索赔记录:

 struct receiveRecord record ; record.account = account; asset claimAmount = 0; 

5.计算每个信封申请人的金额。
对于最后一个参与者,确定剩余的数量,对于其他参与者,声明的数量由当前块的哈希值和有关信封的当前信息计算出的随机数确定:

 if (es.remainPackageCount == 1){ claimAmount = es.remainAmount; record.amount = claimAmount; }else{ H256 random = current_blockhash() ; char part[8]; memcpy(part,&random,8); uint64_t random_num = *(uint64_t*)part; uint32_t percent = random_num % 100 + 1; claimAmount = es.remainAmount * percent / 100; //ont case if (claimAmount == 0){ claimAmount = 1; }else if(isONTToken(es.tokenAddress)){ if ( (es.remainAmount - claimAmount) < (es.remainPackageCount - 1)){ claimAmount = es.remainAmount - es.remainPackageCount + 1; } } record.amount = claimAmount; } es.remainAmount -= claimAmount; es.remainPackageCount -= 1; es.records.push_back(record); 

6.贷记资金


根据计算结果,将相应数量的代币转移到信封申请人的账户中:

 address selfaddr = self_address(); if (isONTToken(es.tokenAddress)){ bool result = ont::transfer(selfaddr,account ,claimAmount); ontio_assert(result,"transfer ont token failed!"); } else if (isONGToken(es.tokenAddress)){ bool result = ong::transfer(selfaddr,account ,claimAmount); ontio_assert(result,"transfer ong token failed!"); } else{ std::vector<char> params = pack(std::string("transfer"),selfaddr,account,claimAmount); bool res = false; call_contract(es.tokenAddress,params, res ); ontio_assert(res,"transfer oep4 token failed!"); } 

7.我们将记下有关资金接收的信息以及有关保管箱中信封的最新信息,并发送有关合同履行的通知:

 storage_put(claimkey,claimAmount); storage_put(rekey,es); char buffer [100]; std::sprintf(buffer, "{\"states\":[\"%s\",\"%s\",\"%s\",\"%lld\"]}","claimEnvlope",hash.c_str(),account.tohexstring().c_str(),claimAmount); notify(buffer); return true; 

如上所述,该合同可以通过ClaimEnvelope API从合同中发送令牌。 这样就可以确保令牌在信封中时的安全性,因为没有人不能在没有满足必要要求的情况下提取资产。

做完了! 您编写了第一个智能合约。 完整的合同代码可以在GitHub上找到

合同测试


有两种验证合同的方法:

  1. 使用CLI
  2. 使用Golang SDK

结论


在本文中,我们讨论了如何使用区块链API为Ontolgy Wasm编写智能合约。 仍然需要解决隐私问题,以便将智能合约转变为成熟的产品。 在代码的这一阶段,任何人都可以通过跟踪合同记录来获得红色信封的哈希,这意味着任何人都可以要求获得信封中的份额。 这个问题可以简单地解决-我们定义了一个可以在创建信封时申请的帐户列表。 如果需要,也可以测试此功能。



从$ 20,000获得dApp开发本体资助

申请本体学生人才计划



您是开发人员吗? 加入我们的Discord技术社区。 此外,请访问我们网站上的开发人员中心 ,您可以在其中找到开发人员工具,文档等。

本体论


Source: https://habr.com/ru/post/zh-CN473784/


All Articles