13分钟阅读

锁定的钱包:以外智能合约的介绍

radek.是一个兴趣的街区智能合同。他还在机器学习方面拥有丰富的经验。

BlockChain及其应用现在是从未以前永远的流行。特别是为以外的Evereum提供智能合同能力,为新的想法开辟了可以以分布式,不可改变的和无信任的方式实施的新想法。

入门 etereum.智能合同 随着学习曲线非常陡峭,空间可能有点压倒。我们希望这篇文章(以及Ethereum系列中的未来文章)可以缓解这种痛苦并让您快速启动和运行。

松露,稳定性和đapps

在本文中,我们假设您对区块链应用程序和以外人有一些基本的理解。我们建议您觉得自己需要刷新所知 此Ethereum概述从松露框架.

这篇文章涵盖的是什么:

  • 锁定钱包的应用
  • 开发环境设置
  • 用松露框架智能合同开发
    • 坚固合同的描述
    • 如何编译,迁移和测试智能合同
  • 使用ðapp与浏览器的智能合同交互
    • 使用MetAmask进行浏览器设置
    • 主要用例的贯穿

锁定的钱包:用例

有许多不同的应用 etereum. 智能合同。目前最受欢迎的是加密货币(实施为ERC20令牌)和众所周知的令牌销售(A.K.A.初始硬币产品或ICOS。)一个很好的实用程序ERC20令牌的榜样 摩托车硬币。在这个博客文章中,我们将探索不同的东西:Crypto钱包合同中的锁定资金的想法。这个想法本身有各种用例。

归功于ICO

有几个例子,但目前可能是锁资资金的最常见原因被称为“归属​​”。想象一下,您刚刚提出了一个成功的ICO,您的公司仍然持有大多数令牌在您的团队成员之间分发。

它将有利于所有所涉及的各方确保雇员持有的令牌不能直接交易。如果没有控制权,任何给定的员工都可能通过销售所有令牌,兑现和退出公司来采取行动。这会对市场价格产生负面影响,并使所有剩余的贡献者对该项目不满意。

加密的“最后的意志和遗嘱”

另一个想法是将智能合同用作加密。想象一下,我们想在家庭成员可以访问的合同中储存我们的加密货币,但只有在我们发生的事情之后。让我们说我们应该通过每个经常唤起一些合同呼叫,“在”用钱包上“登记”。

如果我们不按时办理入住手续,可能会发生在我们身上,他们可以撤回资金。他们将获得的资金比例可以在合同中明确设定,或者可以通过家庭成员之间的共识决定。

养老金或信托基金

锁定基金的另一个应用可能是创建一个小额养老基金或基于时间的节省账户,即,防止所有者在未来某个时间之前撤销任何资金。 (它对上瘾的加密交易员特别有用,帮助保持他们的以太完整。)

我们将在博客帖子的其余部分探索的用例类似:将一些加密的钱送到后面的别人,就像一个未来的生日礼物。

让我们想象一下,我们想向某人送给某人的18岁生日。我们可以在账户的私钥和持有资金的钱包的地址上写下纸质,并将其交给它们在信封中。他们唯一需要做的是,一旦他们是18岁,所有资金将转移到他们的账户中,唯一的函数就会在合同上呼叫。或者,相反,我们可以使用简单的ðapp。听起来不错?让我们开始吧!

etereum.开发设置

在您提前锻造智能合同开发之前,您需要拥有 node.js.Git. 安装在机器上。在这个博客中,我们将使用 松露 框架。即使您可以在没有它的情况下,Trotufe也显着降低了Ethereum Smart合同的开发,测试和部署的入口障碍。我们完全同意他们的陈述:

松露是以国内最受欢迎的发展框架,使命使您的生活变得更加容易。

要安装它,请运行以下命令:

npm install -g truffle

现在,获取此项目的代码:

git clone //github.com/radek1st/time-locked-wallets
cd time-locked-wallets

值得注意的是,该项目遵循标准松露项目结构和兴趣目录是:

  • contracts:持有所有的坚固合同
  • migrations:包含描述迁移步骤的脚本
  • src:包括ðapp代码
  • test:存储所有合同测试

概述包含的智能合同

