FISCO BCOS——SmartDev-Contract——RewardPoint积分模板合约案例分析

Posted Blockchain_KT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FISCO BCOS——SmartDev-Contract——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)使用说明