V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
omnigeeker
V2EX  ›  程序员

奇了,结婚也能写成区块链智能合约

  •  
  •   omnigeeker ·
    omnigeeker · 2018-12-13 17:18:38 +08:00 · 3335 次点击
    这是一个创建于 2174 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在下面的文章中,我将深入介绍“智能婚礼合约”的技术实施细节。 请记住,这是一个原型,因此产品功能可能并不齐全。

    如果您对整体想法和概念感兴趣,可以在这里阅读更多相关信息。

    介绍

    该项目基本上是使用 Truffle Framework ( Truffle,Ganache )、Web3 和 MetaMask 完成。这是与以太坊进行通信的简单 Web 前端构建的。

    实现

    一般的想法是建立一个婚姻合同的数字版本,参考过去的西方书面纸质结婚合同(中国似乎不流行),包含所有必要的功能,如管理资金和资产。 然而,我们希望这款产品不仅仅优先处理数字等价物,合同同样也能处理资金,也能动态管理资产(这听起来很花哨,但它很直接)。

    合约部署

    首先要做的是将新婚夫妻的钱包地址作为构造函数参数添加到合同中。 因为这些地址无法更改,所以这是最好的方法。

    const SmartWeddingContract = artifacts.require("./contracts/SmartWeddingContract.sol");
    const husbandAddress = require("../config.js").husbandAddress;
    const wifeAddress = require("../config.js").wifeAddress;
    module.exports = function(deployer) {
        deployer.deploy(SmartWeddingContract, husbandAddress, wifeAddress);
    };
    

    上传可写合约

    您可能会问,既然我们的最终目标是创建一个优秀的数字合同版本,为什么我们还要上传一份书面合同。

    原因是我们希望它能在法律背景下工作。 而现在,如果与一份简单的旧书面法律文件没有任何联系,这在奥地利是不行的。

    因此,书面合同是手工签署的,并保存为 PDF 上传到 IPFS (去中心化存储系统)。 该文件可以在这里找到:

    [解读] :IPFS 除非自己部署 IPFS 节点, 否则不能保证数据不丢失。因为 IPFS 是不带有激励性质的去中心化存储系统。如果要保证文件不丢失,可以采用 PP.IO( https://pp.io ),这是保证高可靠性的去中心化存储系统,兼容 AWS S3 的接口,并且能做到 12 个 9 地(99.9999999999%)保证文件不会丢失(具体原理可见《 How decentralized storage is able to ensure that data is not lost 》)。

    在以太坊上存储数据需要花费很多。 基于他们的黄纸存储 256 字节需要 20k 的 Gas。 我们假设 PDF 有 100 kB:

    100kB/256 bit = ~390 (256-bit words) 390 (256-bit words) * 20k (Gas) * 10 Gwei (gas price) = 0.078 ETH

    想象一下,一个小文件存储就要支付 10 美元以上。 如果你想上传一个更大的文件,这可能很容易花费数百甚至数千美元。 我们只需要将文件的哈西上链(存储在区块链上),而不是将整个文件存储在以太坊上。

    签名合约

    成功部署合同并上传 IPFS 哈希后,智能婚礼合约处于未签名状态。

    modifier isSigned() {
      require(signed == true, "Contract has not been signed by both spouses yet!");
      _;
    }
    

    夫妻双方必须首先使用私钥对其进行签名,以便调用更多功能。 这是由于 isSigned 修饰符在调用之前检查合同的状态。

    function signContract() external onlySpouse {
        require(isSameString(writtenContractIpfsHash, ""), "Written contract ipfs hash has been proposed yet!");
        require(hasSigned[msg.sender] == false, "Spouse has already signed the contract!");
        
        // Sender 签名
        hasSigned[msg.sender] = true;
    
        emit Signed(now, msg.sender);
    
        // 检查夫妻双方是否都已经签名
        if (hasSigned[husbandAddress] && hasSigned[wifeAddress]) {
            signed = true;
            emit ContractSigned(now);
        }
    }
    

    signContract 函数只能由夫妻调用。 “神奇”部分是最后的 multisig。if-clause 检查丈夫和妻子是否签署了合同。 如果是,则全局签名状态更改为 true。

    资产

    资产,例如:汽车,房子、股票甚至是婚姻合约管理的主要内容,应声明以下问题:

    • 谁拥有什么?
    • 离婚后会发生什么?

    与普通旧书面合同相反,基本上每一项变更都必须由公证人确认,智能合约可实现动态资产管理。

    增加资产

    为了增加资产,配偶一方需要提出资产,另一方需要批准资产。使用此功能可以创建新资产,并且建议添加资产的配偶立即批准该资产。

    使用 CryptoJS 通过 AES 对资产数据进行客户端加密 /解密。因此,只有密码文本存储在区块链中以确保隐私。

    function proposeAsset(string _data, uint _husbandAllocation, uint _wifeAllocation) external onlySpouse isSigned isNotDivorced {
        require(isSameString(_data, ""), "No asset data provided!");
        require(_husbandAllocation >= 0, "Husband allocation invalid!");
        require(_wifeAllocation >= 0, "Wife allocation invalid!");
        require((_husbandAllocation + _wifeAllocation) == 100, "Total allocation must be equal to 100%!");
    
        // 添加新资产
        Asset memory newAsset = Asset({
            data: _data,
            husbandAllocation: _husbandAllocation,
            wifeAllocation: _wifeAllocation,
            added: false,
            removed: false
        });
        uint newAssetId = assets.push(newAsset);
    
        emit AssetProposed(now, _data, msg.sender);
    
        // 映射到存储对象(否则无法访问映射)
        Asset storage asset = assets[newAssetId - 1];
    
        // Sender 立刻批准
        asset.hasApprovedAdd[msg.sender] = true;
    
        emit AssetAddApproved(now, _data, msg.sender);
    }
    

    批准一个资产

    在签订合约时,资产以相同的方式批准(使用 multisig )。只有配偶双方同意才能添加资产。

    function approveAsset(uint _assetId) external onlySpouse isSigned isNotDivorced {
        require(_assetId > 0 && _assetId <= assets.length, "Invalid asset id!");
    
        Asset storage asset = assets[_assetId - 1];
    
        require(asset.added == false, "Asset has already been added!");
        require(asset.removed == false, "Asset has already been removed!");
        require(asset.hasApprovedAdd[msg.sender] == false, "Asset has already approved by sender!");
    
        // Sender 批准
        asset.hasApprovedAdd[msg.sender] = true;
    
        emit AssetAddApproved(now, asset.data, msg.sender);
    
        // 检查夫妻双方是否都已批准
        if (asset.hasApprovedAdd[husbandAddress] && asset.hasApprovedAdd[wifeAddress]) {
            asset.added = true;
            emit AssetAdded(now, asset.data);
        }
    }
    

    删除一个资产

    同样,双方必须都同意才能删除资产。

    function removeAsset(uint _assetId) external onlySpouse isSigned isNotDivorced {
        require(_assetId > 0 && _assetId <= assets.length, "Invalid asset id!");
    
        Asset storage asset = assets[_assetId - 1];
    
        require(asset.added, "Asset has not been added yet!");
        require(asset.removed == false, "Asset has already been removed!");
        require(asset.hasApprovedRemove[msg.sender] == false, "Removing the asset has already been approved by the sender!");
    
        // 批准 Sender 删除
        asset.hasApprovedRemove[msg.sender] = true;
    
        emit AssetRemoveApproved(now, asset.data, msg.sender);
    
        // 检查夫妻双方是否都批准移除资产
        if (asset.hasApprovedRemove[husbandAddress] && asset.hasApprovedRemove[wifeAddress]) {
            asset.removed = true;
            emit AssetRemoved(now, asset.data);
        }
    }
    

    合约作为储蓄账户

    智能婚礼合约也可以用作储蓄账户。 它可以充当普通的以太坊钱包,这意味着它可以接收 ETH。并且通过支付功能,妻子和丈夫都可以通过这个钱包地址发送到另一个地址(例如,支付食物或假期,购买其他资产......)来花费这些 ETH。

    function() external payable isSigned isNotDivorced {
        emit FundsReceived(now, msg.sender, msg.value);
    }
    

    这是默认功能。 重要的关键词是可支付。 它使智能合约能够获得资金( ETH )。

    function pay(address _to, uint _amount) external onlySpouse isSigned isNotDivorced {
        require(_to != address(0), "Sending funds to address zero is prohibited!");
        require(_amount <= address(this).balance, "Not enough balance available!");
    
        // 将资金发送到目的地地址
        _to.transfer(_amount);
    
        emit FundsSent(now, _to, _amount);
    }
    

    付费功能仅获取目的地地址和要发送的金额。

    离婚

    也许最重要的功能是离婚后会发生的事情,这也是婚姻合同存在的原因之一。

    离婚功能只能由配偶调用。和以前一样,必须双发都同意离婚。一旦双方同意,合同状态就会改变。 剩余的资金( ETH )被分成两半并发送给每一方。

    function divorce() external onlySpouse isSigned isNotDivorced {
        require(hasDivorced[msg.sender] == false, "Sender has already approved to divorce!");
    
        // Sender 批准
        hasDivorced[msg.sender] = true;
    
        emit DivorceApproved(now, msg.sender);
    
        // 检查夫妻双方是否都同意离婚
        if (hasDivorced[husbandAddress] && hasDivorced[wifeAddress]) {
            divorced = true;
            emit Divorced(now);
    
            // 获取合约的余额
            uint balance = address(this).balance;
    
            // 将余额分成两半
            if (balance != 0) {
                uint balancePerSpouse = balance / 2;
    
                // 转账给原丈夫方
                husbandAddress.transfer(balancePerSpouse);
                emit FundsSent(now, husbandAddress, balancePerSpouse);
    
                // 转账给原妻子方
                wifeAddress.transfer(balancePerSpouse);
                emit FundsSent(now, wifeAddress, balancePerSpouse);
            }
        }
    }
    

    在将合同状态更改为离婚后,任何人都无法再与合同互动。 这是由于 isNotDivorced 修饰符在起作用。它被添加到所有关键功能中,以确保以后不能更改任何内容。

    modifier isNotDivorced() {
        require(divorced == false, "Can not be called after spouses agreed to divorce!");
        _;
    }
    

    事件

    DApp 使用了许多事件。几乎在每种情况下都会发出它们,以便为前端应用程序创建正确的日志。 在这里插入图片描述 每个事件还有一个时间戳变量。它仅显示一个大致的时间。事件在 web 前端执行的时间。Web3 也支持这一点,所以没有必要记录每个事件的时间戳,但是:

    Web3 需要为每个事件异步查询远程以太坊节点(这看起来相当愚蠢) 但它是免费的(在 Solidity 事件中存储数据不需要任何费用)

    event WrittenContractProposed(uint timestamp, string ipfsHash, address wallet);
    event Signed(uint timestamp, address wallet);
    event ContractSigned(uint timestamp);
    event AssetProposed(uint timestamp, string asset, address wallet);
    event AssetAddApproved(uint timestamp, string asset, address wallet);
    event AssetAdded(uint timestamp, string asset);
    event AssetRemoveApproved(uint timestamp, string asset, address wallet);
    event AssetRemoved(uint timestamp, string asset);
    event DivorceApproved(uint timestamp, address wallet);
    event Divorced(uint timestamp);
    event FundsSent(uint timestamp, address wallet, uint amount);
    event FundsReceived(uint timestamp, address wallet, uint amount);
    

    智能婚礼合同部署在以太坊 Ropsten 测试网和以太坊主网上。

    GitHub

    完整的源代码可在以下位置获得: https://github.com/block42-blockchain-company/smart-wedding-contract

    文章作者:Wayne Wong 转载请注明出处 如果有关于 PPIO 的交流,可以通过下面的方式联系我: 加我微信,注意备注来源 wechat:omnigeeker

    8 条回复    2018-12-14 13:51:29 +08:00
    buhi
        1
    buhi  
       2018-12-13 17:20:58 +08:00
    政治不正确, 婚姻也不一定都是一个 wife 和一个 husband :P
    kuangwinnie
        3
    kuangwinnie  
       2018-12-13 17:37:37 +08:00   ❤️ 2
    **和以前一样,必须双发都同意离婚。**
    世界上不存在不能单方面毁约的合约
    mind3x
        4
    mind3x  
       2018-12-13 18:27:10 +08:00   ❤️ 1
    恶心,我专门去看了 medium 上的原文,根本没有任何提到 IPFS 的可靠性或者建议用 pp.io 的文字:

    > So the written contract is signed by hand (the old way) and uploaded as a PDF to IPFS (a decentralized storage system). The file can be found: here

    发贴者用这种方式推自己的私货,欺骗性非常大。

    @Livid 这种恶性的推广非常令人厌恶。
    luopengfei14
        5
    luopengfei14  
       2018-12-13 18:43:10 +08:00 via iPhone
    传销币吧?😖
    Leigg
        6
    Leigg  
       2018-12-14 07:59:12 +08:00 via iPhone
    没考虑小三?
    xuhp
        7
    xuhp  
       2018-12-14 10:46:26 +08:00
    no1xsyzy
        8
    no1xsyzy  
       2018-12-14 13:51:29 +08:00
    https://www.v2ex.com/about
    “这里绝对不会全文转载任何文章,而只会以链接方式分享”
    https://www.v2ex.com/t/10553
    “• Deactivate 会对做出什么样行为的用户使用?
    ……
    - 全文转载 ”
    全文翻译应该也算转载吧。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1853 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 16:27 · PVG 00:27 · LAX 08:27 · JFK 11:27
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.