应用示例

本文提供基于实际场景的合约设计示例,供开发人员参考。下面以基于智能合约的住房租赁积分管理系统为例,阐述如何设计和实现智能合约。

说明

本文中的示例仅供参考,不可应用于生产环境。

背景

基于区块链智能合约的住房租赁积分管理系统旨在有效地服务于公众群体、市民,真正让那些为城市当前发展做出努力的人有房住、租得起,让那些为城市建设长远发展做出贡献的人有房产、买得起。为此,建立住房租赁积分制度,从住房租赁市场主体属性、政策激励、租赁行为三方面,运用区块链、大数据等前沿技术,建立科学、有效的住房租赁积分全生命周期管理机制,营造活力、健康、有序、可持续的住房租赁生态。

目标

通过使用区块链平台作为底层数据支撑,确保隐私保护和数据不可篡改,达到公平、公正、透明的目的;利用积分决策引擎,结合住房租赁积分评价体系,产出应用于住房租赁场景的综合积分方案。该方案具备横向扩展性,在底层技术、数据不变的基础上,未来能够支撑经济信用积分、绿色生活积分等诸多积分体系的建设,从而扩展形成城市信用体系,应用于如医疗教育、金融借贷、绿色生活等诸多场景。

合约设计

权限管理

在住房租赁积分管理系统中,主要分为4种角色,分别是管理者、操作者、观察者以及市民。管理者、操作者、观察者是智能合约的操作人员,每个角色可以对应多名人员。市民是租赁积分智能合约服务的对象,是整个智能合约的核心用户。

角色操作权限

  • 管理者是合约的超级管理员,能够对管理者、操作者以及观察者进行添加、删除、查询操作。

  • 操作者可以查询操作者信息。

  • 观察者可以查询观察者信息。

1

积分操作权限

  • 管理者:具有积分奖励、积分查询、积分转移、积分违规扣除、积分权益兑换等积分操作权限。

  • 操作者:操作者是由管理者设置的积分操作角色,该角色可以是具体的人员也可以是系统服务,具有积分奖励、积分转移、积分违规扣除、积分权益兑换等积分操作权限。

  • 观察者:观察者是由管理者设置的积分查询角色,该角色可以是具体的人员也可以是系统服务,具有积分查询的操作权限。

  • 市民:市民是住房租赁积分系统的用户,是住房租赁积分系统的实际权益对象。

    • 市民可以通过组织机构的租赁积分外部服务系统代理的观察者权限查看自己的积分状况。

    • 租赁积分外部服务系统会根据市民的合规表现,通过操作者的权限进行积分奖励或积分违规扣除。

    • 市民可以通过租赁积分外部服务系统代理的操作者权限进行积分权益兑换或积分转移操作。

2

积分管理

租赁积分智能合约提供的服务方法包括:积分奖励积分查询积分权益兑换积分违规扣除积分转移以及积分达到阈值后触发积分事件

3

积分奖励(awardScore)

在市民租赁房屋的过程中,针对租赁的时间以及租赁过程中产生的行为,为市民进行积分累计。

积分查询(queryScore)

市民可通过租赁积分外部服务系统代理的观察者权限查看自己的积分状况。

积分权益兑换(exchangeScore)

市民的积分达到指定阈值后可以享有相应的权益,如租房优惠、买房资格、落户资格等。

积分违规扣除(deductScore)

在市民租赁房屋的过程中,针对租赁过程中产生的错误行为,对市民惩罚性的扣减积分。住房租赁积分作为正向激励的积分体系,鼓励民众积极向上。对于积分主体的伪造资料行为,视情节严重程度扣减积分、取消积分资格以及收回积分对应的权益。

积分事件(SCORE_EQUITY_NOTICE)

当积分权益实时增加时,会触发积分权益检查。若积分达到了指定的阈值,则会产生相应的积分权益通知事件。通过外部服务系统通知到具体的市民。

积分转移(transferScore)

当市民因工作关系、家庭因素需要到其他城市生活时,可以将该城市的积分转移到目标城市的积分系统。为满足该合约的调用,目标合约需要有对应的积分奖励方法(awardScore),且需要为发起合约提供操作权限,即将发起合约的地址设置为操作者或管理员。

智能合约代码

pragma solidity ^0.4.20;

