首先从共识引擎-Engine开始记录

Engine是一个独立于具体算法的共识引擎接口

Author(header) (common.Address, error) 返回打包header对应的区块的矿工地址

VerifyHeader(chain ChainReader, header, seal bool) 验证header是否遵循当前Engine的共识原则。seal代表是否要顺便把VerifySeal做了

VerifyHeaders

VerifyUncles(chain ChainReader, block) error

VerifySeal(chain ChainReader, header) error

Prepare(chain, header) error 为header的共识fields的初始化做准备, The changes are executed incline.

Finalize

(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
        uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)
会话结束后状态的修改

Seal(chain, block,results chan<- *types.Block, stop <-chan struct{}) 生成sealing request并且把结果加入channel中。注意,异步(the method returns immediately and will send the result async),根据共识算法不同,可能获得复数个blocks

SealHash(header) common.Hash 在sealed之前的块的Hash值

CalcDifficulty(chain, time uint64, parent *types.Header) *bigInt 难度调节算法,返回新块的难度

APIs(chain ChainReader)[]rpc.API 返回该共识引擎所提供的RPC APIs

// PoW is a consensus engine based on proof-of-work.
type PoW interface {
    Engine
    // Hashrate returns the current mining hashrate of a PoW consensus engine.
    Hashrate() float64
}
 
引擎的实现是Ethhash算法,Ethash算法是Ethereum1.0提出的PoW算法,也是Dagger-Hashimoto方法用在Ether上的最后一版改编版。(之后可能用PoS代替)
Ethash的设计目的是:
1. IO饱和,以便抵抗ASIC类似机器的挖掘。尽量消耗IO带宽
2. GPU friendliness
3. light client容易验证,能够在0.01s(c语言)内验证一回合的挖矿验证,只占1MB以内的内存
4. light client更慢: light client的速度更慢,就算用特制硬件也要比full node 更慢-对应hashimotolight和hashimotofull,目前看来前者要比后者用到的memory少但是可能需要频繁调用
5. light client fast startup: 在js中用40s以内的时间
基本流程
1. 首先根据当前epoch生成一个seed
2. 通过seed,计算一块伪cache,大小16MB
3. 通过16MBcache,可以生成约1G大小的dataset(或者说DAG)(DAG每30000个blocks完全更新一次)
4. 随机抽取dataset的一部分(2块cache),与当前header的RLP的keccak256hash值做hash。
5. 验证者可以生成那一块cache来验证,耗CPU较少
 6. 不忘记删掉旧的epoch
 
基本参数
 
WORD_BYTES =                     # bytes in word
DATASET_BYTES_INIT = ** # bytes in dataset at genesis
DATASET_BYTES_GROWTH = ** # dataset growth per epoch
CACHE_BYTES_INIT = ** # bytes in cache at genesis
CACHE_BYTES_GROWTH = ** # cache growth per epoch
CACHE_MULTIPLIER= # Size of the DAG relative to the cache
EPOCH_LENGTH = # blocks per epoch
MIX_BYTES = # width of mix
HASH_BYTES = # hash length in bytes
DATASET_PARENTS = # number of parents of each dataset element
CACHE_ROUNDS = # number of rounds in cache production
ACCESSES = # number of accesses in hashimoto loop

