Time-locked Wallets:一个以太坊智能合约的教程
前言
本次推荐的是一篇关于通过以太坊了解区块链的教程,能力有限,本身没接触过,各位尽量看原文吧。原文:Time-lockedWallets: An Introduction to Ethereum Smart Contracts 作者:RADEK OSTROWSKI
正文
区块链及其应用如今从未像现在这样流行。特别是以太坊,提供智能合约功能,为可以以分布式,不可变和无可信赖的方式实施的新想法敞开大门。 由于学习曲线非常陡峭,因此在Ethereum智能合约部分开始可能会有点压倒性。我们希望这篇文章(以及以太坊系列未来的文章)能够缓解这种痛苦,并让您快速启动并运行。Truffle, Solidity, 和ĐApps
在本文中,我们假设您对区块链应用和以太坊有一些基本的了解。如果你觉得你需要补充你的知识,我们推荐从Truffle框架中这个以太坊概述。 这篇文章涵盖了什么内容:- 时间锁定钱包(Time-locked Wallets)的应用
- 开发环境设置
- 使用松露框架进行智能合约开发
- 固体合约的说明
- 如何编译,迁移和测试智能合约
- 使用ÐApp与浏览器的智能合约交互
- 使用MetaMask进行浏览器设置
- 主要用例的贯穿
时间锁定钱包:用例
以太坊智能合约有许多不同的应用。目前最受欢迎的是加密货币(实现为ERC20令牌)和众筹令牌销售(也称为初始硬币产品或ICO)。实用ERC20令牌的一个很好的例子是Motoro Coin。在这篇博客文章中,我们将探讨一些不同之处:将资金锁定在加密钱包合同中的想法。这个想法本身有各种用例。归属于ICO
有几个例子,但目前锁定资金的最常见原因可能是“归属”。想象一下,您刚刚提出了一个成功的ICO,并且您的公司仍然拥有分配给您的团队成员的大部分代币。 确保员工持有的代币不能直接交易是有益的。如果没有控制措施,任何特定的员工都可以通过出售所有代币,兑现和退出公司来采取行动。这将对市场价格产生负面影响,并使该项目的所有剩余贡献者不满。基于密码的“最后遗嘱和遗嘱”
另一个想法是使用智能合约作为加密意志。想象一下,我们希望将我们的加密货币储蓄存储在家庭成员可以访问的合同中,但只能在我们发生某些事情后才能使用。假设我们应该通过钱包进行“登记”,通过每隔一段时间唤起一些合同电话。 如果我们没有按时办理登机手续,我们可能会发生一些事情,他们可以撤回资金。他们各自收到的资金比例可以在合同中明确规定,也可以由家庭成员协商一致决定。养老金或信托基金
锁定资金的另一个应用可能是创建一个小型养老基金或基于时间的储蓄账户,即防止业主在未来一段时间之前提取任何资金。(这对于上瘾的加密交易者可以特别有用,以帮助保持其乙醚的完整性。) 我们将在本篇博文的其余部分探讨的用例是类似的:将一些加密资金留给其他人使用,比如将来的生日礼物。 让我们想象一下,在他们18岁生日的时候,我们想给某人献上一个以色列人。我们可以在一张纸上写下账户的私钥和持有这笔资金的钱包的地址,并将其交给他们。他们唯一需要做的就是在18岁时从他们的账户上调用合同的功能,并将所有资金转移给他们。或者,我们可以使用简单的应用程序。听起来不错?让我们开始吧!以太坊开发设置
在开展智能合约开发之前,您需要在您的计算机上安装Node.js和Git。在这个博客中,我们将使用松露框架。即使你没有它,松露也能显着减少进入以太坊智能合约开发,测试和部署的门槛。我们完全同意他们的发言:Truffle是以太坊最受欢迎的开发框架,其使命是让您的生活变得更加轻松。要安装它,请运行以下命令:
npm install -g truffle现在,得到这个项目的代码:
git clone https://github.com/radek1st/time-locked-wallets cd time-locked-wallets重要的是要注意,该项目遵循标准松露项目结构,并且感兴趣的目录是:
contracts
:持有所有Solidity合约migrations
:包含描述迁移步骤的脚本src
:包含ÐApp代码test
:存储所有合同测试
包含的智能合同概述
这个项目包含了几个合同。这里是简历:TimeLockedWallet.sol
是这个项目的主要合同,并在下面详细描述。TimeLockedWalletFactory.sol
是factory
任何人都可以轻松部署自己的合同TimeLockedWallet
。ERC20.sol
是以太坊令牌的ERC20标准接口。ToptalToken.sol
是一个定制的ERC20令牌。SafeMath.sol
是ToptalToken用于执行安全算术运算的小型库。Migrations.sol
是一个促进迁移的内部松露合同。
TimeLockedWallet.sol
我们的TimeLockedWallet.sol
Solidity合约看起来像这样:
pragma solidity ^0.4.18;上面的行表示此合同所需的Solidity编译器的最低版本。
import "./ERC20.sol";在这里,我们导入其他合约定义,稍后在代码中使用。
contract TimeLockedWallet { ... }以上是我们的主要目的。
contract
范围是我们合同的代码。下面介绍的代码来自大括号内。
address public creator; address public owner; uint public unlockDate; uint public createdAt;这里我们定义了几个
public
默认生成相应getter方法的变量。它们中的一些是类型uint
(无符号整数),一对是address
(16个字符长的以太坊地址)。
modifier onlyOwner { require(msg.sender == owner); _; }简而言之,
modifier
在开始执行它所连接的函数之前,必须满足这个先决条件。
function TimeLockedWallet( address _creator, address _owner, uint _unlockDate ) public { creator = _creator; owner = _owner; unlockDate = _unlockDate; createdAt = now; }这是我们的第一个功能。由于名称与我们的合同名称完全相同,因此它是构造函数,创建合同时仅调用一次。 请注意,如果您要更改合同名称,这将成为任何人都可以调用的正常功能,并在合同中形成后门,就像Parity Multisig Wallet缺陷中的情况一样。另外,请注意,案例也很重要,所以如果这个函数的名字是小写的,它也会成为一个常规函数,而不是你想要的东西。
function() payable public { Received(msg.sender, msg.value); }上述功能是一种特殊类型,称为
fallback
功能。如果有人向本合同发送ETH,我们会很乐意收到。合同的ETH余额将会增加,并且会触发Received
事件。要使任何其他功能接受传入的ETH,您可以使用payable
关键字标记它们。
function info() public view returns(address, address, uint, uint, uint) { return (creator, owner, unlockDate, createdAt, this.balance); }这是我们的第一个常规功能。它没有函数参数并定义要返回的输出元组。请注意,
this.balance
返回此合约的当前以太平衡。
function withdraw() onlyOwner public { require(now >= unlockDate); msg.sender.transfer(this.balance); Withdrew(msg.sender, this.balance); }只有在
onlyOwner
先前定义的修饰符满足的情况下才能执行上述功能。如果该require
陈述不正确,则合同退出并出现错误。这就是我们检查是否unlockDate
已经过去的地方。msg.sender
是这个函数的调用者,它被转移到合约的整个乙醚余额。在最后一行,我们也发起了一个Withdrew
事件。事件稍后介绍。
有趣的是,now
这相当于 - block.timestamp
可能并不像人们想象的那么准确。矿工应该选择它,因此可能会长达15分钟(900秒),正如下面的公式所解释的那样:
因此,parent.timestamp >= block.timestamp <= now + 900 seconds
now
不应该用于测量小时间单位。
function withdrawTokens(address _tokenContract) onlyOwner public { require(now >= unlockDate); ERC20 token = ERC20(_tokenContract); uint tokenBalance = token.balanceOf(this); token.transfer(owner, tokenBalance); WithdrewTokens(_tokenContract, msg.sender, tokenBalance); }这是我们提取ERC20令牌的功能。由于合同本身并不知道分配给此地址的任何令牌,因此我们必须传递我们想要撤回的已部署ERC20令牌的地址。我们通过实例化它,
ERC20(_tokenContract)
然后查找并将整个令牌余额转移给收件人。我们还发起了一个WithdrewTokens
活动。
event Received(address _from, uint _amount); event Withdrew(address _to, uint _amount); event WithdrewTokens(address _tokenContract, address _to, uint _amount);在这个片段中,我们定义了几个事件。触发事件基本上是附加在区块链交易收据上的日志条目。每个事务可以附加零个或多个日志条目。事件的主要用途是调试和监视。 这就是我们需要的时间锁定ether和ERC20令牌 - 只需几行代码。不错,是吧?现在让我们来看看我们的其他合同
TimeLockedWalletFactory.sol
。
TimeLockedWalletFactory.sol
创建更高级别的工厂合同背后有两个主要原因。第一个是安全问题。通过分离不同钱包中的资金,我们不会只有一个拥有大量乙醚和代币的合同。这将给钱包所有者100%的控制权,并希望阻止黑客尝试利用它。 其次,工厂合同允许轻松,轻松地创建TimeLockedWallet合同,而无需提供任何开发设置。所有你需要做的就是从另一个钱包或ĐApp调用一个函数。pragma solidity ^0.4.18; import "./TimeLockedWallet.sol"; contract TimeLockedWalletFactory { ... }以上内容与前一份合同相似,非常相似。
mapping(address => address[]) wallets;在这里,我们定义一个
mapping
类型,它类似于字典或地图,但预设了所有可能的键并指向默认值。在address
类型的情况下,默认值是零地址0x00
。我们也有一个数组类型address[]
,它持有address
es。
在Solidity语言中,数组总是包含一个类型,并且可以具有固定或可变的长度。在我们的例子中,数组是无界的。
为了总结我们的业务逻辑,我们定义了一个映射wallets
,它由用户地址(契约创建者和所有者)组成,每个映射指向一组关联的钱包契约地址。
function getWallets(address _user) public view returns(address[]) { return wallets[_user]; }在这里,我们使用上述函数返回
_user
创建或拥有的所有合约钱包。请注意view
(在较旧版本的编译器版本中constant
)表示这是一个不会更改区块链状态的函数,因此可以在不花费任何气体的情况下免费调用它。
function newTimeLockedWallet(address _owner, uint _unlockDate) payable public returns(address wallet) { wallet = new TimeLockedWallet(msg.sender, _owner, _unlockDate); wallets[msg.sender].push(wallet); if(msg.sender != _owner){ wallets[_owner].push(wallet); } wallet.transfer(msg.value); Created(wallet, msg.sender, _owner, now, _unlockDate, msg.value); }这是合同中最重要的部分:工厂方法。它让我们通过调用它的构造函数,即时创建一个新的时间锁定钱包
new TimeLockedWallet(msg.sender, _owner, _unlockDate)
。然后,我们为创作者和收件人存储它的地址。之后,我们将在此函数执行过程中传递的所有可选的乙醚转移到新创建的钱包地址。最后,我们将该Create
事件表示为:
event Created(address wallet, address from, address to, uint createdAt, uint unlockDate, uint amount);
ToptalToken.sol
如果我们不创建自己的以太坊令牌,那么本教程并不是那么有趣,所以为了完整起见,我们正在实现ToptalToken
。ToptalToken
是一个标准的ERC20令牌,实现了下面介绍的接口:
contract ERC20 { uint256 public totalSupply; function balanceOf(address who) public view returns (uint256); function transfer(address to, uint256 value) public returns (bool); function allowance(address owner, address spender) public view returns (uint256); function transferFrom(address from, address to, uint256 value) public returns (bool); function approve(address spender, uint256 value) public returns (bool); event Approval(address indexed owner, address indexed spender, uint256 value); event Transfer(address indexed from, address indexed to, uint256 value); }下面定义了与其他令牌的区别:
string public constant name = "Toptal Token"; string public constant symbol = "TTT"; uint256 public constant decimals = 6; totalSupply = 1000000 * (10 ** decimals);我们给它一个名字,一个符号,一百万的总供给量,并且可以将它整除六位小数。 要发现令牌合约的不同变体,请随时探索OpenZeppelin回购。
松露控制台:编译,迁移和测试智能合同
要快速开始,请使用内置区块链运行Truffle:truffle develop你应该看到这样的东西:
Truffle Develop started at http://localhost:9545/ Accounts: (0) 0x627306090abab3a6e1400e9345bc60c78a8bef57 (1) 0xf17f52151ebef6c7334fad080c5704d77216b732 (2) 0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef (3) 0x821aea9a577a9b44299b9c15c88cf3087f3b5544 (4) 0x0d1d4e623d10f9fba5db95830f7d3839406c6af2 (5) 0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e (6) 0x2191ef87e392377ec08e7c08eb105ef5448eced5 (7) 0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5 (8) 0x6330a553fc93768f612722bb8c2ec78ac90b3bbc (9) 0x5aeda56215b167893e80b4fe645ba6d5bab767de Mnemonic: candy maple cake sugar pudding cream honey rich smooth crumble sweet treat记忆种子可让您重新创建私钥和公钥。例如,将其导入到MetaMask中,如下所示:

> compile你应该看到:
Compiling ./contracts/ERC20.sol... Compiling ./contracts/Migrations.sol... Compiling ./contracts/SafeMath.sol... Compiling ./contracts/TimeLockedWallet.sol... Compiling ./contracts/TimeLockedWalletFactory.sol... Compiling ./contracts/ToptalToken.sol... Writing artifacts to ./build/contracts现在,我们需要定义我们想要部署哪些合约。这是在
migrations/2_deploy_contracts.js
:
var TimeLockedWalletFactory = artifacts.require("TimeLockedWalletFactory"); var ToptalToken = artifacts.require("ToptalToken"); module.exports = function(deployer) { deployer.deploy(TimeLockedWalletFactory); deployer.deploy(ToptalToken); };我们首先导入我们的两个合同工件
TimeLockedWalletFactory
和ToptalToken
。然后我们简单地部署它们。我们错过TimeLockedWallet
了目的,因为这份合同是动态部署的。有关迁移的更多信息,请参阅Truffle迁移文档。
要迁移合同,请运行:
> migrate这应该导致类似于以下内容:
Running migration: 1_initial_migration.js Deploying Migrations... ... 0x1c55ae0eb870ac1baae86eeb15f3aba3f521df46d9816e04400e9b5951ecc099 Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0 Saving successful migration to network... ... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956 Saving artifacts... Running migration: 2_deploy_contracts.js Deploying TimeLockedWalletFactory... ... 0xe9d9c37508bb58a1591d0f052d6870810118a0a19f728bf0cea4f4e5c17acd7a TimeLockedWalletFactory: 0x345ca3e014aaf5dca488057592ee47305d9b3e10 Deploying ToptalToken... ... 0x0469ce110735f27bbb1a85c85a77ba4b0ba0d5aa52c3d67164045b849d8b2ed6 ToptalToken: 0xf25186b5081ff5ce73482ad761db0eb0d25abfbf Saving successful migration to network... ... 0x059cf1bbc372b9348ce487de910358801bbbd1c89182853439bec0afaee6c7db Saving artifacts...你可以知道
TimeLockedWalletFactory
并ToptalToken
成功部署。
最后,为确保一切正常,我们运行一些测试。测试位于test
目录中,并与主要合同TimeLockedWalletTest.js
和TimeLockedWalletFactoryTest.js
。为简洁起见,我们不会深入写作测试的细节,并将其作为读者的练习。要执行测试,只需运行:
> test...希望你会看到所有的测试通过像这样:
Contract: TimeLockedWalletFactory ✓ Factory created contract is working well (365ms) Contract: TimeLockedWallet ✓ Owner can withdraw the funds after the unlock date (668ms) ✓ Nobody can withdraw the funds before the unlock date (765ms) ✓ Nobody other than the owner can withdraw funds after the unlock date (756ms) ✓ Owner can withdraw the ToptalToken after the unlock date (671ms) ✓ Allow getting info about the wallet (362ms) 6 passing (4s)
时间锁定的钱包ÐApp
现在是时候看到这一切都在行动。与任何区块链交互的最简单方法是使用带有Web UI的分布式应用程序,即所谓的应用程序(有时称为“dapps”)。分布式应用设置
为了运行此应用程序,您需要安装以太坊启用的浏览器。实现这个最简单的方法是安装MetaMask Chrome插件。还有一个关于用松露安装和配置MetaMask的视觉指南。智能合约情景
回到我们的场景,我们为什么不先介绍演员?让我们假设爱丽丝将成为时间锁定钱包的创造者,鲍勃将成为基金的接受者/最终拥有者。
- Alice为Bob创建一个时间锁定的钱包并发送一些ETH
- Alice另外发送一些ERC20 Toptal Tokens
- 鲍勃可以看到他接触到的钱包和他创建的钱包
- 在钱包的时间锁定到期之前,Bob不能提取任何资金
- 当Bob解锁后,Bob撤回ETH
- Bob退出所有ERC20 Toptal Tokens