该项目中包含了几份合同。这是rundown:

  • timelockedwallet.sol. 是该项目的主要合同,下面详细介绍。
  • timelockedwalletfactory.sol. is the factory contract which lets anyone easily deploy their own TimeLockedWallet.
  • ERC20.sol 是Etereum标记的ERC20标准的界面。
  • toptallok.sol. 是一个定制的ERC20令牌。
  • SafeMath.sol 是由TopTaltoken用于执行安全算术运算的小型库。
  • Migrations.sol 是一个内部松露合同,促进了迁移。

有关以书面合约的任何问题,请参阅官方 坚固智能合同文档.

timelockedwallet.sol.

Our timelockedwallet.sol. 坚固 contract looks like this:

pragma solidity ^0.4.18;

上面的行表示本合同所需的稳定性编译器的最小版本。

import "./ERC20.sol";

在这里,我们导入其他合同定义,以后在代码中使用。

contract TimeLockedWallet {
    ...
}

The above is our main object. contract scopes the code of our contract. The code described below is from inside the curly brackets.

address public creator;
address public owner;
uint public unlockDate;
uint public createdAt;

Here we define several public variables which by default generate corresponding getter methods. A couple of them are of type uint (unsigned integers) and a couple are address (16-character long Ethereum addresses.)

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

In simple terms, modifier is a precondition that has to be met before even starting the execution of the function it is attached to.

function TimeLockedWallet(
    address _creator, address _owner, uint _unlockDate
) public {
    creator = _creator;
    owner = _owner;
    unlockDate = _unlockDate;
    createdAt = now;
}

这是我们的第一个功能。由于名称与我们的合同名称完全相同,因此它是构造函数,并且在创建合同时仅调用一次。

请注意,如果您要更改合同的名称,这将成为任何人可调用的正常功能,并在您的合同中形成后门就像这样的情况 奇偶校验MultiSig钱包错误。此外,请注意,案例也很重要,所以如果这个函数名称在小写中,它也会成为一个常规功能 - 再次,不是你想要的东西。

function() payable public { 
  Received(msg.sender, msg.value);
}

The above function is of a special type and is called the fallback function. If someone sends any ETH to this contract, we will happily receive it. The contract’s ETH balance will increase and it will trigger a Received event. To enable any other functions to accept incoming ETH, you can mark them with the payable keyword.

function info() public view returns(address, address, uint, uint, uint) {
    return (creator, owner, unlockDate, createdAt, this.balance);
}

This is our first regular function. It has no function parameters and defines the output tuple to be returned. Note that this.balance returns the current ether balance of this contract.

function withdraw() onlyOwner public {
   require(now >= unlockDate);
   msg.sender.transfer(this.balance);
   Withdrew(msg.sender, this.balance);
}

The above function can only be executed if onlyOwner modifier defined earlier is satisfied. If the require statement is not true, the contract exits with an error. That’s where we check if the unlockDate has gone by. msg.sender is the caller of this function and it gets transferred the entire ether balance of the contract. In the last line, we also fire a Withdrew event. Events are described a bit later.

Interestingly, now—which is equivalent to block.timestamp—may not be as accurate as one may think. It is up to the miner to pick it, so it could be up to 15 minutes (900 seconds) off as explained in the following 公式:

parent.timestamp >= block.timestamp <= now + 900 seconds

As a consequence, now shouldn’t be used for measuring small time units.

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);
}

Here is our function for withdrawing ERC20 tokens. As the contract itself is unaware of any tokens assigned to this address, we must pass in the address of the deployed ERC20 token we want to withdraw. We instantiate it with ERC20(_tokenContract) 和 then find and transfer the entire token balance to the recipient. We also fire a WithdrewTokens event.

event Received(address _from, uint _amount);
event Withdrew(address _to, uint _amount);
event WithdrewTokens(address _tokenContract, address _to, uint _amount);

在此代码段中,我们正在定义几个事件。触发的事件基本上是将记录条目附加到区块链上的事务收据。每个事务都可以附加零个或多个日志条目。事件的主要用途是 调试和监控.

That’s all we need to time-lock ether and ERC20 tokens—just a few lines of code. Not too bad, huh? Now let’s have a look at our other contract, timelockedwalletfactory.sol..

timelockedwalletfactory.sol.

