以太坊 面试题

目录

(1) 以太坊常见问题

问:在以太坊中,Wei和Ether(以太币)有什么区别?

问:以太坊的平均区块间隔时间是多少?

问:以太坊的平均区块大小是多少?

问:以太币是如何产生的?

问:以太坊中的节点是什么?

问:以太坊都有哪些网络?

问:与以太坊网络交互都哪些方法?

问:你是否能在以太坊中“隐藏”一笔交易?

问:以太坊的交易记录存放在哪里?

问:以太坊主链已经很强大了,为什么还要使用以太坊私有链?

问:如何查看一笔交易或一个区块的详细信息?

问:如何查看私有链中一笔交易或一个区块的详细信息?

问:区块链的共识过程是什么?

问:以太坊挖矿操作的工作原理是什么呢?

问:区块链中最常用的两种共识协议是什么?

问:请简述权益证明的工作原理。

问:以太坊使用哪种共识协议呢?

问:签署一笔交易需要用到什么工具?

问:在私钥丢失后,用户是否还可以恢复以太坊帐户?

问:用什么方法可以连接到以太坊节点?

问:以太坊中异常火爆的Geth是什么呢?

问:连接到Geth客户端的默认方式是什么?

问:Geth客户端中都有哪些API(Application Programming Interface,应用程序编程接口)?

问:你可以使用哪些RPC通过网络连接到Geth客户端?

问:如果你输入命令“—rpc”,启用的是哪一个RPC?

问:默认情况下哪些RPC API是启用的?

问:如何为JSON-RPC启用admin api?

问:命令“—datadir”有什么功能?

问:Geth的“快速”同步是什么,为什么它更快速呢?

问:命令“—testnet”有什么功能?

问:启动Geth客户端会在屏幕上打印大量的输出信息,如果不想被这些繁杂信息干扰该怎么办?

问:如何使用IPC-RPC连接两个Geth客户端?

问:如何将自定义javascript文件加载到Geth控制台?

问:Geth客户端中帐户存储在哪里?

问:如何使用给定的账户发起一笔交易?

问:我们刚才说到了有关索引的内容。账户的索引取决于什么?

问:Geth客户端是否能用来挖矿?

问:挖矿选项中的“etherbase”是什么?

(2) 智能合约常见问题

问:ABI是什么?

问:智能合约是什么?

问:智能合约可以用哪种语言编写?

问:智能合约都有什么样的使用场景呢?

问:什么是MetaMask?

问:Metamask使用什么节点?

问:相比于传统以太坊钱包,有哪些功能是Metamask不支持的?

问:智能合约的执行是免费的吗?

问:查看智能合约的状态是免费的吗?

问:谁来执行智能合约?

问:为什么调用智能合约中的函数需要花钱?

问:为什么以太坊中要引入燃料费用呢?

问:燃料价格是否能决定交易被处理的时间?

问:交易中的燃料使用量取决于什么?

问:交易费该如何计算?

问:如果执行智能合约的花费少于交易者支付的燃料费用,他是否会获得退款?

问:如果执行智能合约的花费超过了交易者支付的燃料费用,这时会发生什么?

问:谁来支付调用智能合约的费用?

问:节点在哪里运行智能合约的代码呢?

问:以太坊虚拟机需要什么工具来运行智能合约?

问:以太坊虚拟机都包含哪些部分?

问:Remix是什么?

问:在Remix中,你可以连接哪些节点?

问:什么是DApp,它与普通App有什么不同?

(3) Solidity常见问题

问:Solidity是静态类型语言(类型的检查是在运行前做的,如编译阶段)还是动态类型语言(类型的检查是在运行时做的)?

问:Solidity中的什么结构与Java中的类(Class)相对应?

问:智能合约的实例是什么?

问:Java和Solidity之间存在哪些差异。

问:在Solidity文件中需要指定的第一个参数是什么?

问:一个智能合约包含什么?

问:智能合约中都有哪些类型的函数?

问:如果我将多个智能合约定义放入单个Solidity文件中,会出现什么样的错误?

问:两个智能合约可以通过哪些方式进行交互?

问:当你尝试部署具有多个智能合约的文件时会发生什么?

问:如果我有一个庞大的项目,我是否需要将所有相关的智能合约保存在一个文件中?

问:我是否只能导入本地文件?

问:以太坊虚拟机的内存都有哪些部分?

问:请解释一下存储(Storage)。

问:请解释一下内存(Memory)。

问:请解释一下Calldata。

问:存储区和内存区分别存储了哪些变量?

问:阅读以下代码,请解释代码的哪一部分对应哪个内存区域:

问:我是否可以这样定义一个函数:

问:EVM调用和非EVM调用之间有什么区别呢?

问:如何设定智能合约的以太币余额限制,如果向有余额限制的智能合约中发送超额的以太币会发生什么?

问:如何在智能合约帐户中设置msg.val的值?

(4) DApp和web3.0常见问题

问:DApp是什么?

问:DApp与智能合约有何不同?

问:前端使用什么工具连接到后端的智能合约?

问:请列几个你所知道的DApp。

问:你需要什么工具与DApp的智能合约进行交互?

问:ABI的作用是什么?

问:字节码的作用是什么?

问:为什么要使用大数运算程序库(BigNumber library)?

问:为什么要始终检查DApp代码的开头是否设置了web3提供程序(provider)?

问:为什么使用web3 js的1.x版本而不是0.2x.x版本?

问:在web3 1.x版本中如何列出所有帐户?