cache size和dataset size因为要随着时间增长,所以是CACHE_BYTES_INIT + CACHE_BYTES_GROWTH * (block_number // EPOCH_LENGTH)之下的最大质数

生成mkcache的seed

 def get_seedhash(block):
s = '\x00' * 32
for i in range(block.number // EPOCH_LENGTH):
s = serialize_hash(sha3_256(s))
return s

  

生成cache的方法

注意ETH用的hash是sha3的变体,更接近keccak算法

def mkcache(cache_size, seed):
n = cache_size // HASH_BYTES # Sequentially produce the initial dataset
o = [sha3_512(seed)]
for i in range(, n):
o.append(sha3_512(o[-])) # Use a low-round version of randmemohash
for _ in range(CACHE_ROUNDS):
for i in range(n):
v = o[i][] % n
o[i] = sha3_512(map(xor, o[(i-+n) % n], o[v])) return o

生成dataset的方法

FNV_PRIME = 0x01000193

def fnv(v1, v2):
return ((v1 * FNV_PRIME) ^ v2) % ** def calc_dataset_item(cache, i):
n = len(cache)
r = HASH_BYTES // WORD_BYTES
# initialize the mix
mix = copy.copy(cache[i % n])
mix[] ^= i
mix = sha3_512(mix)
# fnv it with a lot of random cache nodes based on i
for j in range(DATASET_PARENTS):
cache_index = fnv(i ^ j, mix[j % r])
mix = map(fnv, mix, cache[cache_index % n])
return sha3_512(mix)
def calc_dataset(full_size, cache):
return [calc_dataset_item(cache, i) for i in range(full_size // HASH_BYTES)]

算法主体

注意这里的s与seed不要混淆

def hashimoto(header, nonce, full_size, dataset_lookup):
n = full_size / HASH_BYTES
w = MIX_BYTES // WORD_BYTES
mixhashes = MIX_BYTES / HASH_BYTES
# combine header+nonce into a byte seed
s = sha3_512(header + nonce[::-])
# start the mix with replicated s
mix = []
for _ in range(MIX_BYTES / HASH_BYTES):
mix.extend(s)
# mix in random dataset nodes
for i in range(ACCESSES):
p = fnv(i ^ s[], mix[i % w]) % (n // mixhashes) * mixhashes
newdata = []
for j in range(MIX_BYTES / HASH_BYTES):
newdata.extend(dataset_lookup(p + j))
mix = map(fnv, mix, newdata)
# compress mix
cmix = []
for i in range(, len(mix), ):
cmix.append(fnv(fnv(fnv(mix[i], mix[i+]), mix[i+]), mix[i+]))
return {
"mix digest": serialize_hash(cmix),
"result": serialize_hash(sha3_256(s+cmix))
} def hashimoto_light(full_size, cache, header, nonce):
return hashimoto(header, nonce, full_size, lambda x: calc_dataset_item(cache, x)) def hashimoto_full(full_size, dataset, header, nonce):
return hashimoto(header, nonce, full_size, lambda x: dataset[x])
def mine(full_size, dataset, header, difficulty):
# zero-pad target to compare with hash on the same digit when reversed
target = zpad(encode_int(** // difficulty), 64)[::-1]
from random import randint
nonce = randint(, **)
while hashimoto_full(full_size, dataset, header, nonce) > target:
nonce = (nonce + ) % **
return nonce
结构定义
// Ethash is a consensus engine based on proof-of-work implementing the ethash
// algorithm.
type Ethash struct {
config Config caches *lru // In memory caches to avoid regenerating too often
datasets *lru // In memory datasets to avoid regenerating too often // Mining related fields
rand *rand.Rand // Properly seeded random source for nonces
threads int // Number of threads to mine on if mining
update chan struct{} // Notification channel to update mining parameters
hashrate metrics.Meter // Meter tracking the average hashrate // Remote sealer related fields
workCh chan *sealTask // Notification channel to push new work and relative result channel to remote sealer
fetchWorkCh chan *sealWork // Channel used for remote sealer to fetch mining work
submitWorkCh chan *mineResult // Channel used for remote sealer to submit their mining result
fetchRateCh chan chan uint64 // Channel used to gather submitted hash rate for local or remote sealer.
submitRateCh chan *hashrate // Channel used for remote sealer to submit their mining hashrate // The fields below are hooks for testing
shared *Ethash // Shared PoW verifier to avoid cache regeneration
fakeFail uint64 // Block number which fails PoW check even in fake mode
fakeDelay time.Duration // Time delay to sleep for before returning from verify lock sync.Mutex // Ensures thread safety for the in-memory caches and mining fields
closeOnce sync.Once // Ensures exit channel will not be closed twice.
exitCh chan chan error // Notification channel to exiting backend threads
}

Ethash在ethash.go中有以下一些方法:

1. cache

2. dataset

这两个方法先在内存中找,接着去磁盘的DAG中找,最后创建对应数据结构

3. Hashrate

收集本机和网络peer的过去一分钟内search invocation的速率(单位每秒)

SUGAR:

Go non-blocking channel op

如果通道恰好能够收发,那么就对通道做对应操作

    select {
case msg := <-messages:
fmt.Println("received message", msg)
case sig := <-signals:
fmt.Println("received signal", sig)
default:
fmt.Println("no activity")
}

go-ethereum源码分析 PartII 共识算法的更多相关文章

  1. 死磕以太坊源码分析之Kademlia算法

    死磕以太坊源码分析之Kademlia算法 KAD 算法概述 Kademlia是一种点对点分布式哈希表(DHT),它在容易出错的环境中也具有可证明的一致性和性能.使用一种基于异或指标的拓扑结构来路由查询 ...

  2. [dev][ipsec][dpdk] strongswan/dpdk源码分析之ipsec算法配置过程

    1 简述 storngswan的配置里用一种固定格式的字符串设置了用于协商的预定义算法.在包协商过程中strongswan将字符串转换为固定的枚举值封在数据包里用于传输. 协商成功之后,这组被协商选中 ...

  3. Spring Cloud Ribbon 源码分析---负载均衡算法

    上一篇分析了Ribbon如何发送出去一个自带负载均衡效果的HTTP请求,本节就重点分析各个算法都是如何实现. 负载均衡整体是从IRule进去的: public interface IRule{ /* ...

  4. [ethereum源码分析](3) ethereum初始化指令

    前言 在上一章介绍了关于区块链的一些基础知识,这一章会分析指令 geth --datadir dev/data/02 init private-geth/genesis.json 的源码,若你的eth ...

  5. Vue3源码分析之Diff算法

    Diff 算法源码(结合源码写的简易版本) 备注:文章后面有详细解析,先简单浏览一遍整体代码,更容易阅读 // Vue3 中的 diff 算法 // 模拟节点 const { oldVirtualDo ...

  6. Ethereum 源码分析之框架

    accounts 实现了一个高等级的以太坊账户管理     bmt          二进制的默克尔树的实现     build           主要是编译和构建的一些脚本和配置     cmd  ...

  7. [ethereum源码分析](5) 创建新账号

    前言 在上一章我们介绍了 ethereum运行开启console 的过程,那么在这一章我们将会介绍如何在以太坊中创建一个新的账号.以下的理解可能存在错误,如果各位大虾发现错误,还望指正. 指令分析 指 ...

  8. go ethereum源码分析 PartIV Transaction相关

    核心数据结构: core.types.transaction.go type Transaction struct { data txdata // caches hash atomic.Value ...

  9. [ethereum源码分析](4) ethereum运行开启console

    前言 在上一章我们介绍了  ethereum初始化指令 ,包括了系统是如何调用指令和指令的执行.在本章节我们将会介绍 geth --datadir dev/data/ --networkid cons ...

随机推荐

  1. asp.net core 使用 web deploy 部署网站

    1.添加角色和功能中 web服务器(iis)->管理工具->管理服务  打勾并安装 2.安装 web deploy,也可以通过 web平台安装程序 来安装,搜索web deploy就可以了 ...

  2. github收藏夹

    技术面试必备基础知识 https://github.com/CyC2018/CS-Notes 程序员简历模板列表 https://github.com/geekcompany/ResumeSample ...

  3. 数据服务器------sql

    服务器:能够在网站中提供各种(浏览网页,收发邮件视频,语言)等服务器的软件与硬件集合. 数据库服务器(软件):(特点:永久海量存储数据:高速的查询){所有其他软件没有的特点} 数据库服务器分类 网状数 ...

  4. javascript prop和attr的区别

    在网上找了一些资料,通过自己去寻找,尝试,此次做一次总结. 1/  attr()是jQuery 1.0版本就有的函数,    prop()是jQuery 1.6版本新增的函数.毫无疑问,在1.6之前, ...

  5. P4114 Qtree1

    思路 树剖一发,注意对LCA的处理 代码 #include <cstdio> #include <algorithm> #include <cstring> usi ...

  6. React.js小书总结

    (迁移自旧博客2017 08 27) 第一阶段 react的组件相当于MVC里面的View. react.js 将帮助我们将界面分成了各个独立的小块,每一个块就是组件,这些组件之间可以组合.嵌套,就成 ...

  7. 如何恢复已禁用的console.log?

    如何恢复已禁用的console.log? How to Restore a Disabled console.log?通过将其拉出iframe,在已删除的页面(如twitter)上恢复console. ...

  8. 使用Spring MVC实现数据绑定

    使用Spring MVC实现数据绑定 ——Spring MVC支持将表单参数自动绑定在映射方法的参数. ①绑定标量数据 前端代码: <form action="${pageContex ...

  9. 剑指offer 11:二进制中 1 的个数

    题目描述 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 解题代码 法一: public class Solution { public int NumberOf1(int n) { ...

  10. VS的工作目录和输出目录

    C+ +项目,解决方案总文件夹下就只包含解决方案配置文件sln和一个项目总文件夹和一个Debug文件夹以及一个Release文件夹(共四个东东,其中Debug和Release文件夹中存放最终生成的结果 ...