以太坊智能合约的编程目前主流的技术之一就是Solidity语言,该语言与JavaScript有多种相似的特性,并且支持使用JavaScript编写单元测试脚本。今天以实现区块链上的分布式商店积分、积分兑换、积分交易系统的后端逻辑为例子记录一下Solidity编程过程。
编程过程中值得注意的点以注释的格式表示。
pragma solidity ^0.5.0;//约定Solidity版本
contract Loyalty {
address owner;//address类型是区块链编程所特有的基本类型,指向链中某一个具体区块的位置
uint numshop=0;//uint类型是solidity语言整数类型,uint32表示32bits的整数,类似uint256等
uint issuetime=0;
constructor()public{//Solidity初始化合约的语法-构造器
owner=msg.sender;//区块链合约被部署时,调用合约的区块就是信息的发送人
}
mapping (uint => shop) shops;//映射类型是获取区块地址、目标对象的方法
mapping (address => bool) shopss;
mapping (address => uint) shopids;
mapping (uint => shoppoint) shoppoints;
struct shop {//定义商店对象,并设置对象属性
uint shopId;
address shopOwner;
uint shopBalance;
}
struct shoppoint {//A set of points from a shop to a customer
uint shopId;
address customer;
uint point;
}
event Redeem (//区块链智能合约需要与前端交互
uint shopId,
uint point,
uint transactionId
);
function onboardShop(address shopadr) public returns (uint){ //callable only by network owner
numshop++;//定义商店列表函数,买卖前需要注册商店
uint256 shopId=numshop;
shops[shopId]=shop(shopId,shopadr,0);
shopss[shopadr]=true;
shopids[shopadr]=shopId;
return shopId;
}
function issuePoint(uint shopId, uint point, address customer) public{ //callable only by respective shop owner
require(shopss[msg.sender]=true,"Only respective shop can issue points.");
issuetime++;
shoppoints[issuetime]=shoppoint(shopId,customer,point);//issuence
shops[shopId].shopBalance+=point;//shop record
}//定义积分发送函数
//To realize the freedom of amount, resum customer's balance everytime he redeems
function redeemPoint(uint _shopId, uint _point, uint transactionId) public returns(uint){
require((msg.sender != owner) && (shopss[msg.sender]==false),"Only customer can redeem points.");//callable by customer only
uint i;
uint thebalance=0;
for (i=0;i<=issuetime;i++){//compute balance
if ((shoppoints[i].customer==msg.sender) && (shoppoints[i].shopId ==_shopId)){
thebalance+=shoppoints[i].point;//destory old balance
delete shoppoints[i];
}
}
if (thebalance < _point){//逻辑校验
return 0;
}
thebalance-=_point;
if (thebalance>0){
issuetime++;
shoppoints[issuetime]=shoppoint(_shopId,msg.sender,thebalance);//New balance
}
shops[_shopId].shopBalance-=_point;//shop record
emit Redeem(_shopId,_point,transactionId);
return 1;
}
//对函数调用权限进行控制
function transferPoint(uint _shopId, uint _point, address recipient) public{ //callable by customer
require(msg.sender != owner && shopss[msg.sender]==false,"Only customer can transfer.");
require(recipient != owner && shopss[recipient]==false,"Recipient should be customer.");
uint i;
uint thebalance;
for (i=0;i<=issuetime;i++){//compute balance
if ((shoppoints[i].customer==msg.sender) && (shoppoints[i].shopId==_shopId)){
thebalance+=shoppoints[i].point;//destory old balance
delete shoppoints[i];
}
}
thebalance-=_point;
require(thebalance>=0,"You can't transfer because you don't have enough points.");
if (thebalance>0){
issuetime++;
shoppoints[issuetime]=shoppoint(_shopId,msg.sender,thebalance);//New balance
}
issuetime++;
shoppoints[issuetime]=shoppoint(_shopId,recipient,_point);
}
function pointBalance(uint _shopId, address _customer) public view returns(uint){ //balance of a customer
uint i;
uint thebalance;
for (i=0;i<=issuetime;i++){//compute balance
if ((shoppoints[i].customer==_customer)&& (shoppoints[i].shopId==_shopId)){
thebalance+=shoppoints[i].point;
}
}
return thebalance;
}
//查询函数应该可以被多方调用
function totalBalance(uint shopId) public view returns(uint){ //total points issued by a shop
if (shops[shopId].shopBalance>100000000000000000000000000){//It's a obviously baleful number, so defend the shop from attack
return 0;
}else{return shops[shopId].shopBalance;}
}
function checkShopId(address shop) public view returns(uint){
return shopids[shop];
}
}
至此实现了Solidity编程环境内的商店积分发送、交易功能,可运行于以太坊上。