在下面的文章中,我将深入介绍“智能婚礼合约”的技术实施细节。 请记住,这是一个原型,因此产品功能可能并不齐全。
如果您对整体想法和概念感兴趣,可以在这里阅读更多相关信息。
介绍
该项目基本上是使用Truffle Framework(Truffle,Ganache)、Web3和MetaMask完成。这是与以太坊进行通信的简单Web前端构建的。
Ropsten Testnet上的前端DApp
实现
一般的想法是建立一个婚姻合同的数字版本,参考过去的西方书面纸质结婚合同(中国似乎不流行),包含所有必要的功能,如管理资金和资产。 然而,我们希望这款产品不仅仅优先处理数字等价物,合同同样也能处理资金,也能动态管理资产(这听起来很花哨,但它很直接)。
image
现在让我们来看看一些代码。
合约部署
首先要做的是将新婚夫妻的钱包地址作为构造函数参数添加到合同中。 因为这些地址无法更改,所以这是最好的方法。
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。
资产
资产,例如:汽车,房子、股票甚至是婚姻合约管理的主要内容,应声明以下问题:
- 谁拥有什么?
- 离婚后会发生什么?
与普通旧书面合同相反,基本上每一项变更都必须由公证人确认,智能合约可实现动态资产管理。
image
增加资产
为了增加资产,配偶一方需要提出资产,另一方需要批准资产。使用此功能可以创建新资产,并且建议添加资产的配偶立即批准该资产。
使用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使用了许多事件。几乎在每种情况下都会发出它们,以便为前端应用程序创建正确的日志。
image
每个事件还有一个时间戳变量。它仅显示一个大致的时间。事件在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
网友评论