永恒之恋•山海珈锁——基于区块链的同心锁数字化替代方案

引言

很多情侣在确立了恋爱关系后,为了推进感情的发展,都会前往向往已久的名山大川旅行。情侣们到了景区之后会买同心锁,挂在一起游览过的地方,以此来象征爱情的永恒长久。同心锁不仅丰富了旅行,而且一定程度上促进了旅游业的发展。但是对于景区,每年都会接待数以万计的游客,可以用于挂锁的空间越来越少,而且也出现了随处乱挂的行为,因此部分景区对可挂锁的景点进行了限制。此外,出于安全管理以及景区的可持续发展等方面的考虑,景区会回收部分同心锁,以减少其重量对于旅游设施安全性的威胁。但是,此举确保安全的同时,也难免略显不妥,毕竟情侣分手后可能还会不远万里回到景区,只为寻找当年那把同心锁。

核心功能

为了解决上述问题,同时进一步升华同心锁的发明意义,本文提出了一种基于区块链技术的同心锁数字化替代方案——永恒之恋•山海珈锁。区块链具有天然的可信性和不可篡改性,在国内的应用以联盟链居多。通过区块链账本的分布式存储,可以确保链上数据的可追溯性和不可篡改性。

当情侣游历景点时,可以通过手机APP扫码的方式,将对彼此的山盟海誓以及当时的合照上传至APP,同时输入一个未来可能的结婚期限date,而APP会记录当前的上传时间Date。而后双方可以各自输入具有祝福意义的金额,但对于初次使用APP的用户并不用立即支付,只是一种数字约定。在date之前,若双方结婚,则可以凭借结婚信息(例如结婚证的截图)获得计算公式为(D[(当前时间 - Date)] / D[(date - Date)] 链上总金额 0.01)的资金奖励。(结婚越早,奖励越多,D[]为按天数取整)。若用户超过date未提供结婚信息,或者双方中某一位违背了曾经的山盟海誓导致其中一方选择提前开锁,则双方均需要支付当初输入计算公式为(D[(date - Date)] / D[(当前时间 - Date)] * 双方输入金额总和)的金额,当然也可以不支付,这样会被永恒之恋•山海珈锁系统永久拉黑,当然也可能导致多个景点的APP同时对用户进行拉黑处理。因此用户使用APP时需要进行身份认证。

对于在该景点的山海珈锁上已经有支付信息的用户而言,山海珈锁希望用户能够珍视感情,把握机会,因此当该用户再次使用时,会直接进行收费处理,例如输入金额时,会要求立即支付金额的50%,若在date前完成结婚信息的上传,则会返回已经支付的金额以及奖励。若依然未能如愿,则双方支付的金额按照扣除50%后的总金额计算。当date到达时,双方也可以选择延期,即重新设定date值,但双方均需要立即支付计算公式为([(新date - Date) / (旧date - Date)] 0.01 双方输入金额总和)的金额(新date越久远,支付越多) ,待提供结婚信息后,同样会一起返还。已经开启山海珈锁,即被锁定的两个用户中,任一一人在当前锁被解开之前,无法再次与另一个用户共同开启山海珈锁,从而一定程度上避免出轨与出墙。所有支付的金额均通过智能合约记录于链上,用于维持整个永恒之恋•山海珈锁分布式生态。

智能合约主要实现用户地址记录、金额记录、发放奖励等功能。系统后端实现计算时间、触发交易执行以及认证等功能。发放的奖励可以用于兑换对应景区的包含爱情哈希值的专属纪念品,如刻有爱情哈希值的戒指(爱情哈希值由智能合约基于防碰撞哈希函数生成,可理解为世间唯一),进而促进情侣重游景点。有关于时间计算的操作应该放到后端进行,但出于合约完整性,在合约中进行不完全实现,以自2023年开始的总天数统计各类参与计算的时间。在具体实现过程中涉及许多同步与互斥操作,例如已经被锁定的用户不能再次被同一把锁锁住(互斥),以及双方需要同步进行的操作,均以男生先操作,女生后操作的顺序执行(同步)。互斥与同步关系均可通过信号量实现。

意义