contract LeasingScoreManager {
    identity[] adminList;
    identity[] observerList;
    identity[] calculatorList;

    //用于记录管理数组被置位元素
    uint adminDeled;
    uint observerDeled;
    uint calculatorDeled;

    mapping (bytes32 => uint) leasingScore;

    enum ScoreAction {
        //奖励积分, 因相关行为,进行积分奖励
        ActionAwardScore,
        //积分权益兑换,花费指定积分
        ActionExchangeScore,
        //扣除积分, 因违规等, 扣除积分
        ActionDeductScore,
        //查询积分
        ActionQueryScore
        //积分转移, 将积分转移到其他的合约。
    }

    enum UserRole {
        RoleAdmin, //管理员
        RoleCalculator, //积分操作员
        RoleObserver, //积分查看人员
        RolePlayer //积分参与者
    }

    //是否为有效的操作人
    event VALID(bool valid, UserRole role);
    //admin/observer/calculator 用户存在
    event USER_EXIST(bool exist, UserRole role);
    //积分事件
    event SCORE_OPERATOR(ScoreAction action, string describe);
    //积分操作错误
    event SCORE_ERROR(ScoreAction action, string describe);
    //积分权益通知
    event SCORE_EQUITY_NOTICE(string action, uint score, string describe);

    constructor() public {
        adminList.push(msg.sender);
        adminList.push(this);
    }

    function indexAdmin(identity admin) view returns (uint) {
        for (uint i = 0; i < adminList.length; i++) {
            if (adminList[i] == admin) {
                return i;
            }
        }
        return adminList.length;
    }

    function validAdmin(identity admin) view returns (bool) {
        return indexAdmin(admin) < adminList.length;
    }

    function indexCalculator(identity calculator) view returns (uint) {
        for (uint i = 0; i < calculatorList.length; i++) {
            if (calculatorList[i] == calculator) {
                return i;
            }
        }
        return calculatorList.length;
    }

    function validCalculator(identity calculator) view returns (bool) {
        return indexCalculator(calculator) < calculatorList.length;
    }

    function indexObserver(identity observer) view returns (uint) {
        for (uint i = 0; i < observerList.length; i++) {
            if (observerList[i] == observer) {
                return i;
            }
        }
        return observerList.length;
    }

    function validObserver(identity observer) view returns (bool) {
        return indexObserver(observer) < observerList.length;
    }

    modifier onlyAdmin {
        bool isValid = validAdmin(msg.sender);
        emit VALID(isValid, UserRole.RoleAdmin);
        require(isValid);
        _;
    }

    modifier onlyCalculatorOrAdmin {
        bool isValid = validAdmin(msg.sender);
        if(isValid) {
            emit VALID(isValid, UserRole.RoleAdmin);
        } else {
            isValid = validCalculator(msg.sender);
            emit VALID(isValid, UserRole.RoleCalculator);
        }
        require(isValid);
        _;
    }

    modifier onlyObserverOrAdmin {
        bool isValid = validAdmin(msg.sender);
        if(isValid) {
            emit VALID(isValid, UserRole.RoleAdmin);
        } else {
            isValid = validObserver(msg.sender);
            emit VALID(isValid, UserRole.RoleObserver);
        }
        require(isValid);
        _;
    }

    function addAdmin(identity admin) public onlyAdmin {
        bool isExist = validAdmin(admin);
        emit USER_EXIST(isExist, UserRole.RoleAdmin);
        require(!isExist);
        if(adminDeled > 0) {
            uint deled = 1;
            for (uint i = 0; i < adminList.length; i++) {
                if(deled&adminDeled != 0) {
                    adminList[i] = admin;
                    adminDeled ^= deled;
                    break;
                }
                deled <<= 1;
            }
        } else {
            adminList.push(admin);
        }
    }

    function removeAdmin(identity admin) public onlyAdmin {
        uint index = indexAdmin(admin);
        bool isValid = index != adminList.length;
        emit USER_EXIST(isValid, UserRole.RoleAdmin);
        require(isValid);
        delete adminList[index];
        adminDeled ^= 1 << index;
    }

    function queryAdmins() view public onlyAdmin returns (identity[]) {
        return adminList;
    }

    function addCalculator(identity calculator) public onlyAdmin {
        bool isExist = validCalculator(calculator);
        emit USER_EXIST(isExist, UserRole.RoleCalculator);
        require(!isExist);
        if(calculatorDeled > 0) {
            uint deled = 1;
            for (uint i = 0; i < calculatorList.length; i++) {
                if(deled&calculatorDeled != 0) {
                    calculatorList[i] = calculator;
                    calculatorDeled ^= deled;
                    break;
                }
                deled <<= 1;
            }
        } else {
            calculatorList.push(calculator);
        }
    }

    function removeCalculator(identity calculator) public onlyAdmin {
        uint index = indexCalculator(calculator);
        bool isValid = index < calculatorList.length;
        emit USER_EXIST(isValid, UserRole.RoleCalculator);
        require(isValid);
        delete calculatorList[index];
        calculatorDeled ^= 1 << index;
    }

    function queryCalculators() view public onlyCalculatorOrAdmin returns (identity[]) {
        return calculatorList;
    }

    function addObserver(identity observer) public onlyAdmin {
        bool isExist = validObserver(observer);
        emit USER_EXIST(isExist, UserRole.RoleObserver);
        require(!isExist);
        if(observerDeled > 0) {
            uint deled = 1;
            for (uint i = 0; i < observerList.length; i++) {
                if(deled&observerDeled != 0) {
                    observerList[i] = observer;
                    observerDeled ^= deled;
                    break;
                }
                deled <<= 1;
            }
        } else {
            observerList.push(observer);
        }
    }

    function removeObserver(identity observer) public onlyAdmin {
        uint index = indexCalculator(observer);
        bool isValid = index < observerList.length;
        emit USER_EXIST(isValid, UserRole.RoleObserver);
        require(isValid);
        delete observerList[index];
        observerDeled ^= 1 << index;
    }

    function queryObservers() view public onlyObserverOrAdmin returns (identity[]) {
        return observerList;
    }

    function checkScoreEquity(uint balance, uint score) {
        uint total = balance + score;
        if(total >= 100 && balance < 100) {
            emit SCORE_EQUITY_NOTICE("RentConcessions", total, "Citizens enjoy a 90% discount on rental housing");
        }
        if(total >= 200 && balance < 200) {
            emit SCORE_EQUITY_NOTICE("RentConcessions_1", total, "Citizens enjoy a 80% discount on rental housing");
        }
        if(total >= 300 && balance < 300) {
            emit SCORE_EQUITY_NOTICE("PurchaseDiscount", total, "Citizens enjoy a 90% discount on purchase housing");
        }
    }

    //积分奖励
    function awardScore(bytes32 player, uint score, string describe) public onlyCalculatorOrAdmin {
        uint balance = leasingScore[player];
        leasingScore[player] = balance + score;
        emit SCORE_OPERATOR(ScoreAction.ActionAwardScore, describe);
        checkScoreEquity(balance, score);
    }

    //积分权级兑换,为消费者主动意愿,若不够花,则不扣除积分,且发送失败事件。
    function exchangeScore(bytes32 player, uint score, string describe) public onlyCalculatorOrAdmin returns (bool) {
        emit SCORE_OPERATOR(ScoreAction.ActionExchangeScore, describe);
        if(leasingScore[player] >= score) {
            leasingScore[player] -= score;
            return true;
        }
        emit SCORE_ERROR(ScoreAction.ActionExchangeScore, "Score not enough to exchange");
        return false;
    }

    //积分扣除,为消费者被动意愿,进行强制积分扣除,若积分不够,则清零,且发送失败时间
    function deductScore(bytes32 player, uint score, string describe) public onlyCalculatorOrAdmin {
        emit SCORE_OPERATOR(ScoreAction.ActionDeductScore, describe);
        uint balance = leasingScore[player];
        if(balance >= score) {
            leasingScore[player] -= score;
        } else {
            if(balance != 0) {
                leasingScore[player] = 0;
            }
            emit SCORE_ERROR(ScoreAction.ActionDeductScore, "Score not enough to deduct");
        }
    }

    //积分查询
    function queryScore(bytes32 player, string describe) view public onlyObserverOrAdmin returns (uint) {
        emit SCORE_OPERATOR(ScoreAction.ActionQueryScore, describe);
        return leasingScore[player];
    }

    //拼接字符串
    function stringAdd(string a, string b) returns(string){
        bytes memory _a = bytes(a);
        bytes memory _b = bytes(b);
        bytes memory res = new bytes(_a.length + _b.length);
        for(uint i = 0;i < _a.length;i++)
            res[i] = _a[i];
        for(uint j = 0;j < _b.length;j++)
            res[_a.length+j] = _b[j];  
        return string(res);
    }

    //积分转移,将积分从一个一个合约转移到另一个合约
    function transferScore(bytes32 player, uint score, LeasingScoreManager toContract, string describe) public onlyCalculatorOrAdmin {
        //判断积分是否够用并扣除
        describe =  stringAdd("Transfer score: ", describe);
        require(exchangeScore(player, score, describe));
        toContract.awardScore(player, score, describe);
    }
}