(二)以太坊工作原理

2.1 以太坊概览

以太坊是一个去中心化的平台,可以在其上部署Dapp,Dapp用一个或者更多个只能合约创建,使用Solidity编程语言编写只能合约。智能合约完全按照程序运行,而且防停机、防审查、防欺诈、防第三方干扰。

以太坊的内部货币为以太坊,部署智能合约或者调用其方法需要用到以太币。只能合约可以有多个实例,每个实例都有自己专门的地址,用户账户和智能合约都可以持有以太币。

以太坊使用区块链数据结构和工作量证明共识协议。

有两种网络中的节点:普通节点和矿工,普通节点只备份区块链上的数据,而矿工通过挖矿创建区块链。

2.2 以太坊账户

创建以太坊账户,只需要一个非对称加密密钥对 - 由不同算法生成,如RSA、ECC

以太坊使用ECC 椭圆曲线加密算法,它拥有多个参数来调节速度和安全性,以太坊使用secp256k1参数。

以太坊使用256位加密,公钥是一个256位数,被编译成长度为64的十六进制字符串

从公钥生成地址的过程如下:

- 生成公钥的keccak-256哈希,给出一个256位数字
- 丢弃前面的96位,即12个字节,得到160位二进制数据,20字节
- 把地址编译成十六进制字符串,得到一个40字符串的字节串,就是账户地址
2.3 交易

交易是一个签名数据包,用于从一个账户向另一个账户或者向一个合约转以太币、调用合约方法或者部署一个新合约。

交易使用椭圆曲线数字签名算法(ECDSA)签名、ECDSA是一种基于ECC的数字签名算法。

交易包含:

-   信息接受者
-   识别发起人及其意愿的签名
-   要转账的以太币数量
-   交易执行允许进行的计算资源最大值(gas上线)
-   交易发起人愿意为单位计算资源支付的费用(gas价格)
-   输入数据(如果调用合约方法)
-   初始化代码(如果目的是部署合约)

交易费=交易消耗的gas * gas价格

如果确定交易将永久出现在区块链中,则称交易已确认。

2.4 共识

以太坊使用工作量证明共识协议防止区块链被篡改。工作量证明系统需要解决一个复杂问题以创建一个新的区块。

在工作量证明系统中,创建区块的过程称为挖矿,旷工是网络中挖区块的节点。

网络安全不是用矿工总数衡量,而是用网络中的全部算力衡量。

区块链中有多少个区块没有限制,可以生成的以太币总数也没有限制。

为了挖区块,旷工首先从收到的广播中收集新的、未挖处处的交易,然后过滤掉不合法的交易。

合法的交易必须满足正确的使用私钥签名、账户中有足够的余额进行交易等条件

现在矿工创建一个有区块头和内容的区块,内容是区块包含的交易列表。

区块头包含前一个区块的哈希、区块序号、随机数、目标值、难度值、矿工地址等内容。

  • 时间戳表示区块初始时间
  • 随机数是一个没有意义的值,纯粹是为了设置一个小于或等于目标值的区块哈希,以太坊使用ethash哈希算法,发现随机数的唯一方法是穷尽所有可能,目标值是一个256位的数字,根据不同的因素计算。

区块头的难度值是目标值的一种不同表述方法,目标值越低,发现随机数需要的时间越多。目标值越高需要的时间越少。

计算问题难度值的公式如下:

1
current_block_difficulty = previous_block_difficulty + previous_block_difficulty // 2048 * max(1 - (currency_block_timestamp - previous_block_timestamp) // 10, -99) + int(2 ** ((current_block_number // 1000) -  2))

如果网络中的节点接收到两个不同的合法区块链,那么多有取款的整体难度值较高的那个区块链被视为合法的区块链。

2.5 时间戳

计算区块目标值的公式需要用到当前时间戳,且每个区块在区块头附加了当前时间戳。

没有什么机制可以阻止矿工在挖区块时使用其他时间戳(而非当前时间戳)。
时间戳验证失败,其他节点不会接受该区块,这样就浪费了矿工的资源。

当一个矿工广播一个新挖出的区块时,其他节点对其时间戳的验证取决于其时间戳是否大于前一个区块的时间戳。

2.6 随机数

随机数是一个64位未签名证书。随机数是一个问题的解决办法,矿工不断尝试随机数,知道发现目标值。