永恒之恋•山海珈锁方案致力于以区块链技术一定程度上助力旅游景点可持续发展,促进情侣恋爱关系的发展,构建友好生态。在以数字化方案替代实体同心锁的同时,节约了资源,利于环保,解决了实体锁乱挂现象所导致的安全隐患。为情感顺利的情侣增添幸福感,促进情感不顺的情侣及时做出自己的选择。

智能合约实现

角色合约role.sol:

// SPDX-License-Identifier: Apache-2.0
pragma solidity^0.8.7;

//用户结构体
struct Person {
    string name;   //姓名
    string gender; //性别 用于后续的同步信号量设置
}

//角色结构体
struct UserRole {
    mapping(address => bool) isExistent; //当前用户是否存在(已经注册)
    mapping(address => Person) user;     //用户
    mapping(address => bool) islocked;   //当前用户是否已经被锁定
    mapping(address => uint256) account; //每个人的独立账户 记录有多少金额
    address[] isSigned;                  //是否已经单方签名
    uint256 NoPerson;
}
contract Role {

    //角色是否存在
    function isRole(UserRole storage m_role,  address m_person) internal view returns (bool) {
        if(m_person == address(0)) {
            return false;
        }
        return m_role.isExistent[m_person];
    }

    //添加角色 用户前端发起注册后,后端触发合约执行,系统完成角色信息填充
    function addRole(UserRole storage m_role, address m_person, string memory m_name, string memory m_gender) internal returns(bool) {
        if(isRole(m_role, m_person)) {
            return false;
        }
        m_role.isExistent[m_person] = true;
        m_role.user[m_person].name = m_name;
        m_role.user[m_person].gender = m_gender;
        m_role.islocked[m_person] = false;
        m_role.account[m_person] = 0;
        m_role.NoPerson += 1;
        return true;
    }

    //删除角色 用户前端发起注销账户后,后端触发合约执行,系统完成角色信息清除
    function removeRole(UserRole storage m_role,  address m_person) internal returns (bool) {
        if(!isRole(m_role, m_person)) {
            return false;
        }
        //清空信息
        delete m_role.isExistent[m_person];
        delete m_role.user[m_person];
        delete m_role.islocked[m_person];
        delete m_role.account[m_person];
        m_role.NoPerson -= 1;
        return true;
    }

}

存证合约evidence.sol:

// SPDX-License-Identifier: Apache-2.0
pragma solidity^0.8.7;
import "./role.sol";

struct EternalLock {
        bytes32 hashLove; //爱情哈希值
        uint256 date;
        uint256 Date;
        uint256 m_sum; //男方设定金额
        uint256 f_sum; //女方设定金额
        address m_pk;
        address f_pk;
    }

