FISCO BCOS——SmartDev-Contract——RewardPoint积分模板合约案例分析
Posted Blockchain_KT
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FISCO BCOS——SmartDev-Contract——RewardPoint积分模板合约案例分析相关的知识,希望对你有一定的参考价值。
RewardPoint积分模板合约案例分析
一、合约场景分析
在区块链的业务方案中,积分系统是非常常见的一种场景。
基于智能合约的积分系统,支持多家机构发行、用户注册、积分消费、积分销户、用户销户等多种功能。
典型的积分场景包括了以下角色:
- 发行者:发行积分。
- 用户:获得和消费积分,还可实现积分的转账。
- 管理者:积分系统的管理者。
积分系统中包括了以下功能:
- 创建积分系统
- 注册:用户注册会员,只有经过注册的用户才能获得和消费积分。
- 销户: 用户销户。只有积分为0的用户才能正常销户。
- 添加发行者: 发行者具有发行积分的权利。
- 发行积分: 支持定向发送到某个积分账户。
- 删除发行者: 发行者可以注销自身的发行积分的权利。
- 转账: 支持用户积分的互转和消费等。
- 积分销毁: 用户可以销毁自己的积分。
源码库:
https://github.com/WeBankBlockchain/SmartDev-Contract
源码分析者:
github:Blockchain_Key
源码链接:
https://github.com/WeBankBlockchain/SmartDev-Contract/tree/master/contracts/business_template/reward_point
二、合约场景说明
三、基础库合约介绍(无单独使用)
1. 角色库合约
LibRoles.sol
pragma solidity ^0.4.25;
library LibRoles
struct Role
mapping (address => bool) bearer;
function add(Role storage role, address account) internal
require(!has(role, account), "Roles: account already has role");
role.bearer[account] = true;
function remove(Role storage role, address account) internal
require(has(role, account), "Roles: account does not have role");
role.bearer[account] = false;
function has(Role storage role, address account) internal view returns (bool)
require(account != address(0), "Roles: account is the zero address");
return role.bearer[account];
(1)功能说明
本合约作为库合约,进行角色增,删,查操作
(2)接口说明
add(Role storage role, address account)
:添加角色remove(Role storage role, address account)
:删除角色has(Role storage role, address account) internal view returns (bool)
:查询角色,有返回true,没有返回false
(3)使用说明
作为库合约,不能直接部署,必须由其他合约引入使用
2.运算库合约
LibSafeMath.sol
pragma solidity ^0.4.25;
library LibSafeMath
function sub(uint256 a, uint256 b) internal pure returns (uint256)
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
function add(uint256 a, uint256 b) internal pure returns (uint256)
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
(1)功能说明
本合约作为安全运算库合约,进行加,减操作
(2)接口说明
sub(uint256 a, uint256 b) internal pure returns (uint256)
:传入uint256类型的两个参数,进行想减,返回减后结果add(uint256 a, uint256 b) internal pure returns (uint256)
:传入uint256类型的两个参数,进行想加,返回加后结果
(3)使用说明
作为库合约,不能直接部署,必须由其他合约引入使用
四、业务封装合约介绍(无对外接口)
1.角色库合约应用
IssuerRole.sol
:
pragma solidity ^0.4.25;
import "./LibRoles.sol";
contract IssuerRole
using LibRoles for LibRoles.Role;
event IssuerAdded(address indexed account);
event IssuerRemoved(address indexed account);
LibRoles.Role private _issuers;
constructor () internal
_issuers.add(msg.sender);
modifier onlyIssuer()
require(isIssuer(msg.sender), "IssuerRole: caller does not have the Issuer role");
_;
function isIssuer(address account) public view returns (bool)
return _issuers.has(account);
function addIssuer(address account) public
_issuers.add(account);
emit IssuerAdded(account);
function renounceIssuer(address account) public onlyIssuer
_issuers.remove(account);
emit IssuerRemoved(account);
(1)功能说明
引入库合约LibRoles.sol,具体化角色合约,提供角色判定是否存在,增加,销毁操作
(2)接口说明
isIssuer(address account) public view returns (bool)
:传入地址,判定当前地址是否存在,返回bool类型addIssuer(address account)
:传入地址,添加此地址角色renounceIssuer(address account)
:调用者必须是此合约的创建者,传入地址,删除此地址角色
(3)使用说明
作为封装合约,此处不单独使用
2.合约管理员
BasicAuth.sol
pragma solidity ^0.4.25;
contract BasicAuth
address public _owner;
constructor() public
_owner = msg.sender;
modifier onlyOwner()
require(auth(msg.sender), "Only owner!");
_;
function setOwner(address owner)
public
onlyOwner
_owner = owner;
function auth(address src) public view returns (bool)
if (src == address(this))
return true;
else if (src == _owner)
return true;
else
return false;
(1)功能说明
此合约提供修改部署此合约的初始管理员地址,以及判定地址是否为合约初始管理员地址或修改后的管理员地址
(2)接口说明
- setOwner(address owner):传入一个地址,设置当前地址为合约管理员
- auth(address src) public view returns (bool):判定传入的src地址是否为合约管理员地址
(3)使用说明
作为封装合约,此处不单独使用
3.底层数据存储
RewardPointData.sol
pragma solidity ^0.4.25;
import "./BasicAuth.sol";
import "./IssuerRole.sol";
contract RewardPointData is BasicAuth, IssuerRole
mapping(address => uint256) private _balances;
mapping(address => bool) private _accounts;
uint256 public _totalAmount;
string public _description;
address _latestVersion;
constructor(string memory description) public
_description = description;
modifier onlyLatestVersion()
require(msg.sender == _latestVersion);
_;
function upgradeVersion(address newVersion) public
require(msg.sender == _owner);
_latestVersion = newVersion;
function setBalance(address a, uint256 value) onlyLatestVersion public returns (bool)
_balances[a] = value;
return true;
function setAccount(address a, bool b) onlyLatestVersion public returns (bool)
_accounts[a] = b;
return true;
function setTotalAmount(uint256 amount) onlyLatestVersion public returns (bool)
_totalAmount = amount;
return true;
function getAccountInfo(address account) public view returns (bool, uint256)
return (_accounts[account], _balances[account]);
function hasAccount(address account) public view returns(bool)
return _accounts[account];
function getBalance(address account) public view returns (uint256)
return _balances[account];
(1)功能说明
此合约提供版本更替,设置积分余额,设置账户是否存在,设置积分总量,获取地址是否存在以及地址余额,判定账户是否存在,获取账户余额
(2)接口说明
upgradeVersion(address newVersion)
:更换版本地址,仅管理员可操作setBalance(address a, uint256 value) onlyLatestVersion public returns (bool)
:传入地址和uint256类型的参数,给此地址设置余额setAccount(address a, bool b) onlyLatestVersion public returns (bool)
:传入地址和bool类型的参数,给此地址设置是否存在getAccountInfo(address account) public view returns (bool, uint256)
:传入地址,获取此地址的相关信息(是否存在,余额多少)hasAccount(address account) public view returns(bool)
:传入地址,判定此地址是否存在getBalance(address account) public view returns (uint256)
:传入地址,获取此地址的余额
(3)使用说明
作为封装合约,此处不单独使用
五、业务接口合约介绍
1.管理员部署合约
Admin.sol
pragma solidity ^0.4.25;
import "./BasicAuth.sol";
import "./RewardPointController.sol";
import "./RewardPointData.sol";
contract Admin is BasicAuth
address public _dataAddress;
address public _controllerAddress;
constructor() public
RewardPointData data = new RewardPointData("Point of V1");
_dataAddress = address(data);
RewardPointController controller = new RewardPointController(_dataAddress);
_controllerAddress = address(controller);
data.upgradeVersion(_controllerAddress);
data.addIssuer(msg.sender);
data.addIssuer(_controllerAddress);
function upgradeVersion(address newVersion) public
RewardPointData data = RewardPointData(_dataAddress);
data.upgradeVersion(newVersion);
(1)功能说明
对外接口合约,用于初始化数据存储合约;以数据存储合约为地址,初始化创建积分控制台合约;更新版本地址信息
(2)接口说明
constructor()
:构造函数中,初始化各个合约以及对相关信息进行添加upgradeVersion(address newVersion)
:传入新版本地址,修改地址
(3)使用说明
合约部署进行初始化操作,主要接口调用_controllerAddress(),用于获取RewardPointController合约的地址
- 合约部署
- 调用_controllerAddress,用于获取RewardPointController合约的地址
2.积分控制台合约
RewardPointController.sol
pragma solidity ^0.4.25;
import "./RewardPointData.sol";
import "./BasicAuth.sol";
import "./LibSafeMath.sol";
contract RewardPointController is BasicAuth
using LibSafeMath for uint256;
RewardPointData _rewardPointData;
event LogRegister(address account);
event LogUnregister(address account);
event LogSend( address indexed from, address indexed to, uint256 value);
constructor(address dataAddress) public
_rewardPointData = RewardPointData(dataAddress);
modifier accountExist(address addr)
require(_rewardPointData.hasAccount(addr)==true && addr != address(0), "Only existed account!");
_;
modifier accountNotExist(address account)
require(_rewardPointData.hasAccount(account)==false, "Account already existed!");
_;
modifier canUnregister(address account)
require(_rewardPointData.hasAccount(account)==true && _rewardPointData.getBalance(account) == 0 , "Cann't unregister!");
_;
modifier checkAccount(address sender)
require(msg.sender != sender && sender != address(0), "Can't transfer to illegal address!");
_;
modifier onlyIssuer()
require(_rewardPointData.isIssuer(msg.sender), "IssuerRole: caller does not have the Issuer role");
_;
function register() accountNotExist(msg.sender) public returns (address)
_rewardPointData.setAccount(msg.sender, true);
// init balances
_rewardPointData.setBalance(msg.sender, 0);
emit LogRegister(msg.sender);
function unregister() canUnregister(msg.sender) public returns (address)
_rewardPointData.setAccount(msg.sender, false);
emit LogUnregister(msg.sender);
function isRegistered(address addr) public view returns (bool)
return _rewardPointData.hasAccount(addr);
function balance(address addr) public view returns (uint256)
return _rewardPointData.getBalance(addr);
function transfer(address toAddress, uint256 value) accountExist(msg.sender) accountExist(toAddress)
public returns(bool b, uint256 balanceOfFrom, uint256 balanceOfTo)
uint256 balance1 = _rewardPointData.getBalance(msg.sender);
balanceOfFrom = balance1.sub(value);
_rewardPointData.setBalance(msg.sender, balanceOfFrom);
uint256 balance2 = _rewardPointData.getBalance(toAddress);
balanceOfTo = balance2.add(value);
_rewardPointData.setBalance(toAddress, balanceOfTo);
emit LogSend(msg.sender, toAddress, value);
b = true;
function destroy(uint256 value) accountExist(msg.sender) public returns (bool)
uint256 totalAmount = _rewardPointData._totalAmount();
totalAmount = totalAmount.sub(value);
_rewardPointData.setTotalAmount(totalAmount);
uint256 balance1 = _rewardPointData.getBalance(msg.sender);
balance1 = balance1.sub(value);
_rewardPointData.setBalance(msg.sender, balance1);
emit LogSend( msg.sender, address(0), value);
return true;
function issue(address account, uint256 value) public accountExist(account) returns (bool)
uint256 totalAmount = _rewardPointData._totalAmount();
totalAmount = totalAmount.add(value);
_rewardPointData.setTotalAmount(totalAmount);
uint256 balance1 = _rewardPointData.getBalance(account);
balance1 = balance1.add(value);
_rewardPointData.setBalance(account, balance1);
emit LogSend( address(0), account, value);
return true;
function isIssuer(address account) public view returns (bool)
return _rewardPointData.isIssuer(account);
function addIssuer(address account) public returns (bool)
_rewardPointData.addIssuer(account);
return true;
function renounceIssuer() public returns (bool)
_rewardPointData.renounceIssuer(msg.sender);
return true;
(1)功能说明
此合约作为积分控制控制,用于注册账户,销毁账户,判定账户是否已经创建,查看账户余额,给某地址转发余额,注销某数量的账户余额等
(2)接口说明
- addIssuer(address account) :添加资产发行者。只有资产发行者可以添加新的资产发行者。
- issue(address account, uint256 value) :发行积分
- isIssuer(address account) :判断是否是资产发行者。
- addIssuer(address account) :添加发行者,只能由发行者添加。
- renounceIssuer() :撤销发行者,只能撤销自己。
- register() :普通用户账户注册。
- unregister() :普通用户账户注销。
- isRegistered(address addr) :判断普通用户是否注册。
- balance(address addr) :查询普通用户的积分。
- transfer(address toAddress, uint256 value) :往目标账户转积分
- destroy(uint256 value) :销毁自己账户中的指定数量的积分
(3)使用说明
-
不需要重新部署,以Admin.sol合约中调用_controllerAddress函数返回的RewardPointController合约地址来调用RewardPointController合约
-
资产发行者操作说明:
(1)管理员admin通过addIssuer添加其他发行者KT1
(2)通过isIssuer接口查询某个用户是否为资产发行者
(3)发行者通过renounceIssuer接口撤销自己发行者的权限,并再次查询KT1是否为发行者
-
普通用户账户注册
(1)通过register()接口注册普通用户账户user,积分默认为0
安装FISCO-BCOS的那些坑