每个矿工挖的区块的哈希是不同的,因为哈希取决于如时间戳、旷工地址等内容,而且对于所有狂公共来说这些内容很可能是不一样的。因此解决问题并不是一场比赛,而更像是意见碰运气的事

2.7 区块时间

我们看到的区块难度值公式使用了一个长达10s的阈值以确保挖出父区块和子区块的时间差在10~20s因为我们需要有一个相对稳定的生成区块的平均时间,即区块时间。

无效区块:两个矿工同时挖出了一个区块,网络最终会接受难度值较大的那个区块,被放弃的那个区块就是无效区块。

网络中生成的无效块总数与生成新的区块所需的平均时间成反比。难度越简单,区块时间越短,就有可能有更多的矿工挖出矿工,最终会舍弃很多区块。

无效块会影响区块链的安全,网络安全由网络中矿工的全部算力衡量,当算力增加时难度值也要增加,以确保区块不是在平均时间之前生成的

更高的难度值意味着更安全的区块链,因为篡改所需的算力会比多,而且最后不会被接受

以太坊用ghost协议解决无效块带来的安全问题。
ghost协议仅仅把无效块添加到母链上,掩盖了安全问题,由此增加了区块链的整体难度值(包括无效块的难度值之和)。

如何在不产生交易冲突的情况下把无效块添加到母链中呢?
事实上,任何一个区块链都可以接纳零个或者多个无效块

为了激励矿工接纳无效块,矿工接纳无效块也会得到回报,无效块中的交易不需要确认,无效块矿工也不想无效块接纳的交易收取交易费

在以太坊中 无效块称为 叔块

矿工接纳无效块得到的回报计算公式为(uncle_block_number+8-block_number) * 5 / 8

其余回报归侄块即包含孤块的区块。

2.8 分叉

在节点验证区块链发生冲突时,会发生分叉 forking

分叉分为三种:

  • 普通分叉

    由于两个或者多个矿工几乎同时发现了一个区块引起的暂时冲突,如果一个区块的难度高于另一个,这个冲突就解决了。

  • 软分叉

    更改源代码,要求有50%以上算力的矿工升级。例如:更新源代码使得旧区块/交易的一部分失效,有50%以上算力矿工升级后可以解决,这样新的额区块链将有更大的难度值,最后被整个网络接受

  • 硬分叉

    更改源代码引起冲突,全部矿工升级,以解决冲突。例如更改源代码为了个更改对矿工的汇报,全部矿工都需要升级。

    2.9 创世区块

genesis block,区块链中的第一个区块,其区块序号是0。

只有网络中的两个节点有相同的创世区块,他们才会彼此配对进行同步,否则他们将会彼此拒绝。
每一个节点生成自己的创世区块
对于不同的网络,创世区块被硬编到客户端里

2.10 以太币面值
1
2
3
4
5
6
7
8
9
10
11
12
13
1以太币 = 1000000000000000000 wei(18个0 100万千亿)

= 1000000000000000 Kwei

= 1000000000000 Mwei

= 1000000000 Gwei

= 1000000 Szabo

= 1000 Finnery

= 0.01 Kether
2.11 以太坊虚拟机

以太坊虚拟机是以太坊只能合约字节码执行环境,网络中的每个节点都运行EVM,所有节点执行使用EVM指向智能合约的全部交易,因此他们进行同样的计算,并存储同样的数值

出于各种原因,每个节点执行并存储最终状态。

有两种EVM实现

  • 字节码VM
  • JIT-VM
    在两种情况下Solidity代码被编译成字节码,在JIT-VN中,字节码编译更充分,比字节码VM更高效
2.12 gas

计算资源的计量单位。

每个交易包含gas上限和为每个gas支付费用的单价

2.13 发现对等节点

节点不需要连接到网络中的每一个节点,它只连接到几个其他节点,这些节点再连接到另外一个节点,按照这个方式,整个网络彼此连接。

以太坊有自己的节点发现协议可用于解决发现网络中的其他节点。该协议以Kadelima协议为基础。

在节点发现协议中有一个特殊节点,叫做Bootstrap节点,它保存了一段时间内与他们连接的所有的节点的列表,但其本身不保存区块链。

