一个针对可出售智能合约的协议

原文作者:Pablo Ruiz

原文地址:https://medium.freecodecamp.org/a-protocol-for-sellable-smart-contracts-829bc2ce02b3

照片由 Jezael Melgoza 上传至 Unsplash

智能合约所有权的概念并没有被内置进以太坊里面。即便智能合约的创建和部署都由一个账户完成,智能合约的创建者(无论是外部持有账户(External Owned Account,EOA)还是另外一个合约)对自己部署的智能合约也没有一点特权。

智能合约的很多使用场景都要有个 "持有者" 的角色。而 “持有者” 应该被赋予对智能合约的一些特权和责任。

在一个众筹合约(crowdsale contract)里,持有者可能要负责管理整个过程,并在出现问题时中止这个众筹。

在一些像 Lottery 和 Ruffle Dapp 这样的抽奖里,持有者可能要负责执行抽签的过程。

在任何需要持有资金的合约里,可能需要将持有人设置为在合约销毁时的受益人。

照片由 Ricardo Resende 上传至 Unsplash

将持有者设定为部署合约的那个账号是很多智能合约都遵循的一个惯例。

就像下面这样:

pragma solidity 0.4.19;

contract MyContract {
    address owner;

    function MyContract(){
        owner = msg.sender;
    }
}

然后加个修饰符:

modifier onlyOwner {
    require(msg.sender == owner);
    _;    
}

最后利用这个修饰符确保只有合约持有者才能进行一些关键的操作。

// 自毁合约并且把资金都转给持有者
// 显然,这个操作只能提供给持有者

function destroyContract() public onlyOwner {
    selfdestruct(owner);
}

合同所有权变更的问题

有些情况需要将合同的所有权交给其他人。这里举几个例子:

  • 合约持有者只是代表别人部署了合约(比如开发人员或顾问为某个公司做承包工作)。
  • 一个公司想清算或者变卖它的资产,而资产包括了持有或者没持有以太币的智能合约。
  • 智能合约的持有者想拿它送人,把它捐掉,或者卖掉换钱。
照片由 rawpixel.com 上传至 Unspixel

有些合约(不过恐怕不多)会内置一个用来把合约所有权交给其他账户的函数。其中一部分还会内置另外一个能让被交予所有权的用户接受这一交接的函数。

function changeOwner(address _newOwner) public onlyOwner {
    ownerCandidate = _newOwner;
}


function acceptOwnership() public {
    require(msg.sender == ownerCandidate);  
    owner = ownerCandidate;
}

目前,changeOwner() 还有 acceptOwnership() 这两个函数还有一些没能解决的常见的问题,使得它们没能被广泛使用。这些问题在上面提到的所有权交接的情形里都会有所涉及:

  • 合约买方怎么知道卖方会在买方付款之后真的会执行相应的 changeOwner() 函数?
  • 换个角度也会有这种情况。如果采用货到付款的方式,卖方怎么知道买方会在卖方先交付所有权之后会真的付款?
  • 买方怎么知道卖方在交接所有权之前不会篡改合约(毕竟它只是数据)?

针对可出售合约的协议

我所提出的解决方案是实现一系列功能,使智能合约的持有者可以通过与特定的某个人交换以太币的形式,或者以宣布出售合约并基于先到先得的规则来让别人购买的形式,来出售一个智能合约。这一协议也可以加以扩展,使得使用其他的销售方法,并在不同的销售方法里进行不同形式的拍卖成为可能。

这一合约的细节可以在相应的EIP(Ethereum Improvement Proposals,以太坊改进提案)里面查阅并且讨论。

接下来我会介绍一个使用这一协议的范例。这一范例的源码也可以在我的 Github 项目上查阅。

处理所有权

处理合约的所有权是非常基础的部分。正如惯例所做的那样,我们会在合约初始化时设置该条约的持有者为msg.sender

function Sellable() public {
    owner = msg.sender;
    Transfer(now,address(0),owner,0);
}

然后我们再加上修饰符 onlyOwner。这一修饰符会用在每一个我们想只让合约持有者能执行的函数:

modifier onlyOwner {
    require(msg.sender == owner);
    _;    
}

我们会希望我们的合约能在特定的条件下允许 owner 对其进行修改。

将合约出售

合同的持有者可以通过调用以下函数来将它出售:

function initiateSale(uint _price, address _to) onlyOwner public {
    require(_to != address(this) && _to != owner);
    require(!selling);
        
    selling = true;
        
    // 如果确定了买方就在这里将买方记录下来
    sellingTo = _to;
        
    askingPrice = _price;
}

initiateSale() 函数有两个参数:

  • uint _price:这是合约持有者对所出售的合约的定价。
  • address_to:这是可选的参数,对应于合约买方。

在出售合约时,持有者有两种选择:在交易已经事先定好的情况下持有者可以指定买家;或者他们也可以只 “宣布” 出售合约,并且让第一个宣称想要(而且按价格支付了)的人得到它。

另外,定价可以设置为 0。这意味着合同的持有者可以赠送,捐赠合约或只是把合约弄走。

这里要注意一个重点:有个修饰符ifNotLocked可以添加到合同的函数里面,以防止合同在被出售的过程中被执行。如果使用得当,这可以防止合约数据在出售完成之前被篡改。