问:“.call”和“.send”有什么区别?

问:是否可以通过这样的命令“.send({value:1})”发送一个以太币?

问:那是否意味着,为了发送一个以太币,我需要将值设置为10^18?

问:调用“.send()”时我需要指定什么?

问:将以太币发送到特定地址的函数是否只有

问:以太坊的可扩展性问题有什么解决方案?

02 面试核心要点梳理

(1) Solidity文件的布局

Pragma版本(Version Pragma)

导入其他源文件

注释

(2) 以太坊存储区域

(3) Solidity中的数据类型

布尔类型

整型

地址类型

字符串类型

(4) 运算符

算术运算符

增量运算符

位运算符

逻辑运算符

(5) Solidity中的数据结构

结构体

数组

映射

(6) 程序控制结构

(7) 函数

(8) 函数修改器

(9) 继承


(1) 以太坊常见问题

问:在以太坊中,Wei和Ether(以太币)有什么区别?

答:Wei是以太币的最小面值,就好比说人民币的最小面值是分,英镑的最小面值是便士。 其换算关系为1以太币 =10^18Wei。

问:以太坊的平均区块间隔时间是多少?

答:平均区块间隔时间为14秒,当然了这只是理论值,你可以在Etherscan

(https://etherscan.io/chart/blocktime)

中查到每日的平均区块时间间隔。

问:以太坊的平均区块大小是多少?

答:区块大小视情况而定,大约是2KB。不同于比特币使用区块大小来规定区块的交易量上限,以太坊使用燃料(gas)限制。燃料限制决定了每个区块中处理的交易量、存储/带宽的上限,因为交易和智能合约中函数的执行都要根据指令的复杂度多少来付出相应的燃料,所以使用燃料限制来约束区块大小是可行的。

燃料限制会随网络的波动和矿工的意愿变化,每个矿工都可以选择自己愿意接受的燃料价格。燃料价格就像是比特币中的交易费,只是这里的价格是最小单位燃料的价格,而不是每笔交易的价格。

想要算出一个区块中可以容纳多少笔交易,你不需要清楚地知道燃料的价格,只需知道平均每笔交易使用多少燃料并用整个燃料限制除以它即可。

去年以太猫的发行造成了以太坊网络的大拥堵,整个网络中充斥着大量未被处理的交易。在这种情况下矿工有两种选择。他们可以投票提高燃料限制来处理更多交易,也可以开始提高自己的燃料价格标准并拒绝处理燃料费用过低的交易。

与比特币一样,即使燃料价格很低的交易也可能会被处理加入区块链中,但矿工肯定会先处理完燃料价格高的交易再处理它。如果你的交易并没有那么紧急,设置一个很低的燃料价格也不是不可以,就像我们现实生活中的“花时间来节省金钱”。

如果有恶意用户持续地发起海量交易堵塞网络,全网的交易成本就会越来越高,直到这个恶意用户用完资金或者矿工赚足了交易费并决定扩大网络容量。

问:以太币是如何产生的?

答:2014年以太坊项目以众筹的形式创建并预售了6000万个以太币,除此之外,矿工挖矿也会生成新的以太币。

问:以太坊中的节点是什么?

答:从本质上来说,节点是一台连接到区块链、可以处理交易的计算机。

问:以太坊都有哪些网络?

答:以太坊共有三种类型的网络:以太坊主链(就是我们平时用的以太坊)、以太坊测试网络(如Ropsten和Rinkeby,供开发人员的学习和测试)和以太坊私有链(也叫以太坊私有网络,任何人都能用以太坊的代码部署自己的私有链)。

问:与以太坊网络交互都哪些方法?

答:你可以使用电子钱包或DApp。

问:你是否能在以太坊中“隐藏”一笔交易?

答:不能。以太坊区块链中所有的交易都是公开可见的。

问:以太坊的交易记录存放在哪里?

答:在公共可见的账本中,这个帐本通常被称为区块链。

问:以太坊主链已经很强大了,为什么还要使用以太坊私有链?

答:原因有很多,主要是因为数据涉及隐私,将数据库去中心化,权限控制和测试。

问:如何查看一笔交易或一个区块的详细信息?

答:你可以使用区块链浏览器,如etherscan.io或live.ether.camp。

问:如何查看私有链中一笔交易或一个区块的详细信息?

答:一些开源的区块链浏览器满足这种需求,如etherparty推出的区块链浏览器

(https://github.com/etherparty/explorer)。

问:区块链的共识过程是什么?

答:共识是按照特定的协议(如以太坊的协议)验证交易,将交易打包进区块并加入区块链的过程。

问:以太坊挖矿操作的工作原理是什么呢?

答:从原理上说,以太坊中的挖矿操作几乎与比特币相同。

format_png

“套路”

简单地说,就是对于每个包含交易的区块,矿工使用计算机反复且非常快速地试验谜题的答案,直到有一个矿工猜对谜题。

更具体地说,矿工将当前区块唯一的区块头元数据(包括时间戳和软件版本)和一个随机数(nonce value)作为哈希函数的输入,函数将返回一个固定长度、看起来像是由数字和字母随机组成的乱码,叫做哈希值。哈希函数的特性是不同输入对应不同的哈希值,因此矿工仅需改变随机数的值,就会得到一个完全不同的哈希值。

如果算出来的哈希值小于当前的目标值(挖矿难度),则这个矿工就挖出了一个区块,他会获得一些以太币奖励,然后通过向全网络广播该区块,其他节点可以验证该区块中的交易,验证通过后将该区块加入到本地区块链的副本中。也就是说,如果矿工B算出了一个哈希值,矿工A将立刻停止当前区块的哈希值计算,把B挖出的区块加入区块链中并开始新一轮的哈希值计算。

矿工们很难在这场算力竞争中作弊。为了得到迷题的答案,除了一个个试没有更好的办法,也没有伪造这些计算工作的可能,这就是该解谜方法被称为“工作量证明”的原因。

从另一方面来说,用户不需要来验证哈希值是否正确,因为每个节点都已验证过。

一般来说,每12到15秒就会有一个矿工挖出一个新区块。如果矿工解谜的时间开始出现更快或更慢的倾向,算法会自动调整问题的难度,以使矿工解谜的时间稳定在14秒左右。

矿工有一定几率能挖到新区块赚取以太币奖励,他们的赚钱能力取决于运气和他们投入的计算能力。

以太坊使用的工作量证明算法被称为“ethash”,它被设计的需要更多内存,从而增大了使用昂贵的ASIC矿机挖矿的难度,因为ASIC矿机的出现严重压榨了使用其他设备矿工的收益,以至于在比特币中唯一能盈利的挖矿形式就是使用这种定制化的芯片。

从某种意义上来说,ethash可能已经成功实现了这一目标,因为专用的ASIC矿机不能用于挖掘以太坊(至少目前还没有)。

此外,由于以太坊将要从工作量证明挖矿逐步过渡到权益证明挖矿,因而购买ASIC矿机可能不是一个明智的选择,因为一旦以太坊转向权益证明它必将被淘汰。

问:区块链中最常用的两种共识协议是什么?

答:工作量证明(PoW)和权益证明(PoS),业界也在不断涌现新的共识协议,比如说活动证明(Proof-of-Activity,PoA)。

问:请简述权益证明的工作原理。

答:权益证明就是根据持有货币的数量和时间随机选择区块的创建者,它不是计算密集型的挖矿形式。

问:以太坊使用哪种共识协议呢?

答:截至2018年初,以太坊使用工作量证明建立共识,但在未来它将转向权益证明。

活动证明在比特币的工作量证明中引入了一种新形式的激励结构,这种混合结构结合了工作量证明和权益证明。为避免系统出现恶性通货膨胀,应该停发新的加密货币,也就是说矿工的挖矿行为不再能拿到挖矿奖励。

这样系统中的加密货币数量就恒定了。在活动证明中,矿工使用工作量证明产生区块,加密货币的持有者通过权益证明签署区块,交易费将按比例分给矿工和签署该块的验证人。

问:签署一笔交易需要用到什么工具?

答:用户的私钥。

问:在私钥丢失后,用户是否还可以恢复以太坊帐户?

答:是的,用户可以使用12字助记词恢复。

问:用什么方法可以连接到以太坊节点?

答:IPC-RPC,JSON-RPC和WS-RPC(RPC指Remote Procedure Call,远程过程调用)。

问:以太坊中异常火爆的Geth是什么呢?

答:Geth是以太坊的一个命令行客户端。

问:连接到Geth客户端的默认方式是什么?

答:默认情况下使用IPC-RPC,禁用其他所有的RPC。

format_png 1

问:Geth客户端中都有哪些API(Application Programming Interface,应用程序编程接口)?

答:Admin(管理员)、 eth(以太币)、web3、miner(矿工)、net(网络)、personal(个人)、shh、debug(调试)和 txpool(工具)。

问:你可以使用哪些RPC通过网络连接到Geth客户端?

答:你可以使用JSON-RPC或WS-RPC通过网络连接到Geth客户端。 IPC-RPC只能用来连接本地部署的Geth客户端。

问:如果你输入命令“—rpc”,启用的是哪一个RPC?

答:JSON-RPC。

问:**默认情况下哪些RPC API是启用的?**

答:eth(以太币)、 web3和net(网络)。

问:如何为JSON-RPC启用admin api?

答:输入命令“—rpcapi”。

问:命令“—datadir”有什么功能?

答:它指定了区块链的存储位置。

问:Geth的“快速”同步是什么,为什么它更快速呢?

答: “快速”同步仅下载收款交易所在的区块,并拉取(pull)整个最近状态数据库,而不是像普通同步一样,下载整个区块链的数据并重放所有发生的交易。

问:命令“—testnet”有什么功能?

答:它将客户端连接到以太坊Ropsten测试网络。

问:启动Geth客户端会在屏幕上打印大量的输出信息,如果不想被这些繁杂信息干扰该怎么办?

答:使用“—verbosity”命令调低输出信息复杂度的值(默认值为3)。

问:如何使用IPC-RPC连接两个Geth客户端?

答:首先启动一个Geth客户端,复制其管道位置(pipe location),然后使用相同的数据文档存储目录(datadir)启动另一个Geth客户端,并使用”—attach”命令传递复制的管道位置。

问:如何将自定义javascript文件加载到Geth控制台?

答:输入”—preload”命令和文件的路径即可。

问:Geth客户端中帐户存储在哪里?

答:存储在密钥库(keystore)目录中。

问:如何使用给定的账户发起一笔交易?

答:首先你要在“—unlock”命令中传入帐户地址或索引来解锁账户。然后你需要使用“--password”命令指定一个此账户的密码文件。

问:我们刚才说到了有关索引的内容。账户的索引取决于什么?

答:取决于你添加帐户的顺序。

问:Geth客户端是否能用来挖矿?

答:是的,输入“—mine”命令即可。

问:挖矿选项中的“etherbase”是什么?

答:这是接受挖矿奖励的帐户地址,该帐户的索引为0。

(2) 智能合约常见问题

问:ABI是什么?

答:简单来说,“ABI”就是低级别的“API”。

ABI(Application Binary Interface)应用程序二进制接口,从本质上讲就是你调用智能合约中的函数并获取它返回值的方式。

ABI中定义了如何调用智能合约中的函数以及应该选用哪种二进制格式将信息从一个程序组件传递到下一个程序组件的详细信息。

以太坊智能合约以字节码的形式部署在以太坊区块链上,一个智能合约中可能会有多个函数。所以说,有了ABI你才可以指定调用智能合约中的哪个函数,才能保证函数的返回值是你期望的格式。

这是一个来自以太坊ABI规范的例子:

  1. contract Foo {
  2. function bar(real[2] xy) {}
  3. function baz(uint32 x, bool y) returns (bool r) { r = x > 32 || y; }
  4. function sam(bytes name, bool z, uint[] data) {}
  5. }

如果我们想使用参数“69”和“真”调用函数baz(),我们总共会传递68字节的数据,整个过程可以分解为:

0xcdcd77c0:baz()函数的ID。这是函数baz(uint32,bool)以ASCII形式编码的Keccak-256哈希值的前4个字节。

0x0000000000000000000000000000000000000000000000000000000000000045

传入函数baz()的第一个参数,32位无符号整数69被填充为32个字节(10进制中的69换算成16进制为45)。

0x00000000000000000000000000000000000000000000000000000000000000000001

传入函数baz()的第二个参数,布尔值真,也就是1,被填充为32个字节。

这68个字节会存放在交易的数据字段(data),需要注意的是,一定要仔细检查交易数据字段中添加的内容,因为在将其传递给智能合约时可能会产生意外的,甚至可能是恶意的副作用。)

为了避免出现生成函数ID时的常见错误,在此过程中必须使用规范的数据类型,就比如说使用标准的256位无符号整型(uint256)而不是无符号整型(uint)。

在Solidity中计算上述sam()函数ID的的代码如下:

  1. bytes4(sha3("sam(bytes,bool,uint256[])")

在这里可以使用诸如web3.js等高级程序库来抽象大部分的细节,不过提供给web3.js的JSON格式ABI是必不可少的。

注意:*ABI是一个抽象,它并不是以太坊核心协议的一部分*。任何人都可以为自己的智能合约定义专属的ABI,这些智能合约的任何调用者都必须遵守该ABI的规定才能得到有意义的调用结果。但是,对于所有开发人员来说,使用Solidity,Serpent和web3.js更为简单,这些也都符合ABI的规定。

问:智能合约是什么?

答:从本质上说,智能合约就是用多种语言编写的计算机代码。智能合约存在于区块链网络上,它们按照自身嵌入的规则执行相关操作,可以看做是参与者之间的契约。

问:智能合约可以用哪种语言编写?

答:最常用的语言是Solidity,除此之外还有一些小众的语言,比如说Serpent和类Lisp语言。

问:智能合约都有什么样的使用场景呢?

答:举一个简单的商品买卖场景,买方在以太坊中部署一个智能合约并向其中存入资金,卖方看到存入的资金后发送商品,买方收到商品后通过智能合约支付货款。这里的智能合约可以看作是一个保障淘宝交易的去中心化的支付宝。

问:什么是MetaMask?

答:Metamask是一款插件类型的以太坊钱包,可以帮助用户在浏览器中与以太坊网络进行交互。

format_png 2

问:Metamask使用什么节点?

答:它使用infura.io。

问:相比于传统以太坊钱包,有哪些功能是Metamask不支持的?

答:它不支持采矿和部署智能合约。

问:智能合约的执行是免费的吗?

答:不,只能通过执行交易来调用智能合约,而交易需要燃料费用。

问:查看智能合约的状态是免费的吗?

答:是的,查询状态不需要执行交易。

问:谁来执行智能合约?

答:矿工。

问:为什么调用智能合约中的函数需要花钱?

答:除了一些不会改变智能合约状态,除了返回值之外没有其他逻辑的函数之外,调用智能合约中的函数都是需要花钱的。

这笔花费中,除了向智能合约中转入以太币执行调用之外,调用改变智能合约状态的函数需要花费燃料来执行。

问:为什么以太坊中要引入燃料费用呢?

答:因为矿工使用自己的计算机(矿机)执行智能合约代码,矿工如果能收回购买机器的成本并获得盈利才能保证整个系统生态的安全性,所以以太坊设计使得矿工可以通过执行调用者请求的代码来赚取燃料费用,从而维持一个健康的生态。

问:燃料价格是否能决定交易被处理的时间?

答:是的,也不全是。一般来说,你支付的燃料价格越高,交易越有可能被加入区块链。尽管如此,燃料价格并不能保证交易更快地被处理。

问:交易中的燃料使用量取决于什么?

答:燃料使用量取决于存储的用量,指令(操作码)的类型和数量。每一条以太坊虚拟机的操作码都明确规定了所需燃料的数量。

问:交易费该如何计算?

答:**交易费 = 使用的燃料数量 * 燃料价格**(燃料价格由交易者指定)。

问:如果执行智能合约的花费少于交易者支付的燃料费用,他是否会获得退款?

答:是的。

问:如果执行智能合约的花费超过了交易者支付的燃料费用,这时会发生什么?

答:用户不会获得退款,并且一旦所有燃料耗尽执行就会停止,智能合约的状态就不会改变。

问:谁来支付调用智能合约的费用?

答:调用智能合约的用户。

问:节点在哪里运行智能合约的代码呢?

答:节点在以太坊虚拟机(EVM)中运行智能合约的代码。以太坊虚拟机规范是以太坊协议的一部分。以太坊虚拟机只是节点运行的一个进程。

问:以太坊虚拟机需要什么工具来运行智能合约?

答:它需要智能合约的字节码,它由高级别语言(如Solidity)编译生成。

问:以太坊虚拟机都包含哪些部分?

答:内存区域,堆栈和执行引擎。

问:Remix是什么?

答:Remix是一个用于开发,测试和部署智能合约的在线工具。它非常适合快速构建和测试轻量级的智能合约,但不适用于复杂的智能合约。

问:在Remix中,你可以连接哪些节点?

答:你可以使用Metamask钱包连接到公共节点,使用Geth钱包连接到本地节点以及使用Javascript虚拟机连接到内存中模拟的节点。

问:什么是DApp,它与普通App有什么不同?

答:应用程序通常包含一个与某些中心化资源(由组织拥有)交互的客户端,通常有一个与中心化数据层相连的中间层。如果中心化数据层中的数据丢失,则无法(轻松)恢复。

DApp表示去中心化的应用程序。DApp通过智能合约与区块链网络交互,它们使用的数据驻留在智能合约的实例中,与中心化的数据相比,去中心化的数据安全性更高。

(3) Solidity常见问题

问:Solidity是静态类型语言(类型的检查是在运行前做的,如编译阶段)还是动态类型语言(类型的检查是在运行时做的)?

答:Solidity是静态类型语言,这意味着类型在编译阶段是已知的。

问:Solidity中的什么结构与Java中的类(Class)相对应?

答:智能合约。

问:智能合约的实例是什么?

答:智能合约的实例就是在区块链上部署的一个智能合约。

问:Java和Solidity之间存在哪些差异。

答:相比于Java,Solidity支持多继承(multiple inheritance),但不支持方法重载(Overloading)。

问:在Solidity文件中需要指定的第一个参数是什么?

答:第一个参数是Solidity编译器的版本,需要指定为^ 0.4.8。不要小看了这一步,因为它可以避免出现在使用其他版本编译器进行编译时引入的不兼容错误。

问:一个智能合约包含什么?

答:智能合约主要由存储变量,函数和事件组成。

问:智能合约中都有哪些类型的函数?

答:有构造函数(constructor),回退函数(fallback function),常量函数(constant functions)和修改智能合约状态的函数。

问:如果我将多个智能合约定义放入单个Solidity文件中,会出现什么样的错误?

答:将多个智能合约定义放入单个Solidity文件中是完全可行的。

问:两个智能合约可以通过哪些方式进行交互?

答:一个智能合约可以调用、创建和继承另一个智能合约。

问:当你尝试部署具有多个智能合约的文件时会发生什么?

答:编译器只会部署该文件中的最后一个智能合约,也就是说,其他所有智能合约都被忽略了。

问:如果我有一个庞大的项目,我是否需要将所有相关的智能合约保存在一个文件中?

答:不需要,你可以使用import语句导入文件,如下所示:

  1. import “./MyOtherContracts.sol”;

问:我是否只能导入本地文件?

答:不,你也可以使用HTTP导入文件(甚至是Github上的文件),如下所示:

  1. import “http://github.com/<owner>/<repo>/<path to the file>”

问:以太坊虚拟机的内存都有哪些部分?

答:存储(Storage)、内存(Memory)和Calldata。

问:请解释一下存储(Storage)。

答:可以把存储想象成一个数据库。每个智能合约都管理自己的存储变量,从而得到一个键值对数据库(256位的键和值)。存储与普通数据库的唯一区别就是,由于存在燃料费用的问题它读取和写入操作的成本更高。

问:请解释一下内存(Memory)。

答:内存是一个临时性的存储。当函数调用执行完毕后,内存中的数据将会被释放。你可以在内存中分配各种复杂的数据类型,如数组和结构体。

问:请解释一下Calldata。

答:Calldata可以理解为一个函数调用堆栈(Callstack)。它是临时的,不可修改的,它存储着以太坊虚拟机的执行数据。

问:存储区和内存区分别存储了哪些变量?

答:状态变量和局部变量(通常局部变量都是对状态变量的引用)位于存储区中,而函数的参数位于内存区中。

问:阅读以下代码,请解释代码的哪一部分对应哪个内存区域:

  1. contract MyContract {
  2. // part 1
  3. uint count;
  4. uint[] totalPoints;
  5. function localVars(){
  6. // part 2
  7. uint[] localArr;
  8. // part 3
  9. uint[] memory memoryArr;
  10. // part 4
  11. uint[] pointer = totalPoints;
  12. }
  13. }

答:第1部分 - 存储。

  1. 第2部分 - 存储(数组大小指向计数器的位置)
  2. 第3部分 – 内存。
  3. 第4部分 – 对存储的引用。

问:我是否可以这样定义一个函数:

  1. function doSomething(uint[] storage args) internal returns(uint[] storage data) {…}

答:是的,你可以强制函数的参数为存储类型。在这种情况下,如果你没有向函数中传入存储引用,编译器将会抱怨。

问:EVM调用和非EVM调用之间有什么区别呢?

答:

  • EVM调用是智能合约中的函数调用,它触发函数执行并需要燃料。

  • 非EVM调用读取公开可见的数据,不需要燃料。

问:如何设定智能合约的以太币余额限制,如果向有余额限制的智能合约中发送超额的以太币会发生什么?

答:

  1. pragma solidity ^0.4.19;
  2. contract yourContract{
  3. uint256 public balanceLimit = 999;
  4. function () payable{
  5. if (this.balance + msg.value > balanceLimit) {
  6. throw;//超额后会回滚。
  7. }
  8. }
  9. }

问:如何在智能合约帐户中设置msg.val的值?

答:*msg.val => msg.value =*

随消息发送的wei的个数。

(4) DApp和web3.0常见问题

问:DApp是什么?

答:去中心化应用程序(DApp)是在点对点网络的计算机上而不是单台计算机上运行的应用程序。去中心化应用程序自点对点网络出现以来就已经存在,它是一种旨在以不受任何实体控制的方式存在于Internet上的软件工具。

  • 去中心化应用程序不一定需要在区块链网络上运行。 BitTorrent(去中心化下载软件)、Popcorn Time(去中心化视频软件)、BitMessage(去中心化通信协议)、Tor(洋葱路由)都是在点对点网络上运行的传统去中心化应用程序,它们都没有在区块链上运行(区块链是一种特殊的点对点网络)。

  • 与简单的智能合约相反,在比特币的经典使用场景,即用户A向用户B汇款,去中心化应用程序的参与者可以是市场中的任意一个人。

问:DApp与智能合约有何不同?

答:去中心化应用程序是一个基于区块链的网站,其中智能合约允许它连接到区块链中。说起来有点绕,为了更好地理解去中心化应用程序和智能合约,让我们先来了解传统网站的工作方式。

  • 传统的Web(网络)应用程序在前端使用HTML,CSS和Javascript来呈现页面,在后端它需要利用API从数据库中获取一些详细信息,比如用户的用户名和头像。当你进入网页版的新浪微博时,该页面将调用API来获取你的个人数据并将其显示在页面上。也就是说,传统网站的工作方式为:前端→API→数据库。
  • 去中心化应用程序类似于传统的Web应用程序。它在前端使用完全相同的技术来呈现页面。一个关键的区别是,去中心化应用程序不使用API来连接到数据库,而是使用智能合约连接到区块链。所以说,基于去中心化应用程序的网站的工作方式为:前端→智能合约→区块链。

在传统的中心化应用程序中,后端代码在中心化的服务器上运行。而去中心化应用程序与之相反,它的后端代码在去中心化的点对点网络上运行。去中心化应用程序包括从后端到前端的整个软件包,智能合约只是去中心化应用程序的一部分。

也就是说,DApp包括:

  • 前端(你能看到的页面),和

  • 后端(应用程序后台的逻辑)。

另一方面,智能合约仅包含后端,通常只是整个DApp的一小部分。这意味着如果你想在智能合约系统上创建去中心化的应用程序,你必须与多个智能合约相结合并依赖第三方系统作为前端。

问:前端使用什么工具连接到后端的智能合约?

答:Web3 API程序库。

问:请列几个你所知道的DApp。

答:Golem(单词含义:傀儡),Golem项目旨在创建业界首个闲置计算机算力的全球市场;Augur(单词含义:预兆),Augur项目旨在将预测市场与去中心化网络相结合,创建一个预测工具,以获得潜在的收益;Aragon Network,它旨在充当一个方便用户的数字化管理组织。

问:你需要什么工具与DApp的智能合约进行交互?

答:需要智能合约的ABI和字节码。

问:ABI的作用是什么?

答:ABI是智能合约公共接口的描述,DApp用这个公共接口来调用智能合约。

问:字节码的作用是什么?

答:每个节点上的以太坊虚拟机都需要智能合约的字节码来运行智能合约。

问:为什么要使用大数运算程序库(BigNumber library)?

答:因为Javascript无法正确处理大数字。

问:为什么要始终检查DApp代码的开头是否设置了web3提供程序(provider)?

答:因为如果不这样,Metamask会用自己的web3提供程序覆盖掉它。

问:为什么使用web3 js的1.x版本而不是0.2x.x版本?

答:主要是因为1.x版本的异步调用使用promise对象(承诺将来会执行,比回调对象更合理和更强大)而不是回调对象,promise对象也是javascript中的首选。

问:在web3 1.x版本中如何列出所有帐户?

答:使用命令:“web3.eth.getAccounts

问:“.call”和“.send”有什么区别?

答:“.send”发起交易并且产生费用,而“.call”仅查询智能合约的状态不产生费用。

问:是否可以通过这样的命令“.send({value:1})”发送一个以太币?

答:不可以,实际上这样你只送了1wei(1以太币 =10^18Wei)。交易中的单位是wei,而不是以太币。

问:那是否意味着,为了发送一个以太币,我需要将值设置为10^18?

答:不用,这样太麻烦。你可以使用util方法,即:

web3.utils.toWei(1,’ether’)

问:调用“.send()”时我需要指定什么?

答:你必须指定其中的发件人地址“from”。其他一切都是可选的。

问:将以太币发送到特定地址的函数是否只有

web3.eth.sendTransaction()一个?

答:不,你还可以调用智能合约中的函数。

问:以太坊的可扩展性问题有什么解决方案?

答:解决可拓展性问题的思路是使用链下交易机制。具体的解决方案有状态通道(state channels)和plasma。

02 面试核心要点梳理

(1) Solidity文件的布局

源文件中可能包含任意数量的智能合约定义,包括指令和编译指示(pragma directives)。

Pragma版本(Version Pragma)

Pragma版本用来声明代码应该使用的Solidity编译器版本。

  1. version pragma ^0.4.00;

注意:有了这条指令,源文件不会使用早于版本0.4.0的编译器进行编译,也不会使用晚于版本0.5.0的编译器进行编译。

导入其他源文件

以太坊的Solidity语言支持与JavaScript非常相似的导入语句(import),不过Solidity中没有“默认导出(default export)”的概念。

在全局级别,你可以使用以下的导入语句:

  1. import "filename";

上述代码将“filename(你所指定的文件名)”中所有全局符号导入到当前的全局范围。

  1. import * as symbolName from "filename";

上述代码创建了一个新的全局符号symbolName,其成员是“filename”中所有的全局符号。

注释

和大多数编程语言一样,Solidity也可以使用单行注释和多行注释。

  1. // 这是一个单行注释。
  2. /*
  3. 这是一个
  4. 多行注释。
  5. */

(2) 以太坊存储区域

在我们进一步深入了解Solidity之前,你应该知道以太坊有三个存储区域。

  1. 存储(Storage):所有智能合约状态变量所在的位置。每个智能合约都有自己的存储,并且只有函数调用才能改变存储中的状态变量。
  2. 内存(Memory):用于保存外部调用的临时数据并在调用结束之后释放数据,相比之下,使用内存中的数据价格更低。
  3. 堆栈(Stack):用于保存小的局部变量。堆栈几乎是免费使用的,但只能保存有限数量的值。

对于几乎所有的类型,你都无法指定它们存储的位置,因为每次使用时它们都会在这三种类型间互相复制。

在了解了以太坊Solidity中的存储位置之后,我们来学习Solidity中的数据类型。

(3) Solidity中的数据类型

以下的数据类型通常也被称为值类型,因为这些类型的数据始终按值传递。

  1. ![format_png 3][]

Solidity中的数据类型

布尔类型

关键词:Bool

布尔类型可能的取值是固定的,即“真”或“假”。

整型

关键字:int / uint(从uint8(8位无符号整型)到uint256(256位无符号整型),从int8(8位整型)到int256(256位整型))

整型就是各种大小的有符号或无符号整数。

我们可以按以下代码来定义整型:

  1. contract MySample{
  2. uint UnsignedInt =50;
  3. }

在上面的代码中,我们创建了一个名为InsignedInt的无符号整型(uint)并将它的值设置为50。

地址类型

关键字:address

地址类型用于保存长度为20字节的值(以太坊地址的长度)。地址类型有其内部的数据成员,是所有智能合约的基础。

地址的数据成员:余额(balance)与转账(transfer)

你可以使用余额属性查询某个地址中的余额,也可以使用转账函数将以太币发送到某个地址,如下所示:

  1. address x = 0x123;
  2. address myAddress = this;
  3. if (x.balance < 10 && myAddress.balance > = 10)
  4. x.transfer(10);

字符串类型

关键字:字符串中的文字通常用双引号或单引号引出,例如“foo”,“bar”。

字符串类型可以是任意长度的UTF数据,如下所示:

  1. string language = "Solidity";

这些值类型可以在包含运算符的表达式中相互交互。接下来,我们就来说说各种运算符。

(4) 运算符

Solidity中的运算符与JavaScript相同,有四种类型的运算符:

  1. ![format_png 4][]

算术运算符

与大多数编程语言类似,Solidity语言中包含一些非常简单的数学运算:

  • 加法:x + y

  • 减法:x - y

  • 乘法:x * y

  • 除法:x / y

  • 求模/求余:x%y

如下所示,Solidity中也支持指数运算符:

  1. uint x = 10 ** 3; // equal to 10^3 = 1000

增量运算符

Solidity中的增量运算符包括:a ++,a - ,++ a,-a,a + = 1,a = a + 1

Solidity中增量运算符的运算规则与其他编程语言类似。

位运算符

Solidity中的位运算符包括:按位或“ | ”,按位异或“ ^ ”,按位取反“ ~ ”,按位右移“ >> ”,按位左移“ << ”。

逻辑运算符

Solidity中的逻辑运算符包括:逻辑非“ ! ”,逻辑与“ && ”,逻辑或“ || ”,相等“ == ”,不相等“ != ”。

用法请看以下示例:

  1. contract operators {
  2. //算数运算符
  3. // +,-,*,/, %, **
  4. // 增量运算符
  5. // a++, a--, a+=1, a=a+1,++a,--a;
  6. a=10;
  7. a= a++; //在这里,输出结果将为10, 因为“a++”运算中先返回a的值再执行自加。
  8. a=++a;//此时输出的结果将为12,因为“++a”运算中先自加再返回a的值。
  9. //逻辑运算符
  10. !, &&, ||, ==, !=
  11. isOwner = true && false;
  12. var orValue= 0x02 | 0x01; // 这里的输出应该是 0x03
  13. //位运算符~,>>, <<;
  14. function Operators() {
  15. // 在这里初始化状态变量}}

在复杂的智能合约中仅仅使用这些数据类型不能满足需求。为此,Solidity还提供了数据结构。

(5) Solidity中的数据结构

Solidity提供三种类型的数据结构:

  1. ![format_png 5][]Solidity中的数据结构

结构体

Solidity提供了一种以结构体定义新数据类型的方法。 结构体是自定义类型,其中可以包含多个变量。我们可以按如下方式定义结构体:

  1. pragma solidity ^0.4.0;
  2. contract Ballot {
  3. struct Voter { // 结构体
  4. uint weight1, weight2, weight3;
  5. bool voted;
  6. address delegate1, delegate2, delegate3, delegate4;
  7. string name;
  8. uint vote1, vote2, vote3, vote4, vote5;
  9. uint height1, height2, height3 } }

注意:结构体类型中最多只能有16个成员,数量超标后可能会发生“堆栈太深(Stack too Deep)”错误。

结构体类型可以用来创建功能更完善、更复杂的数据类型。

结构体类型很强大,但如果要处理多个相同种类的数据,比如说要存储多个以太坊地址,该怎么办呢?与大多数编程语言类似,Solidity中也支持数组。

数组

Solidity中的数组可以是固定长度数组,也可以是动态数组。

  1. uint[3] fixed; //这是一个长度为3的固定长度数组。
  2. uint[] dynamic; //这是一个动态数组,长度随元素的个数变化。

如果数组中的元素为结构体,那么我们可以得到一个结构体数组。如下所示,使用刚才创建的Voter结构体创建一个结构体数组:

  1. Voter[] voting;

注意:将数组声明为公开(public)将自动为其创建getter方法。

  1. Voter[] public voting;

映射

映射可以看作是元素被虚拟初始化的哈希表,即每个键值对中的键被初始化,键映射的值用默认值0代替。

映射的声明方式如下所示:

  1. Mapping(_Keytype => _ValueType )

注意:“_Keytype”可以是除了动态数组,智能合约,枚举类型和结构体类型之外的任何类型。

请看以下示例:

  1. contract MappingExample {
  2. mapping(address => uint) public balances;
  3. function update(uint newBalance) {
  4. balances[msg.sender] = newBalance; }}
  5. contract MappingUser {
  6. function f() returns (uint) {
  7. MappingExample m = new MappingExample();
  8. m.update(100);
  9. return m.balances(this);
  10. }}

(6) 程序控制结构

除了switch和goto之外,Solidity支持JavaScript中的大多数程序控制结构,也就是说Solidity支持以下的程序控制结构:if,else,while,do,for,break,continue,return,? :,它们的用法与C语言和JavaScript中的用法相同。

注意:Solidity不能像C语言和JavaScript那样将非布尔类型数据转换成布尔类型数据。

现在让我们看看如何在Solidity中使用这些程序控制结构:

  1. contract ControlStructure {
  2. address public a;
  3. function ControlStructure>){
  4. // if-else 可以这样使用
  5. if(input1==2)
  6. a=1;
  7. else
  8. a=0;
  9. // while 可以这样使用
  10. while(input1>=0){
  11. if(input1==5)
  12. continue;
  13. input1=input1-1;
  14. a++;}
  15. // for循环可以这样使用
  16. for(uint i=0;i<=50;i++) { a++; if(a==4) break; } //如果使用do while的话,代码为 do { a--; } (while a>0);
  17. // 条件操作可以这样使用
  18. bool IsTrue = (a == 1)?true: false;
  19. /*这将会报错
  20. 因为Solidity不能将非布尔类型数据转换成布尔类型数据
  21. */
  22. if(1)
  23. {
  24. }

将数据类型与程序控制结构相结合就得到了程序的代码。接下来让我们谈谈智能合约中可执行的代码单元,也就是我们刚才提到的函数。

(7) 函数

如何在Solidity中声明一个函数呢?请看以下示例:

  1. function sampleFunc(string name, uint amount) {
  2. }

上面的代码中声明了一个空函数,它有两个参数:一个是字符串和一个是无符号整型(uint)。

你可以通过以下命令调用这个函数:

  1. sampleFunc("Shashank", 10000);

除了函数之外,Solidity中还提供函数修改器(Function Modifiers)。

(8) 函数修改器

函数修改器可以轻易的修改一个函数的功能。只要提前在智能合约的函数定义中声明,即使在执行函数调用之前也可以进行修改。

如果你想要调用一个只有函数的所有者或创建者才能调用的销毁智能合约函数killContract(),代码如下所示:

  1. contract FunctionModifiers{
  2. address public creator;
  3. function FunctionModifiers() {
  4. creator = msg.sender;}
  5. Modifier onlyCreator() {
  6. if(msg.sender!=creator){
  7. throw; }
  8. _; //在函数修改器修改完毕后恢复函数
  9. }
  10. function killContract() onlyCreator{ //不出意外的话,修改后的函数可以正常调用。
  11. self-destruct(creator); }}

(9) 继承

Solidity可以通过复制包含多态(接口的不同实现方式)的代码来支持多继承(一个子类可以有多个父类,它继承了多个父类的特性)。

  1. contract Owned {
  2. address Owner ;
  3. function owned() {
  4. owner = msg.sender;
  5. }}
  6. contract Mortal is Owned { // 'is' 关键词被用来继承。
  7. function kill(){
  8. self-destruct(owner); }}
  9. contract User is Owned, Mortal //多继承
  10. {
  11. string public UserName;
  12. function User(string _name){
  13. UserName = _name;
  14. }}

下一章:如何撤销以太坊交易

经常有用户会想要撤销以太坊的交易,主要是有以下几个原因:将ETH或代币发送到错误的地址;ETH或代币的数量发送错误;交易时间过长;网络交易掉线。1.  取消已上链的以太坊交易由于虚拟货币协议的不可逆性,成功交易 ...