以太坊网络
如果您想与所描述的合约交互,则不必在本地运行它们:我们已将它们部署到Ethereum Rinkeby测试网。ToptalToken部署在此处,TimeLockedWalletFactory部署在此处。 您可以使用我们部署的ÐApp,链接到由GitHub页面提供的上述合同。请注意,您需要安装并连接到Rinkeby的MetaMask。故障排除
在开发这个项目时,我们遇到了几个问题。第一个是Chrome中MetaMask的薄弱(比如抱怨无效nonce
)。我们发现的最简单的修复方法就是重新安装插件。
另外,在编辑智能合同和抱怨invalid number of solidity parameters
错误时,松露有时会失去同步。我们发现一个简单的rm -r build
并且再次进行编译/迁移可以清除它。
以太坊的发展:值得陡峭的学习曲线
我们希望这篇文章激起了你的兴趣,并且你将开始你的开发者之旅进入以太坊之地。通向网络荣耀的道路将是陡峭且耗时的,但有很多资源可以帮助你(比如这个帮助我们的公平比特)。欢迎通过下面的评论与我们联系。 该项目的源代码在GitHub上可用。 如果您想知道如何使用uPort移动应用程序而不是MetaMask,请查看该项目的另一种黑客马拉松获胜版本的演示和源代码。感谢
非常感谢Maciek Zielinski对本项目的贡献。理解基础知识
什么是智能合约?
智能合约是在Ethereum虚拟机之上执行的计算机代码。智能合约可以发送和接受以太网和数据。合约本质上是不可改变的,除非另有规定。
什么是以太坊虚拟机?
以太坊虚拟机(EVM)是一个用于执行字节码的堆栈机器的智能合约的沙盒运行环境。它着重于提供安全性并通过世界各地的计算机执行不可信的代码。
什么是Mist和以太坊钱包?
Mist是分布式应用程序的官方浏览器(ÊApps,有时是dapps),它们是以太网网络用户友好的前端/用户界面。以太坊钱包是其中一个ĐApps。两者都是由建造以太坊的同一个人开发的。
以太坊是开源的吗?
引用Ethereum的发明者Vitalik Buterin的话:“以太坊的一切,包括网站,工具,白皮书,当然还有所有的软件和编译器都是100%,完全的开放源码和使用GPL开放协议。”
以太坊是分散的吗?
是的,它在性质上完全分散。阅读和写作操作完全分散,目前由工作证明机制保证。以太坊的设计方式不会让任何人或团队控制区块链。
以太坊使用什么编程语言?
以太坊的选择语言目前是Solidity。Solidity是一种面向契约的编程语言,主要受JavaScript,C ++和Python的启发,用于编写智能合约。地平线上还有其他语言,比如Vyper。
什么是ERC20令牌?
令牌是实施ERC20标准的智能合约。它们包括获得总供给和平衡等操作以及转移令牌的方法。令牌从未真正离开合同,但只是在内部映射中重新分配给不同持有者的钱包地址。
评论已关闭