当对等节点连接到以太坊网络时,他们首先连接到BootStrap节点,然后由它连接到他们的对等节点列表,然后对等节点与对等节点连接并同步。

以太坊网络有两种

  • 主网

    用于交易

  • 测试网络

    供开发人员测试

    2.14 Whisper和Swarm

Whisper和Swarm分别是去中心化的通信协议和存储平台,由以太坊开发人员开发。

仍在开发中

2.15 geth

是以太坊、Whisper和Swarm节点的一个实现,可以成为全部实现或者一些选定实现的一部分,合并他们的目的是让他们看起来想一个单一的Dapp,通过一个节点客户端可以访问三个dapp

geth是一种CLI应用,使用Go语言编写,在主要的操作系统中都可以使用。

2.15.1 安装
1
2
3
4
5
6
7
8
brew tap ethereum/erhereum
brew install geth

#ubuntu
sudo apt-get install software-properties-commin
sudo add-apt-repository -y ppa:etherum/ethereum
sudo apt-get update
sudo apt-get install ethereum
2.15.2 JSON-RPC和JavaScript操作台

https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console
geth为其他应用提供了与其通信的JSON-RPC API geth使用HTTP、WebSocket和其他协议服务于JSON-RPC API

JSON-RPC API分为:

  • admin
  • debug
  • eth
  • miner
  • net
  • personal
  • shh
  • txpool
  • web3
    等类型
2.15.3 子命令和选项
  • 连接至主网络

    1
    2
    3
    4
    geth --datadir '/usr/mnt/eth' --networkid 1

    //--datadir 指定在哪里存储区块链默认路径[$HOME/.ethereum]
    //--networkid 指定网络ID 1代表主网络 2代表测试网络,默认为1
  • 创建私有网络

只需要给出一个随机网络ID即可。通常创建私有网络的目的是进行开发,可以简单使用–dev 标记运行一个私有网络

2.15.4 创建账户
1
2
3
4
//创建新账户
geth account new
//获取用户所有列表,秘钥默认存储在--datadir路径中,可以使用--keystore选项指定一个不同的目录
geth account list
  • 挖矿

默认geth不启动挖矿,提供–mine选项就可以进行挖矿,还有一些其他的参数

1
2
3
4
5
6
7
--mine 开始挖矿
--minerthreads 执行哈希过程中使用的线程总数,默认使用8个线程
--etherbase 挖矿赚取的回报存入的地址
#账户默认是加密的,所以要访问账户中的以太币,需要解锁。
--unlock 解锁一个或者多个账户,使用逗号分隔地址可以提供多个地址

geth --mine --minerthreads 16 --etherbase '5415b18cd4802c5bc3ab0c28a40c2f840623a956' --unlock '5415b18cd4802c5bc3ab0c28a40c2f840623a956'
  • 快速同步

不下载整个区块,只下载区块头、交易凭证和最新的状态数据库。

为了检查区块链的真实性,该算法在每一个已定义的区块序号之后下载一个完整的区块
快速同步算法 https://github.com/ethereum/go-ethereum/pull/1889

在运行geth过程中使用–fast即可
fast sync只在初始同步时运行,在节点成功与网络同步之后,fast sync就永远禁用了

2.16 以太坊钱包

以太坊钱包使用IPC与geth通信,geth支持以文件为基础的IPC

2.17 浏览器钱包

浏览器钱包Mist是以太坊、Whisper和Swarm的一个客户端,允许用户发送交易、发送Whisper信息、检查区块链等

2.18 以太坊的缺点
  • Sybil攻击

    攻击者视图用它控制的普通节点占满整个网络,用户很有可能只连接到攻击者节点,一旦连接到攻击值节点,攻击者可以拒绝所有节点转播区块和交易,从而将用户网络中断开,攻击者只能转播他创建的区块,从而会将用户放到分开的网络上。

  • 51%攻击

    如果攻击者掌握了网络中一半以上的算力,他就可以比网络中的其他人更快生成区块,可以保留它的私有分叉,知道分叉比诚实网络创造的更长,然后广播自己的分叉
    还可以重写交易,阻止一些交易被挖出,并阻止其他矿工挖出的区块被添加到区块链中。

    2.19 serenity

以太坊下一个主要更新的名字。这个更新将要求硬分叉,把共识协议改为casper,并将整合状态通道和分片。

2.20 总结