contract EternalLockEvidence is Role{

    uint256 sumChain;   //链上总金额

    address[] blackList;    //黑名单
    address admin;      //管理员地址
    UserRole userRole;
    EternalLock[] eternalLock;
    mapping(address => mapping(address => EternalLock)) locked; //已锁定者列表 双方绑定一个标识

    event AddSignaturesEvidence(address _sender);

    constructor(address m_admin) {
        sumChain = 10000; //创始人自掏腰包
        admin = m_admin;
    }
    //判断是否在黑名单里
    function judgeIsInBlackList(address m_user) public view returns (bool) {
        for(uint256 i = 0; i < blackList.length; i++) {
            if(blackList[i] == m_user) {
                return true;
            }
        }
        return false;
    }

    //单方签名 
    function sign(address m_signer) external returns(bool) {
        require(isRole(userRole, m_signer), "the Signer is not existent!");
        require(m_signer != admin, "the Signer is not valid!");
        require(!judgeIsSigned(m_signer), "the user has already signed!");

        userRole.isSigned.push(m_signer);  //将公钥地址放入已签名列表
        emit AddSignaturesEvidence(m_signer);

        return true;
    }

    //判重
    function verify(address m_signer) internal view returns (bool) {
        for(uint256 i = 0; i < userRole.isSigned.length; i ++) {
            if (userRole.isSigned[i] == m_signer) {
                return true;
            }
        }       
        return false;
    }

    //山海珈锁
    function lockEternal(address _m_pk, address _f_pk, uint256 _m_sum, uint256 _f_sum, uint256 m_date, uint256 m_Date) public returns (bool) {
        //判断是否均签名
        if(judgeIsSigned(_m_pk) && judgeIsSigned(_f_pk) && !userRole.islocked[_m_pk] && !userRole.islocked[_f_pk]) {

        //用户是否初次使用(后续更新)

            bytes32 m_hash = keccak256(abi.encode(_m_pk,  _f_pk, _m_sum, _f_sum, m_date, m_Date));
            EternalLock memory el = EternalLock(m_hash, m_date, m_Date, _m_sum, _f_sum, _m_pk,  _f_pk);
            locked[_m_pk][_f_pk] = el;

            userRole.islocked[_m_pk] = true;
            userRole.islocked[_m_pk] = true;//互斥
            return true;

        } else {

            return false;
        }

    }

    //查询爱情哈希值
    function getEvidence(address _m_pk, address _f_pk) external view returns (bytes32 h) {

        return locked[_m_pk][_f_pk].hashLove;
    }

    //返回山海珈锁结构体
    function retEternalLock(address _m_pk, address _f_pk) public view returns (EternalLock memory h) {

        return locked[_m_pk][_f_pk];
    }

    //判断是否已经签名
    function judgeIsSigned(address m_person) public view returns (bool) {      
        for(uint256 i = 0; i < userRole.isSigned.length; i++) {
            if(userRole.isSigned[i] == m_person) {
                return true;
            }
        }

        return false;
    }

    //清除签名 相当于解锁 解开互斥
    function deleteLock(address m_person) public returns (bool) {
        for(uint256 i = 0; i < userRole.isSigned.length; i++) {
            if(userRole.isSigned[i] == m_person) {
                delete userRole.isSigned[i];
                return true;
            }
        }
        return false;
    }

    //修改date值
    function changeDataLock(address _m_pk, address _f_pk, uint256 n_date) public returns (bool) {
        locked[_m_pk][_f_pk].date = n_date;
        return true;
    }

    //换取景区纪念品
    function purchaseSouvenirs(uint256 price, address m_buyer) public returns (bool) {
        //余额不足
        require((userRole.account[m_buyer] >= price), "Insufficient Balance!");
        userRole.account[m_buyer] -= price; //低版本则需要SafeMath
        return true;
    }

    //用户获取奖励
    function getAwards(uint256 sum, address m_person) public returns (bool) {
        userRole.account[m_person] += sum;
        return true;
    } 

    //拉黑
    function putIntoBlackList(address m_person) public {
        blackList.push(m_person);
    }

    //注册用户
    function registerUser(string memory m_name, string memory m_gender, address m_person) public returns (bool) {
        return addRole(userRole, m_person, m_name, m_gender);
    }

    //注销用户
    function cancelUser(address m_person) public {
        removeRole(userRole, m_person);  
    }

    //查询用户
    function queryUser(address m_person) public view returns (string memory, string memory) {
        return (userRole.user[m_person].name, userRole.user[m_person].gender);
    }

    //查询已注册总人数
    function queryNoUser() public view returns (uint256) {
        return userRole.NoPerson;
    }
    //违约 ==> 选择1 ==> 支付金额 ==> 系统触发
    //违约 ==> 选择2 ==> 延期 ==> 系统触发
    //违约 ==> 选择2 ==> 拉黑 ==> 系统触发

    //提前异常开锁 / 超期 ==> 后端完成付费,合约中只计算金额
    function payAmount(address _m_pk, address _f_pk, uint256 time) public returns (bool, uint256) {
        require(judgeIsSigned(_m_pk) && judgeIsSigned(_f_pk), "not locked yet!");
        EternalLock memory e = retEternalLock(_m_pk, _f_pk);

        uint256 d = e.date;
        uint256 D = e.Date;
        uint256 key = (e.m_sum + e.f_sum) * (d - D) / (time - D);
        sumChain += key * 2; 
        //解锁
        deleteLock(_m_pk);
        deleteLock(_f_pk);
        return (true, key);
    }

    //延期
    function delay(address _m_pk, address _f_pk, uint256 new_date) public returns (bool, uint256) {
        require(judgeIsSigned(_m_pk) && judgeIsSigned(_f_pk), "not locked yet!");
        EternalLock memory e = retEternalLock(_m_pk, _f_pk);
        uint256 d = e.date;
        uint256 D = e.Date;
        uint256 key = (e.m_sum + e.f_sum) * (new_date - D) / (d - D) / 100 ; 
        sumChain += key * 2;
        changeDataLock(_m_pk, _f_pk, new_date); 
        return (true, key);
    }

    //后端认证结婚信息后,触发奖励合约
    function giveRewards(address _m_pk, address _f_pk, uint256 time) public returns (bool) {
        require(judgeIsSigned(_m_pk) && judgeIsSigned(_f_pk), "not locked yet!");
        EternalLock memory e = retEternalLock(_m_pk, _f_pk);
        uint256 d = e.date;
        uint256 D = e.Date;
        uint256 key = sumChain * (time - D) / (d - D) / 100; 
        sumChain -= (key * 2);
        getAwards(key, _m_pk);
        getAwards(key, _f_pk);
        return true;
    }

    //查询用户获取的奖励
    function queryUserAwards(address m_person) public view returns (uint256) {
        return userRole.account[m_person];
    } 

    //查看链上总金额
    function querySumChain() public view returns (uint256) {
        return sumChain;
    }
}

