Ontology Wasm自测试网上线以来,吸引了社区开发者的极大关注。Ontology Wasm的上线将降低业务逻辑复杂的dApp合约上行成本,极大丰富dApp生态。开发本体Wasm契约时,开发者不仅可以使用Rust,还可以使用C作为契约开发语言。本期我们将通过两个简单的例子来演示如何使用C开发本体Wasm契约。
按照惯例,我们是不是先来个Hello World
# include # include using?命名空间?ontio班级?你好:公?合同?{public:使用?合同:合同:作废?sayHello(){printf('hello?世界!);}};ONTIO_DISPATCH(你好,(say hello));本体Wasm CDT编译器已经封装了入口和参数解析,开发者不需要重新定义入口方法。下一步是定义契约的外部接口,这是智能契约向外界提供服务的方法。
ONTIO_DISPATCH(你好,(say hello));在上面的例子中,我们暂时只支持sayHello方法:
printf('hello?世界!);这个“Hello world!”调试信息将打印在节点的日志中。在实际应用中,printf只能用于调试目的。作为一个实际的智能合约,需要实现越来越复杂的功能。
本体Wasm提供以下API与区块链底层交互:
红包合约这里我们将通过一个更复杂的例子来演示如何通过这些API开发一个完整的Wasm智能合约。很多时候,我们会通过各种app发出红包,比如微信等聊天工具。我们可以给朋友发红包,也可以抢别人发的红包,收到的钱会打入个人微信账户。类似于微信的流程,我们会尝试创建一个智能合约。通过该合约,用户可以向好友发送ONT、ONG或标准OEP-4令牌资产红包,好友抢到的红包可以直接转到自己的钱包账户。2.1创建契约首先我们需要创建一个新的契约源文件,暂时命名为redEnvelope.cpp。这个契约我们需要三个接口:createRedEnvelope:创建红包queryEnvelope:查询红包信息claime envelope:抢红包
# including namespace;class redEnvelope:public contract { } ONTIO _ DISPATCH(redEnvelope,(createrendenvelope)(query envelope)(claimen velope));
我们需要在存储中保留一些关键数据。在智能契约中,数据以KV的形式存储在契约的上下文空间中,这些数据的键需要加上前缀,方便后续的查询。接下来定义了三种不同的前缀供使用:std:string?rePrefix?=?RE _ PREFIX _ ';std:string?sentPrefix?=?SENT _ COUNT _ ';std:string?claimPrefix?=?CLAIM _ PREFIX _ ';
因为我们的契约支持本体的原生资产ONT和ONG,所以我们可以预先定义这两个资产的契约地址。与标准智能契约不同的是,本体原生契约的契约地址是固定的,而不是根据契约代码的哈希来计算。address on address={ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 };地址?尚力财经小编2022ONGAddress?=?{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2};
我们需要在合同中保存红包的信息,比如红包的资产信息(token的合同地址,红包总额,红包数量等。).structreceiveRecord { address account;//用户地址assetamount//抓取的金额ONTLIB_SERIALIZE(receiveRecord,(account)(amount))};structEnvelopeStruct { address token address;//资产令牌的地址assettotalAmount//红包资产总额assettotalPackageCount//红包总数assetremainAmount//当前剩余金额assetremainPackageCount//当前剩余的红包数STD:vector records;//被抢走的记录是ontlib 尚力财经小编2022 _ serialize(信封结构,(令牌地址)(总金额)(总金额)(剩余金额)(剩余包数)(记录))};
其中:ontlib _ serialize(receive record,(account)(amount))
是Ontology Wasm CDT定义的宏操作,用于在存储结构之前对其进行序列化。2.2准备创建红包的时间差不多了。让我们开始开发具体的接口逻辑。1.创建红包,需要指定创建者地址、红包数量、红包金额和资产的合同地址:boolcreatedenvelope(Address Owner,assetpackcount,assetamount,addresstokenAddr){ return true;}
2。检查是否有创建者的签名,否则事务回滚并退出:on TiO _ assert(check _ witness(owner),' checkwitness?失败’);
注意:ontio_assert(expr,errormsg):当expr为false时,抛出异常并退出。3。如果红包资产是ONT,由于ONT的不可分性(最少一个ONT),红包金额应该大于等于红包个数,每个红包至少保证有一个ONT:if(iso token(token addr)){ ontio _ assert(amount=pack count,' ontamountshouldthanpac)}
4 .对于每个红包的创建者,我们需要记录他发送的红包总数:KEYSENTKEY=make _ key(sent prefix,owner . tohexstring());assetsentcount=0;storage_get(sentkey,sent count);sent count=1;storage_put(sentkey,sent count);
5。生成红包哈希,这是后面唯一标识这个红包的ID:h 256 hash;hash256(make_key(owner,sentcount),hash);钥匙?瑞琪。=?make_key(rePrefix,hash 256 tohexstring(hash));
6。根据令牌资产的类型,将资产转移到契约中,self_address()可以得到当前执行的契约的地址。我们根据用户输入的令牌类型将指定数量的令牌转移到契约中:AddressSelfaddr=self _ address();if(isonottoken(token addr)){ bool result=ont:transfer(owner,selfaddr,amount);ontio_assert(result,' transfernativetokenfailed!' );} else if(isONGToken(token addr)){ bool result=ONG:transfer(owner,selfaddr,amount);ontio_assert(result,' transfernativetokenfailed!' );} else { STD:vector params=pack(STD:string(' transfer '),owner,selfaddr,amount);布尔雷斯;call_contract(tokenAddr,params,RES);ontio_assert(res,' transferoep4tokenfailed!' );}
注1:对于ONT和ONG这两个原生资产,Ontology Wasm CDT提供ont:transfer API进行转移操作;OEP-4资产需要根据通用的跨合同调用方法进行转移。注2:和普通钱包地址一样,契约地址也可以接受任何类型的资产。但是契约的地址是由契约编译后的二进制代码hash生成的,所以没有对应的私钥,所以不能随意操作契约中的资产。如果你没有在合同中设置对资产的操作,就意味着你将无法控制这部分资产。7。将合同信息保存在存储器中:struct?EnvelopeStruct?es?es . token address=token addr;es.totalAmount=金额;es . totalpackagecount=pack count;es.remainAmount=amountes . remainpackagecount=pack count;es . records={ };storage_put(rekey,es);
8。发送创建红包的事件。对智能协定的调用是一个异步过程。合同执行成功后,会发送一个事件通知客户端执行结果。该事件的格式可以由合同的作者指定。char buffer[100];sprintf(buffer,' {'states':['%s ','%s ','%s']} ',' createEnvelope ',owner.tohexstring()。c_str(),hash256ToHexstring(hash)。c _ str());通知(缓冲区);回归?真实;
已经创建了一个简单的红包。接下来,我们需要实现如何查询这个红包的信息。2.3查询红包STD:StringQueryEnvelope(STD:String Hash){ key re-envelope structEnvelopeStructes;storage_get(rekey,es);returnformatEnvelope}注意:对于智能合约的只读操作(如查询),结果可以由pre-exec读取。与普通的合同调用不同,预执行不需要钱包的签名,也不需要ONG的成本。最后其他用户可以根据hash(红包ID)领取(抢到)红包。
2.4收红包
1。领取红包需要输入收件人的账号和红包的hash:
Boolclaimenenvelope(address account,STD:string hash){ return true;}2。同样,我们需要验证收款账户的签名。不允许为他人抢红包,每个红包每个账号只能抢一次:
on TiO _ assert(check _ witness(account),' checkwitness failed ');key claim key=make _ key(claimPrefix,hash,account);asset claimed=0;storage_get(claimkey,claimed);ontio_assert(已声明?==?0,‘你?有吗?声称?这个?信封!”);3。根据hash从存储中取出红包的信息,判断红包是否未被抢过:
key rekey=make _ key(前缀,hash);structEnvelopeStructesstorage_get(rekey,es);ontio_assert(es.remainAmount0,' theEnvelopehasbeenclaimedover!');ontio _ assert(es . remainpackagecount?0,那个?信封?有吗?去过吗?声称?结束了!);4。创建新的集合记录:
StructreceiverRecord;record.account=帐户;资产?克莱蒙特?=?0;5。算算这次收红包的资产数量。如果是最后一个红包,数量就是剩余金额;否则根据当前块hash计算随机数,确定本次领取数量,更新红包信息:
if(es . remainipackagecount==1){申领金额=es.remainin金额;record.amount=claimAmount} else { h 256 random=current _ block hash();char part[8];memcpy(部分,随机,8);uint64_trandom_num=*(uint64_t*)部分;uint32_tpercent=random_num0 1;claimAmount=es.remainAmount*percent/100;//ontcaseif(claimAmount==0){claimAmount=1;}elseif(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. 根据计算结果, 将对应资产从合约中转到领取的账户:
addressselfaddr=self_address();if(isONTToken(es.tokenAddress)){boolresult=ont::transfer(selfaddr,account,claimAmount);ontio_assert(result,"transferonttokenfailed!");}elseif(isONGToken(es.tokenAddress)){boolresult=ong::transfer(selfaddr,account,claimAmount);ontio_assert(result,"transferongtokenfailed!");}else{std::vectorparams=pack(std::string("transfer"),selfaddr,account,claimAmount);boolres=false;call_contract(es.tokenAddress,params,res);ontio_assert(res,"transferoep4tokenfailed!");}7. 记录领取的信息, 将更新后的红包信息写回存储并发送通知事件:
storage_put(claimkey,claimAmount);storage_put(rekey,es);charbuffer[100];std::sprintf(buffer,"{"states":["%s","%s","%s","%lld"]}","claimEnvelope",hash.c_str(),account.tohexstring().c_str(),claimAmount);notify(buffer);return?true;如前面所说,这个合约只能通过 claimEnvelope 这个接口将资产转出合约。所以,合约中的资产是安全的,任何人都无法随意的取走里面的资产。至此, 一个简单的红包合约逻辑完成, 完整的合约代码如下: https://github.com/JasonZhouPW/pubdocs/blob/master/redEnvlope.cpp
2.5合约测试
使用 CLI
请参考:https://github.com/ontio/ontology-wasm-cdt-cpp/blob/master/How_To_Run_ontologywasm_node.md
使用 Golang SDK
请参考:https://github.com/ontio/ontology-wasm-cdt-cpp/blob/master/example/other/main.go
总结
Ontology 作为领先公链,率先支持 Wasm 合约,为 Wasm 技术的成熟贡献自己的一份力量。我们欢迎更多的 Wasm 技术爱好者加入本体开发社区,共同打造技术生态。