最后,这里还有一个函数cancelPurchase()要介绍。它能让合约持有者在有人买到合约之前取消合约的销售。

function cancelSale() onlyOwner public {
    require(selling);
        
    // 重置与出售有关的变量
    resetSale();
}

购买合约

当合约开始出售时,卖方完成出售所需的全部工作就是为买方(如果指定了的话)或任何人(如果没有指定买方)调用以下函数:

function completeSale() public payable {
    require(selling);
    require(msg.sender != owner);
    require(msg.sender == sellingTo || sellingTo == address(0));
    require(msg.value == askingPrice);
        
    // 交换所有权
    address prevOwner = owner;
    address newOwner = msg.sender;
    uint salePrice = askingPrice;
        
    owner = newOwner;
        
    // 重置和出售合约有关的变量
    resetSale();
        
    prevOwner.transfer(salePrice);
        
    Transfer(now,prevOwner,newOwner,salePrice);
}

这里completeSale()函数带有修饰符payable,这意味着该函数是一个需要发送以太币才能执行的函数。而发送的金额必须与持有者设置的定价完全相同。

当 completeSale() 被执行时,一部分以太币会从买家转移给卖家,然后所有权会从卖家转移给买家。然后在完成这一流程后给新的持有者重置合约的交易状态。这样新的持有者就能正常使用或者出售这一合约了。

一个示例

下面演示一个出售简单合约的例子:

contract Kitty is Sellable {
    
    string public name;
    uint public kittyValue = 0;
    
    function Kitty(string _name) public {
        name = _name;
    }
    
    function findNewOwner() public onlyOwner {
        kittyValue = kittyValue + 1 ether;   
        super.initiateSale(kittyValue,address(0));
    }
    
    function renameKitty(string newName) ifNotLocked public onlyOwner {
        name = newName;
    }
    
    function buyKitty() public payable {
        require(msg.value == kittyValue);
        super.completeSale();
    }
}

现在我们有一个用来代表 CryptoKitty😺(链养猫)的合约。持有者可以通过findNewOwner()把它卖掉。每当小猫被出售时,他的价值都会增加 1 个以太币。小猫的主人可以通过函数renameKitty()改变它的名字。不过当小猫正在出售的时候,由于renameKitty()函数加了ifNotLocked修饰符,它的名字就不能改了。

就是这样!

如果您有进一步改进 Sellable 协议的建议,请在我创建的EIP中发表您的意见,错误或建议

照片由 Jonas Vincent 上传至 Unsplash

本文的版权归 Tnecesoc 所有,如需转载请联系作者。

发表于

我来说两句

9 条评论
登录 后参与评论

相关文章

来自专栏量子位

史上最严重数据车祸:100+车厂机密全曝光,通用丰田特斯拉统统中招

100多家车厂,从通用汽车、菲亚特克莱斯勒、福特、丰田,大众到特斯拉,现在机密数据统统被供应商的共同服务器曝光。

580
来自专栏镁客网

想要“挖矿”致富?小心这些方式让你被挖矿,让别人致富!

1082
来自专栏区块链中本聪

区块链技术产生的比特币如何找回

主链侧链开发数字货币交易所白皮书区块链浏览器跨境支付场内场外宠物挖矿游戏基金会牌照 181-4069-6008 微信电话同号

1013
来自专栏智能算法

当酒店开始用机器人送水,服务生的饭碗怎么办?

“电梯先生,请开门,帮我按到20楼层。” 上面这句话来自一台机器人,我还可以负责任的说真实的声音听起来萌萌哒。位于北京三元桥的维景国际大酒店引入了一款机器人服务...

3549
来自专栏FreeBuf

破解iPhone竟然是为了公众安全?

一直以来,iOS系统以安全著称,引来各路高手正向破解挑战。前段时间,以色列的一家公司高调宣布能够解锁任意iPhone设备,包括最新的iPhone X。近期该公司...

3528
来自专栏腾讯研究院的专栏

全球隐私及数据保护法律政策动态报告(上)

全球隐私及数据保护法律政策动态报告(上) 腾讯互联网法律研究中心  2014年第3季度 一、重点摘要 欧盟在cookies治理方面的努力已经进...

1709
来自专栏科技向令说

是旁敲还是侧击,百度卫士7.0要吞下办公室?

2天前,百度卫士推出7.0版本。该版本在优化各项安全功能基础上,还首次推出了百度自研的“智能系统”,此系统号称不仅可以智能感应用户电脑问题后一键解决,还可以感应...

604
来自专栏罗超频道

“技术垄断”猛于虎

近日新浪微博上又有一个开发者遭殃了,有“工作版微信”之称的脉脉面临二选一:要么向微博写回用户profile和职业关系数据,要么在本周六被停止微博授权接口。周五脉...

2688
来自专栏科技向令说

挑战巨头,主打安全的Telegram、超信胜算几何?

如今的移动即时通讯市场有点“蹊跷”,一边是朋友圈里“微信又出故障了”,“微信要上直播了”“烦死了,每天被微信群里的垃圾信息轰炸”等诸如此类的控诉和抱怨,另一边是...

391
来自专栏区块链领域

Fomo3D 千万大奖获得者“特殊攻击技巧”最全揭露!

Fomo3D 游戏第一轮正式结束,最终大奖由地址 0xa169 获得,奖金额高达 10,469.66 以太币。

61

扫描关注云+社区