主合约love.sol:

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.7;
import "./evidence.sol";
contract ExternalLove {
    EternalLockEvidence eternalEvid;
    address admin;
    constructor() {     
        admin = msg.sender;
        eternalEvid = new EternalLockEvidence(admin);
    }

    //签名 ==> 用户触发
    function sign() public returns (bool) {
        eternalEvid.sign(msg.sender);
        return true;
    }

    //开启山海珈锁
    function lockEternal(address _m_pk, address _f_pk, uint256 _m_sum, uint256 _f_sum, uint256 m_date, uint256 m_Date) public returns (bool){
        return eternalEvid.lockEternal(_m_pk, _f_pk, _m_sum, _f_sum, m_date, m_Date);
    }

    //提前开锁,则需要支付金额,末参数动态处理
    function breakContract(uint8 choice, address _m_pk, address _f_pk, uint256 t) public {
        if(choice == 0) {
            eternalEvid.payAmount(_m_pk, _f_pk, t);
        }else if(choice == 1) {
            eternalEvid.delay(_m_pk, _f_pk, t);
        }
    }

    //拉黑
    function putIntoBlackList(address m_person) public {
        eternalEvid.putIntoBlackList(m_person);
    }

    //查询爱情哈希值
    function getHashLove(address _m_pk, address _f_pk) public view returns (bytes32) {
        bytes32 hash = eternalEvid.getEvidence( _m_pk, _f_pk);
        require(hash != bytes32(0), "invalid hash!");
        return hash;
    }

    //成功提供结婚信息,发放奖励,返回所有支付金额
    function giveRewards(address _m_pk, address _f_pk, uint256 time) public returns (bool) {
         require(msg.sender == admin, "must be adminer!");
         return eternalEvid.giveRewards(_m_pk, _f_pk, time);
    }

    //将奖励用于兑换景区纪念品
    function purchaseSouvenirs(uint256 price) public returns (bool) {
         return eternalEvid.purchaseSouvenirs(price, msg.sender);
    }

    //注册用户
    //规定合约参数 m_gender: male or female
    function registerNewUser(string memory m_name, string memory m_gender) public returns (bool) {
        return eternalEvid.registerUser(m_name, m_gender, msg.sender);
    }

    //注销用户
    function cancelUser(address m_person) public returns (bool) {
        eternalEvid.cancelUser(m_person);
        return true;
    }

    //查询用户
    function queryUser(address m_person) public view returns (string memory, string memory) {
        return eternalEvid.queryUser(m_person);       
    }

    //查询已注册总人数
    function queryNoUser() public view returns (uint256) {
        return eternalEvid.queryNoUser();
    }

    //查询用户获取的奖励
    function queryUserAwards() public view returns (uint256) {
        return eternalEvid.queryUserAwards(msg.sender);
    }

    //查看链上总金额
    function querySumChain() public view returns (uint256) {
        require(msg.sender == admin, "must be adminer");
        return eternalEvid.querySumChain();
    }

}

以上为Solidity智能合约的不完全实现,后续更新代码见cuiyuchain/ExternalLove: 永恒之恋•山海珈锁——基于区块链的同心锁数字化替代方案 (github.com)

参考文献

参考文献

小情侣在景区里挂的同心锁,最后都去哪里了?真相令人咋舌|爱情|挂锁_网易订阅 (163.com)

yekai1003/marriage-demo: 基于fisco bcos 区块链的结婚证书系统 (github.com)

评论