原文:Introduction
控制和责任
像以太坊这样的开放式区块链是安全的,因为它们是去中心化的。这意味着以太坊的每个用户都应该控制自己的密钥,这些密钥可以控制对其资金和合约的访问。一些用户选择通过使用第三方保管人(例如交易钱包)来放弃对其密钥的控制。在本书中,我们将教你如何控制和管理自己的密钥。
有了这个控制权就有了很大的责任。如果你丢失钥匙,您将无法获得资金和合约。没有人可以帮助你重新获得访问权限 - 你的资金将被永久锁定。以下是一些帮助您管理此责任的提示:
- 当系统提示你选择密码时:将其设置为强,备份并不共享。如果你没有密码管理器,请将其写下来并将其存放在带锁的抽屉或保险箱中。只要你拥有帐户的“keystore”文件,就可以使用此密码。
- 当系统提示你备份密钥或助记词时,请使用笔和纸进行物理备份。不要把这个任务留给“以后”,你会忘记。这些可用于你丢失系统上保存的所有数据。
- 不要将密钥资料(加密或非加密)存储在数字文档、数码照片、屏幕截图、在线驱动器、加密PDF等中。不要即兴创作。使用密码管理器或笔和纸。
- 在转移任何大额金额之前,首先要做一个小的测试交易(例如,1美元的价值)。收到测试交易后,尝试从该钱包发送。如果您忘记密码或因任何其他原因无法发送资金,最好找一小笔亏本。
- 不要向本书中显示的任何地址汇款。私人密钥列在书中,有人会立即拿走这笔钱。
以太币单位
以太坊的货币单位称为以太,也称为ETH或符号Ξ(来自希腊字母“Xi”,看起来像一个程式化的大写E)或(不太经常)♦,例如,1个以太,或1个ETH,或者Ξ1,或♦1
Tip | 对于Ξ使用Unicode字符926,对于♦使用9830。 |
---|
ether被细分为更小的单位,直到可能的最小单位,称为wei。1 ether = wei。你可能会听到人们也提到货币“以太坊”,但这是一个常见的初学者的错误。以太坊是系统,以太是货币。
以太的值总是在以太坊内部表示为以wei表示的无符号整数值。当您处理1以太时,交易将编码10000000000000000000 wei作为值。
以太的各种单位都有一个使用国际单位制(SI)的科学名称,以及一个向计算和密码学的许多伟大思想致敬的口语名称。
表以太名称和单位名称显示各种单位,其口语(通用)名称及其SI名称。为了与值的内部表示保持一致,该表显示了wei中的所有面额(第一行),其中以太在第7行显示为 wei:
表1.以太面额和单位名称
值(wei) | 指数 | 通用名称 | SI 名称 |
---|---|---|---|
1 | 1 | wei | wei |
1,000 | babbage | kilowei or femtoether | |
1,000,000 | lovelace | megawei or picoether | |
1,000,000,000 | shannon | gigawei or nanoether | |
1,000,000,000,000 | szabo | microether or micro | |
1,000,000,000,000,000 | finney | milliether or milli | |
1,000,000,000,000,000,000 | ether | ether | |
1,000,000,000,000,000,000,000 | grand | kiloether | |
1,000,000,000,000,000,000,000,000 | megaether |
选择以太坊钱包
以太坊钱包是您进入以太坊系统的门户。它包含您的密钥,可以代表您创建和广播交易。选择以太坊钱包可能很困难,因为有许多不同的选项,具有不同的功能和设计。有些更适合初学者,有些更适合专家。即使您选择了您现在喜欢的一个,您也可能决定稍后切换到另一个钱包。以太坊本身在不断变化,“最好”的钱包往往是适应它们的钱包。
但别担心!如果您选择钱包并且不喜欢它的工作方式,您可以很容易地更换钱包。您所要做的就是进行一项交易,将您的资金从旧钱包发送到新钱包,或通过导出和导入您的私钥来移动密钥。
首先,我们将在整本书中选择三种不同类型的钱包作为示例:手机钱包,桌面钱包和基于web的钱包。我们选择了这三个钱包,因为它们代表了广泛的复杂性和功能。但是,选择这些钱包并不是对其质量或安全性的认可。它们只是演示和测试的良好起点。
初学者钱包:
MetaMask
MetaMask是一个浏览器扩展钱包,可在您的浏览器(Chrome,Firefox,Opera或Brave Browser)中运行。它易于使用且便于测试,因为它能够连接到各种以太坊节点和测试区块链。
Jaxx
Jaxx是一款多平台、多币种的钱包,可在各种操作系统上运行,包括Android,iOS,Windows,Mac和Linux。对于新用户而言,它通常是一个不错的选择,因为它的设计简单易用。
MyEtherWallet (MEW)
MyEtherWallet是一个基于web的钱包,可以在任何浏览器中运行。它具有我们将在许多示例中探索的多种复杂功能。
Emerald Wallet
Emerald Wallet旨在与以太坊Classic区块链配合使用,但与其他基于以太坊的区块链兼容。它是一个开源桌面应用程序,可在Windows,Mac和Linux下运行。Emerald钱包可以运行完整节点或连接到公共远程节点,以“轻”模式工作。它还有一个配套工具,可以从命令行执行所有操作。
我们首先在桌面上安装MetaMask。
安装MetaMask
打开Google Chrome浏览器并导航至:
https://chrome.google.com/webstore/category/extensions
搜索“MetaMask”并单击狐狸的徽标。您应该看到扩展程序的详细信息页面如下:
图1. MetaMask Chrome扩展的详细信息页面
验证您是否正在下载真正的MetaMask扩展程序非常重要,因为有时候人们可以通过谷歌的过滤器隐藏恶意扩展。确认您正在查看正确的扩展程序后,请点击“添加到Chrome”进行安装。
第一次使用MetaMask
安装MetaMask后,您应该在浏览器的工具栏中看到一个新图标(狐狸头)。点击它开始。系统会要求您接受条款和条件,然后输入密码来创建新的以太坊钱包:
图2. MetaMask Chrome扩展的密码页面
Tip | 密码控制对MetaMask的访问,因此任何有权访问您浏览器的人都无法使用它。 |
---|
设置密码后,MetaMask将为您生成一个钱包,并显示由12个英文单词组成的助记符备份。如果MetaMask或您的计算机出现问题,可以在任何兼容的钱包中使用这些单词来恢复对您资金的访问。您不需要此恢复的密码。这12个字就足够了。
图3.由MetaMask创建的钱包的助记符备份
Tip | 将您的助记符(12个单词)备份在纸上,两次。将两个纸张备份存放在两个单独的安全位置,例如防火保险箱,锁定抽屉或保险箱。将纸质备份视为您在以太坊钱包中存储的等值现金。任何能够访问这些单词的人都可以访问并窃取您的资金。 |
---|
一旦确认已安全存储助记符,MetaMask将显示您的以太坊帐户详细信息:
图4. MetaMask中的以太坊帐户
您的帐户页面显示您的帐户名称(默认情况下为“Account 1”),以太坊地址(示例中为0x9E713 ...)和彩色图标,以帮助您直观地将此帐户与其他帐户区分开来。在帐户页面的顶部,您可以看到当前正在处理的以太网网络(示例中的“Main Network”)。
恭喜!你已经设置了你的第一个以太坊钱包!
切换网络
正如您在MetaMask帐户页面上看到的,您可以在多个以太坊网络之间进行选择。默认情况下,MetaMask将尝试连接到“Main Network”。其他选择是公共测试网,您选择的任何以太坊节点,或在您自己的计算机上运行私有区块链的节点(localhost):
Main Network
主要的、公共的,以太坊区块链。真正的ETH,真正的价值,真正的结果。
Ropsten Test Network
以太坊公共测试区块链和网络,使用工作证明共识(挖掘)。该网络上的ETH没有任何价值。Ropsten的问题在于,攻击者铸造了成千上万的区块,产生巨大的重组,并将gas限制推高至9B。然后需要一个新的公共测试网,但是稍后(2017年3月25日)Ropsten也恢复了!
Kovan Test Network
以太坊公共测试区块链和网络,使用“Aura”协议进行权威证明POA共识(联合签名)。该网络上的ETH没有任何价值。此测试网络仅由“Parity”支持。其他以太坊客户使用后来提出的“Clique”协议作为权威证明POA。
Rinkeby Test Network
以太坊公共测试区块链和网络,使用“Clique”协议进行权威证明POA共识(联合签名)。该网络上的ETH没有任何价值。
Localhost 8545
连接到与浏览器在同一台计算机上运行的节点。该节点可以是任何公共区块链(main或testnet)的一部分,也可以是私有testnet。
Custom RPC
允许您使用兼容geth的远程过程调用(RPC)接口将MetaMask连接到任何节点。该节点可以是任何公共或私人区块链的一部分。
Tip | 您的MetaMask钱包在其连接的所有网络上使用相同的私钥和以太坊地址。但是,您在每个以太坊网络上的以太坊地址余额将会有所不同。例如,您的密钥可以控制Ropsten上的以太和合约,但不能在主网络上控制。 |
---|
获取一些测试以太
我们的首要任务是为我们的钱包提供资金。我们不会在主网络上这样做,因为真正的以太坊需要花钱并且处理它需要更多的经验。现在,我们将使用一些testnet ether加载我们的钱包。
将MetaMask切换到Ropsten测试网络。然后单击“购买”,然后单击“Ropsten Test Faucet”。MetaMask将打开一个新的网页:
图5. MetaMask Ropsten测试水龙头
您可能会注意到该网页已包含您的MetaMask钱包的以太坊地址。MetaMask将启用了以太坊的网页与您的MetaMask钱包集成在一起。MetaMask可以在网页上“查看”以太坊地址,例如,您可以将付款发送到显示以太坊地址的在线商店。如果网页请求,MetaMask还可以使用您自己的钱包地址填充网页作为收件人地址。在此页面中,水龙头应用程序正在向MetaMask询问钱包地址以发送测试以太。
按绿色“request 1 ether from faucet”按钮。您将在页面的下半部分看到一个交易ID。水龙头应用程序创建了一个交易 - 付款给您。交易ID如下所示:
0x7c7ad5aaea6474adccf6f5c5d6abed11b70a350fbc6f9590109e099568090c57
在几秒钟内,新交易将由Ropsten矿工开采,您的MetaMask钱包将显示1 ETH的余额。单击交易ID,您的浏览器将带您进入区块浏览器,这是一个允许您可视化和浏览区块、地址和交易的网站。MetaMask使用etherscan.io区块浏览器,这是一个比较流行的以太坊区块浏览器。包含我们来自Ropsten测试水龙头的付款的交易显示在Etherscan Ropsten Block Explorer中。
图6. Etherscan Ropsten Block Explorer
该交易已记录在Ropsten区块链上,任何人都可以随时查看,只需搜索交易ID或访问链接即可:
https://ropsten.etherscan.io/tx/0x7c7ad5aaea6474adccf6f5c5d6abed11b70a350fbc6f9590109e099568090c57
尝试访问该链接,或将交易哈希输入ropsten.etherscan.io网站,以便亲自查看。
从MetaMask发送ether
一旦我们从Ropsten测试水龙头接收到我们的第一个测试ether,我们将尝试发送以太,试图将一些回到水龙头。正如您在Ropsten测试水龙头页面上看到的那样,可以选择“捐赠”1个ETH到水龙头。此选项可用,这样一旦您完成测试,您可以返回测试ether的剩余部分,以便其他人可以使用它。尽管测试以太没有价值,但有些人会囤积它,这使得其他所有人都难以使用测试网络。囤积测试以太不受欢迎!
幸运的是,我们不是测试以太囤积者,我们还是希望练习发送以太。
单击橙色“1 ether”按钮告诉MetaMask创建支付水龙头1 ether的交易。MetaMask将准备一个交易并弹出一个确认窗口:
图7.向水龙头发送1个以太网
哎呀!您可能已经注意到您无法完成交易。MetaMask说“交易余额不足”。乍一看,这可能看起来令人困惑:我们有1个ETH,我们想发送1个ETH,为什么MetaMask说我们没有足够的资金?
答案是因为gas的成本。每笔以太坊交易都需要支付费用,矿工会收取费用以验证交易。以太坊交易的虚拟货币费用,称为gas。作为交易的一部分,您使用以太币支付gas。
Tip | 测试网络也需要费用。如果没有费用,测试网络的行为将与主网络不同,使其成为一个不充分的测试平台。费用还保护测试网络免受拒绝服务攻击和构造不良的合约(例如无限循环),就像它们保护主网络一样。 |
---|
当您发送交易时,Metamask计算了最近成功交易的平均gas价格为3 GWEI,即3千兆wei。wei是以太币的最小细分,正如我们用以太币货币单位讨论的那样。发送基本交易的gas成本是21000个gas单位。因此,您花费的最大ETH量是3 * 21000 GWEI = 63000 GWEI = 0.000063 ETH。请注意,平均gas价格可能会波动,因为它们主要由矿工决定。我们将在后面的章节中看到如何增加/减少gas limit,以确保在需要时优先处理您的交易。
所有这些都说:做1 ETH交易成本为1.000063 ETH。当显示总数时,MetaMask会混淆地将其降低到1 ETH,但实际需要的数量是1.000063 ETH并且您只有1个ETH。单击“Reject”以取消此交易。
从水龙头请求多一些的以太,一旦你有2个ETH的余额,你可以再试一次。这次,当您点击橙色的“1以太”捐赠按钮时,您有足够的余额来完成交易。当MetaMask弹出付款窗口时,点击“提交”。在完成所有这些之后,您应该看到余额为0.999937 ETH,因为您将1个ETH发送到具有0.000063 ETH gas的水龙头。
搜索地址的交易历史记录
到目前为止,您已成为使用MetaMask发送和接收测试以太的专家。您的钱包已收到至少两笔付款并至少发送一次。让我们使用ropsten.etherscan.io块资源管理器查看所有这些交易。您可以复制钱包地址并将其粘贴到块浏览器的搜索框中,也可以让MetaMask为您打开页面。在MetaMask中的帐户图标旁边,您会看到一个显示三个点的按钮。单击它以显示与帐户相关的选项菜单:
图8. MetaMask帐户上下文菜单
选择“View account on Etherscan”,在区块浏览器中打开一个网页,显示您帐户的交易历史记录:
图9. Etherscan上的地址交易历史记录
在这里,您可以查看以太坊地址的整个交易历史记录。它显示了Ropsten区块链上记录的所有交易,其中您的地址是交易的发件人或收件人。单击其中一些交易以查看更多详细信息。
您可以浏览任何地址的交易历史记录。看看您是否可以浏览Ropsten测试水龙头地址的交易历史记录(提示:它是您地址中最早支付中列出的“发件人”地址)。您可以看到从水龙头发送到您和其他地址的所有测试以太。您看到的每笔交易都可以带您到更多地址和更多交易。不久之后,你将迷失在互联数据的迷宫中。公共区块链包含大量信息,所有这些信息都可以通过编程方式进行探索,我们将在未来的示例中看到。
介绍世界计算机
我们已经创建了一个钱包,我们发送并接收了以太币。到目前为止,我们已将以太坊视为加密货币。但以太坊还有更多功能。事实上,加密货币功能服从于以太坊作为世界计算机的功能; 去中心化的智能合约平台。Ether用于支付运行智能合约的费用,智能合约是在称为以太坊虚拟机(EVM)的模拟计算机上运行的计算机程序。
EVM是一个全局单例,意味着它就像是一个全局的单实例计算机一样运行,无处不在。以太坊网络上的每个节点都运行EVM的本地副本以验证合约执行,而以太坊区块链在处理交易和智能合约时记录此世界计算机的变化状态。
外部账户(EOA)和合约
我们在MetaMask钱包中创建的帐户类型称为外部拥有帐户(EOA)。外部拥有的账户是那些拥有私钥的账户,可以控制对资金或合约的访问。现在,您可能猜测还有其他类型的帐户。另一种类型的帐户是合约帐户。合约帐户由记录在以太坊区块链上并由EVM执行的软件程序的逻辑所拥有(和控制)。
将来,所有以太坊钱包都可能像以太坊合约一样运行,模糊了外部所有账户和合约之间的区别。但是,始终存在的重要区别在于:当软件通过合约做出决策时,人类通过EOA做出决策。
合约有一个地址,就像EOA(钱包)一样。合约可以发送和接收以太,就像钱包一样。当交易目标是合约地址时,它会使该交易作为其输入使该合约在EVM中运行。
除了ether,交易可包含数据指示哪些特定功能在合约中运行,并传递给该功能的哪些参数。通过这种方式,交易可以在合约中调用函数。最后,合约可以生成调用其他合约的交易,构建复杂的执行路径。一个典型的用途是合约A呼叫合约B,以便在合约A的用户之间维持共享状态。
在接下来的几节中,我们将编写第一份合约。然后,我们将使用我们的MetaMask钱包创建、资助和使用该合约,并在Ropsten测试网络上测试ether。
一个简单的合约:一个测试ether水龙头
以太坊有许多不同的高级语言,所有这些语言都可用于编写合约并生成EVM字节码。到目前为止,一种高级语言是智能合约编程的主要语言:Solidity。Solidity由本书的合著者Gavin Wood创建,并已成为以太坊及其他地区使用最广泛的语言。我们将使用Solidity编写我们的第一份合约。
对于我们的第一个例子,我们将编写一个控制水龙头的合约。我们已经在Ropsten测试网络上使用了一个水龙头来测试ether。水龙头是一件相对简单的事情:它会向任何要求的地址发出以太,并且可以定期重新填充。您可以将水龙头实施为由人(或Web服务器)控制的钱包,但我们将编写实施水龙头的Solidity合同:
Faucet.sol:实施水龙头的Solidity合同
link:code/Faucet.sol[]
下载Faucet.sol:https://github.com/ethereumbook/ethereumbook/blob/first_edition/code/Faucet.sol
这是一个非常简单的合约,尽可能简单。它也是一个有缺陷的合同,表明了一些不良做法和安全漏洞。我们将通过检查后面部分中的所有缺陷来学习。但就目前而言,让我们一行一步地看看这份合约的作用及其运作方式。
第一行是注释:
// Version of Solidity compiler this program was written for
注释供人阅读,不包含在可执行EVM字节码中。我们通常在我们试图解释的代码之前将它们放在行上,或者有时在同一行上。注释以两个正斜杠//开头。从斜线到超出的所有内容,直到该行的结尾,都被视为空行并被忽略。
好的,下一行是我们实际合同开始的地方:
contract Faucet {
该行声明了一个合约对象,类似于其他面向对象语言(如JavaScript,Java或C ++)中的类声明。合约定义包括定义范围的花括号{}之间的所有行,就像在许多其他编程语言中使用花括号一样。
接下来,我们声明水龙头合约的第一个功能:
function withdraw(uint withdraw_amount) public {
该函数名为withdraw,它接受一个名为withdraw_amount的无符号整数(uint)参数。它被声明为公共函数,这意味着它可以被其他合约调用。函数定义遵循花括号:
require(withdraw_amount <= 100000000000000000);
提现功能的第一部分设定了提款限额。它使用内置的Solidity函数require来测试一个前提条件,即withdraw_amount小于或等于100000000000000000 wei,这是ether的基本单位,相当于0.1 ether。如果使用大于该数量的withdraw_amount调用withdraw函数,则此处的require函数将导致合约执行停止并因异常而失败。
这部分合约是我们水龙头的主要逻辑。它通过限制提款来控制合约之外的资金流动。这是一个非常简单的控制,但可以让你一瞥可编程区块链的力量:控制资金的去中心化软件。
接下来是实际提现:
msg.sender.transfer(withdraw_amount);
这里发生了一些有趣的事情。msg对象是所有合约都可以访问的输入之一。它表示触发此合约执行的交易。属性sender是交易的发件人地址。函数传递是一个内置函数,它将以太从合约传递到调用它的地址。向后读,这意味着转移到触发此合约执行的msg的发送者。传递函数将金额作为其唯一参数。我们将withdraw_amount值作为参数传递给上面几行声明的withdraw函数。
下一行是结束大括号,表示我们的withdraw函数定义的结束。
下面我们再声明一个功能:
function () public payable {}
此函数是所谓的“回调”或默认函数,如果触发合约的交易未命名合约中的任何已声明函数或任何函数或未包含数据,则调用此函数。合约可以有一个这样的默认函数(没有名称),它通常是接收以太的函数。这就是为什么它被定义为公共和默认函数,这意味着它可以接受以太合约。除了接受以太之外,它没有做任何事情,如花括号{}中的空定义所示。如果我们创建一个将ether发送到合约地址的交易,就好像它是钱包一样,这个函数将处理它。
在我们的默认函数下面是最后的结束花括号,它关闭了合约Faucet的定义。而已!
编译水龙头合约
现在我们有了第一个示例合约,我们需要使用Solidity编译器将Solidity代码转换为EVM字节码,因此它可以由EVM执行。
Solidity编译器作为独立的可执行文件,作为不同框架的一部分,也捆绑在集成开发环境(IDE)中。为了简单起见,我们将使用一种比较流行的IDE,称为Remix。
使用您的Chrome浏览器(我们之前安装的MetaMask钱包)导航到Remix IDE:
当您第一次加载Remix时,它将以一个名为ballot.sol的示例合约开始。我们不需要它,所以让我们关闭它,点击选项卡一角的x:
图10.关闭默认示例选项卡
现在,通过单击左侧工具栏中的圆形加号,添加一个新选项卡,命名新文件Faucet.sol:
图11.单击加号以打开新选项卡
打开新选项卡后,复制并粘贴我们的示例Faucet.sol中的代码:
图12.将Faucet示例代码复制到新选项卡中
现在我们已经将Faucet.sol合约加载到Remix IDE中,IDE将自动编译代码。如果一切顺利,你会在右侧的Compile选项卡下看到一个带有“Faucet”的绿色框,确认编译成功:
图13. Remix成功编译了Faucet.sol合约
如果出现问题,最可能的问题是Remix IDE使用的是与0.4.19不同的Solidity编译器版本。在这种情况下,我们的pragma指令将阻止Faucet.sol编译。要更改编译器版本,请转到“设置”选项卡,将编译器版本设置为0.4.19,然后重试。
Solidity编译器现在已将Faucet.sol编译为EVM字节码。如果你很好奇,字节码看起来像这样:
PUSH1 0x60 PUSH1 0x40 MSTORE CALLVALUE ISZERO PUSH2 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0xE5 DUP1 PUSH2 0x1D PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x60 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH1 0x3F JUMPI PUSH1 0x0 CALLDATALOAD PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV PUSH4 0xFFFFFFFF AND DUP1 PUSH4 0x2E1A7D4D EQ PUSH1 0x41 JUMPI JUMPDEST STOP JUMPDEST CALLVALUE ISZERO PUSH1 0x4B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x5F PUSH1 0x4 DUP1 DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 SWAP2 SWAP1 POP POP PUSH1 0x61 JUMP JUMPDEST STOP JUMPDEST PUSH8 0x16345785D8A0000 DUP2 GT ISZERO ISZERO ISZERO PUSH1 0x77 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST CALLER PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH2 0x8FC DUP3 SWAP1 DUP2 ISZERO MUL SWAP1 PUSH1 0x40 MLOAD PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 DUP6 DUP9 DUP9 CALL SWAP4 POP POP POP POP ISZERO ISZERO PUSH1 0xB6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 PUSH9 0x13D1EA839A4438EF75 GASLIMIT CALLVALUE LOG4 0x5f PUSH24 0x7541F409787592C988A079407FB28B4AD000290000000000
您是否很高兴使用Solidity等高级语言而不是直接在EVM字节码中编程?我也是!
在区块链上创建合同
所以我们写了合约。我们把它编译成字节码。现在,我们需要在以太坊区块链上“注册”合约。我们将使用Ropsten测试网来测试我们的合约,这就是我们想要记录的区块链。
在区块链上注册合约涉及创建一个特殊交易,其目的地是地址0x00000000000000000000000000000000000000000000,也称为零地址。零地址是一个特殊地址,告诉以太坊区块链您想要注册合约。幸运的是,Remix IDE将为您处理所有这些并将交易发送到MetaMask。
首先,切换到“Run”选项卡,然后在“Environment”下拉选择框中选择“Injected Web3”。这将Remix IDE连接到MetaMask钱包,并通过MetaMask连接到Ropsten测试网络。一旦你这样做,你可以在环境下看到“Ropsten”。此外,在帐户选择框中,它显示您的钱包的地址:
图14. Remix IDE“Run”选项卡,选中“Injected Web3”环境
在我们刚刚确认的“Run”设置的正下方,是水龙头合约,准备好了。单击“Create”按钮(译者注:现在是“Deploy”按钮):
图15.单击Run选项卡中的Create按钮
Remix IDE将构建特殊的“Create”交易,MetaMask将要求您批准它。正如您从MetaMask中看到的那样,合约创建交易中没有以太,但它有258个字节(已编译的合约)并将消耗10 Gwei的气体。点击“Submit”批准它:
图16.显示合约创建交易的MetaMask
现在,等一下:在Ropsten上部署合约大约需要15到30秒。Remix IDE似乎没有做太多,请耐心等待。
创建合约后,它将显示在“Run”选项卡的底部:
图17.水龙头合约正在进行中!
请注意,水龙头合约现在有一个自己的地址:Remix将其显示为Faucet,位于0x72e .... c7829。右侧的小剪贴板符号允许您将合约地址复制到剪贴板中。我们将在下一节中使用它。
与合约交互
让我们回顾一下迄今为止我们学到的东西:以太坊合约是控制资金的程序,它在称为EVM的虚拟机内运行。它们由特殊交易创建,该交易提交其字节码以记录在区块链上。一旦他们在区块链上创建,他们就有了一个以太坊地址,就像钱包一样。只要有人将某个交易发送到合约地址,就会导致合约在EVM中运行,并将该合约作为其输入。发送到合约地址的交易可能包含ether或数据或两者。如果它们含有ether,则将其“存入”合约余额。如果它们包含数据,则数据可以在合约中指定命名函数并调用它,将参数传递给函数。
在区块资源管理器中查看合同地址
现在,我们在区块链上记录了一份合约,我们可以看到它有一个以太坊地址。让我们在ropsten.etherscan.io区块浏览器上查看它,看看合约是什么样的。通过单击其名称旁边的剪贴板图标来复制合约的地址:
图18.从Remix复制合约地址
在一个标签中打开Remix,稍后我们会再次回顾它。现在,将浏览器导航到ropsten.etherscan.io并将地址粘贴到搜索框中。你应该看到合约的以太坊地址历史:
图19.在etherscan块资源管理器中查看水龙头合同地址
资助合约
目前,合约在其历史记录中只有一个交易:合约创建交易。如您所见,合约也没有以太(零余额)。那是因为我们没有在创建交易中向合约发送任何以太,即使我们可以。
让我们给合约发一些以太!您仍然应该在剪贴板中包含合约的地址(如果没有,请从Remix再次复制)。打开MetaMask,并向其发送1个以太,就像您发送给其他任何以太坊地址一样:
图20.将1 ether发送到合约地址
在一分钟内,如果您重新加载etherscan区块资源管理器,它将显示合约地址的另一个交易以及1个以太网的更新余额。
还记得我们的Faucet.sol代码中未命名的默认公共应付款功能吗?它看起来像这样:
function () public payable {}
当您将交易发送到合同地址时,没有数据指定要调用的函数,它会调用此默认函数。因为我们将其声明为应付款,所以它接受并将1以太币存入合约账户余额。您的交易导致合约在EVM中运行,更新其余额。我们资助了我们的水龙头!
提现我们的合约
接下来,让我们从水龙头中提取一些资金。要提现,我们必须构造一个调用withdraw函数的交易,并将withdraw_amount参数传递给它。为了使事情变得简单,Remix将为我们构建该交易,MetaMask将提供它以供我们批准。
返回Remix选项卡,查看“Run”选项卡下的合约。你应该看到一个标有“Withdraw”的红色框,其中包含一个标记为uint256 withdraw_amount的字段条目:
图21. Remix中Faucet.sol的提现功能
这是合约的Remix接口。它允许我们构造调用合约中定义的函数的交易。我们将输入withdraw_amount并单击“Withdraw”按钮以生成交易。
首先,让我们弄清一下withdraw_amount。我们想尝试提现0.1以太,这是我们合约允许的最大金额。请记住,以太坊中的所有货币值都在内部以wei表示,而我们的提现功能期望withdraw_amount也以wei计价。我们想要的数量是0.1以太,这是100000000000000000 wei(1后跟17个零)。
Tip | 由于JavaScript的限制,Remix无法处理大到10 ^ 17的数字。相反,我们将它括在双引号中,以允许Remix将其作为字符串接收并将其作为BigNumber进行操作。如果我们不将它括在引号中,则Remix IDE将无法处理它并显示“Error encoding arguments:Error:Assertion failed” |
---|
在withdraw_amount框中键入“100000000000000000”(带引号),然后单击“Withdraw”按钮:
图22.单击Remix中的“withdraw”以创建提款交易
MetaMask将弹出一个交易窗口供您批准。点击“Submit”将您的提款调用发送给合约:
图23.调用withdraw函数的MetaMask交易
等一下,然后重新加载etherscan区块资源管理器,以查看在水龙头合约地址历史记录中的交易:
图24. Etherscan显示调用withdraw函数的交易
我们现在看到一个新的交易,其中合约地址为目的地,零以太。合约余额已经改变,现在是0.9以太,因为它按要求向我们发送了0.1以太。但我们在合约地址历史记录中看不到“OUT”交易。
提现的地方在哪里?合约的地址历史记录页面中出现了一个新选项卡,名为“Internal Transactions”。因为0.1以太传输源自合约代码,所以它是内部交易(也称为消息)。单击“Internal Transactions”选项卡以查看它:
图25. Etherscan显示了从合约中转出以太的内部交易
这个“Internal Transactions”是由合约在这行代码中发出的(来自Faucet.sol中的提现功能):
msg.sender.transfer(withdraw_amount);
回顾一下:我们从MetaMask钱包发送了一个包含数据指令的交易,调用withdraw_amount参数为0.1 ether的withdraw函数。该交易导致合约在EVM内部运行。当EVM运行水龙头合约的提现功能时,首先它调用require函数并验证我们的金额小于或等于允许的最大提现0.1以太。然后它调用传递函数向我们发送以太。运行转账功能会产生一个内部交易,从合约的余额中将0.1以太币存入我们的钱包地址。这是etherscan中“内部交易”选项卡中显示的那个。
结论
在本章中,我们使用MetaMask设置了钱包,我们使用Ropsten测试网络上的水龙头为其提供资金。我们收到了以太币到我们钱包的以太坊地址。然后我们把以太送到了水龙头的以太坊地址。
接下来,我们在Solidity写了一个水龙头合约。我们使用Remix IDE将合约编译为EVM字节码。我们使用Remix进行交易,并在Ropsten区块链上记录了水龙头合约。一旦记录下来,水龙头合约就有一个以太坊地址,我们发了一些ether。最后,我们构建了一个交易来调用withdraw函数并成功请求0.1 ether。合约检查了我们的请求,并通过内部交易向我们发送了0.1以太。
它可能看起来不多,但我们刚刚成功地与在分散的世界计算机上控制资金的软件进行交互。
我们将在[智能合约]中进行更多智能合约编程,并了解最佳实践和安全注意事项。