创造更高级别的工厂合同背后有两种主要原因。第一个是安全问题。通过将资金分开在不同的钱包中,我们不会只用一个额外数量的乙醚和令牌的合同结束。这将使100%的控制权只是钱包所有者,希望阻止黑客试图利用它。

其次,工厂合同允许简单而轻松的TimelockedWallet合同创建,而无需在存在任何发展设置。您需要做的就是从另一个钱包或đapp调用一个函数。

pragma solidity ^0.4.18;

import "./TimeLockedWallet.sol";

contract TimeLockedWalletFactory {
    ...
}

以上是简单的,与之前的合同非常相似。

mapping(address => address[]) wallets;

Here, we define a mapping type, which is like a dictionary or a map, but with all the possible keys preset and pointing to default values. In the case of the address type, the default is a zero address 0x00. We also have an array type, address[], which is holding addresses.

在稳定性语言中,阵列始终包含一种类型,并且可以具有固定或可变长度。在我们的情况下,数组是无限的。

To sum up our business logic here, we define a mapping called wallets which consists of user addresses—contract creators and owners alike—each pointing to an array of associated wallet contract addresses.

function getWallets(address _user) 
    public
    view
    returns(address[])
{
    return wallets[_user];
}

Here, we are using the above function to return all contract wallets a _user created or has rights to. Note that view (in older complier versions called constant) denotes that this is a function that doesn’t change the blockchain state and can be therefore called for free, without spending any gas.

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);
}

This is the most important part of the contract: The factory method. It lets us create a new time-locked wallet on the fly, by calling its constructor: new TimeLockedWallet(msg.sender, _owner, _unlockDate). We then store its address for the creator and the recipient. Later we transfer all the optional ether passed in this function execution to the newly created wallet address. Finally, we signal the Create event, defined as:

event Created(address wallet, address from, address to, uint createdAt, uint unlockDate, uint amount);

toptallok.sol.

This tutorial wouldn’t be so much fun if we didn’t create our own Ethereum token, so for completeness, we are bringing to life ToptalToken. ToptalToken is a standard ERC20 token implementing the interface presented below:

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);

我们给了它一个名字,一个符号,总供给100万,并使其可分解最多六位小数。

要发现令牌合同的不同变体,请随时探索 OpenZeppelin回购.

松露控制台:编译,迁移和测试智能合同

要快速入门,请使用内置区块链式运行块块:

truffle develop

你应该看到这样的东西:

松露 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,如此所示:

恢复保险库:使用助记符的钱包种子将私人和公钥进入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

Now, we need to define which contracts we want to have deployed. This is done in 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);
};

We first import our two contract artifacts TimeLockedWalletFactoryToptalToken. Then we simply deploy them. We missed TimeLockedWallet on purpose, as this contract is deployed dynamically. For more information on migrations, please refer to the 松露迁移文件.

要迁移合同,请运行:

> 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...

You can tell that TimeLockedWalletFactoryToptalToken got deployed successfully.

Finally, to ensure all is working well, let’s run some tests. The tests are located in the test directory and correspond to the main contracts TimeLockedWalletTest.jsTimeLockedWalletFactoryTest.js. For brevity, we’ll not go into the details of writing tests, and leave it as an exercise for the reader. To execute the tests, simply run:

> 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的分布式应用程序,所以称为ðApps(或有时“DAPP)。”)

分布式应用程序设置

为了运行此ðapp,您需要拥有一个支持的Etereum的浏览器。实现这一目标的最简单方法是安装 MetAmask. Chrome插件。还有一个 安装和配置带松露的元迎物的视觉指南.

智能合同情景

回到我们的情景,为什么我们不先介绍演员?让我们假设爱丽丝将成为锁定钱包的创造者,鲍勃将是资金的收件人/最终所有者。

考虑到MetAmask上的Alice和Bob。

场景概述:

  • 爱丽丝为鲍勃创造了一个锁定的钱包,并发送了一些eth
  • 爱丽丝 还发送了一些ERC20 Toptal令牌
  • 鲍勃 can see the wallets he has access to and the ones he created
  • 鲍勃无法在钱包的时间锁定到期之前撤回任何资金
  • 鲍勃 withdraws the ETH when it gets unlocked
  • 鲍勃 withdraws all ERC20 Toptal Tokens

首先,Alice为鲍勃创建一个锁定的钱包,并发送初始的一个以太。我们可以看到新的合同钱包已经创建并由Bob拥有:

使用时锁的钱包đapp创建用于爱丽丝的钱包。

在合同创建后的任何一点,钱包都可以充满。充值可以来自任何人,并以ether或erc20令牌的形式。让我们已经将100个Toptal代币发送到Bob的新钱包,如下所示:

爱丽丝 使用锁定的钱包đapp发送Bob 100 Toptal令牌。

从爱丽丝的角度来看,钱包在充值后将看起来像这样:

爱丽丝 '冠状钱包的看法。

让我们现在切换角色并以Bob登录。鲍勃应该能够看到他创造的所有钱包或者是收件人。由于爱丽丝创建的合同仍然被锁定,他无法撤回任何资金:

鲍勃'虽然仍然被锁定,但旋转钱包的视图。

耐心等待直到锁定到期......

在锁定的钱包đapp上的一个现在解锁的钱包。

... Bob现在准备撤回以太和Toptalog令牌:

鲍勃 withdrawing ether from the unlocked wallet.

鲍勃 withdrawing Toptal Tokens from the unlocked wallet.

排空后锁定的钱包,他的地址余额增加了,让他非常高兴和感激爱丽丝:

解锁钱包,现在是空的。

etereum.网络

如果您想与所描述的合同进行交互,则不必在本地运行它们:我们已将其部署到Ethereum Rinkeby TestNet。 toptalloken部署 这里 和TimelockedWalletFactory部署 这里.

你可以使用我们的 部署ðapp.,与GitHub页面服务的上述合同相关联。请注意,您需要安装元域并连接到Rinkeby。

故障排除

We have come across a couple of issues while developing this project. The first one was the flakiness of MetaMask in Chrome (like complaining about invalid nonce). The easiest fix we found was to just to reinstall the plugin.

Also, Truffle would get out of sync sometimes when editing smart contracts and complain with an invalid number of solidity parameters error. We found that a simple rm -r build 和 doing a compile/migrate again would clear it.

etereum.开发:值得陡峭的学习曲线

我们希望这篇文章激起了你的兴趣,并且您将开始开发人员进入以外的地面。网络荣耀的道路将陡峭且耗时,但那里有很多资源来帮助你(如 这个 这帮助我们一个公平的一点)。随意通过以下评论进行联系。

此项目的源代码 可在github上获得.

如果您想知道如何使用a 乌斯特 移动应用而不是MetAmask,看看 演示源代码 这个项目的替代品,Hackathon-Winning版本。

我也欢迎您阅读 我的后续教程,侧重于đapp创建。

学分

非常感谢 Maciek Zielinski. 他对这个项目的贡献。

理解基础知识

什么是智能合同?

智能合同是一个计算机代码,可在Ethereum虚拟机之外执行。智能合同可以发送和接受以太网和数据。除非另有编程,否则合同本质上是不可变的。

什么是Ethereum虚拟机?

etereum.虚拟机(EVM)是一个Sandboxed运行时环境,用于实现为执行字节码的堆栈机器实现的智能合同。它专注于提供安全性并通过世界各地的计算机执行不受信任的代码。

什么是薄雾和形象的钱包?

雾是用于分布式应用程序的官方浏览器(đapps,有时dApps),它是以外restum网络的用户友好的前端/ uis。 Ethereum钱包是其中之一。两者都是由相同的人建造了以外人的人开发的。

Etreeum开源吗?

引用Vitalik Buterin,Invereum的发明者:“Ethereum的一切,包括网站,工具,白皮书以及所有的软件和编译器都是100%,墙壁开源和GPL下的墙壁。”

像以太统分散吗?

是的,它在自然界完全分散。阅读和写作操作完全分散,目前通过验证机制保护。 Ethereum以这样的方式设计,即没有单身人或团体控制区块链。

etereum.使用的编程语言是什么?

etereum.的选择语言目前是稳定的。稳定性是一种面向合同的编程语言,主要由JavaScript,C ++和Python引发,用于编写智能合同。地平线上还有其他语言,如vyper。

什么是ERC20令牌?

代币是实施ERC20标准的智能合同。它们包括获取传输令牌的总供电和平衡和方法等操作。令牌从未真正留下合同,但只是在内部映射中重新分配给不同的持有人的钱包地址。