首先从共识引擎-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. 使用Jmeter连接MySql数据库

    准备条件: 1.在MySql中创建数据库和表 2.MySql的驱动jra包 操作步骤: 1.将MySql的驱动jra包放入Jmetre本地文件夹的lib目录下 2.新建一个线程组,在线程组上 添加-配 ...

  2. php中time()与$_SERVER[REQUEST_TIME]用法区别

    简单的说time()与$_SERVER["REQUEST_TIME"]都是获得时间的,但time返回当前的 Unix 时间戳而$_SERVER["REQUEST_TIME ...

  3. js字母/字符与ASCII码转换

    var tempStr="A"; console.log(tempStr.charCodeAt());// 65 ,转ASCII码 console.log(String.fromC ...

  4. CentOS 7 yum install cobbler2.8.3

    安装前注意事项: 1.cobbler主机要为静态ip,否则和dhcpd服务冲突. 2.如果用虚拟机安装,client的内存请设置为2g以上,否则会报错. 3.kickstart文件中不要出现中文,大坑 ...

  5. 安装LNMP笔记

    安装好centos7,主机ssl连不上 网卡设置为桥接模式或NAT模式 防火墙的原因 首先telnet本机22口 先更新yum源 yum update -y 安装网络常用工具:telnet trace ...

  6. javascript 之 函数

    注意:函数名仅仅是一个包含指针的变量而已 函数内部属性 arguments 和this 两个特殊对象 arguments:类数组对象,包含出入函数中的所有参数,主要用途是保存函数参数 callee:该 ...

  7. Linux根目录下各个目录的用途及含义

    Linux根目录下各个目录的用途及含义 Linux ./bin 重要的二进制 (binary) 应用程序,包含二进制文件,系统的所有用户使用的命令都在这个目录下. ./boot 启动 (boot) 配 ...

  8. Linux——高效玩转命令行

    [0]统计文件or压缩文件的行数 zcat file.gz | sed -n '$='   #迅速.直接打印出多少行.-n 取消默认的输出,使用安静(silent)模式    '$='  不知道是什么 ...

  9. Git仓库删除大文件

    Git仓库删除大文件 背景 当用Git久了,难免会手误或临时添加一些大文件到仓库中,即使以后添加进了.gitignore,甚至做了git rm,但是Git为了保证版本可回退,history pack里 ...

  10. Descriptio Resource Path LocationType Archive for required library: 'D:/apache-maven/apache-maven-3.6.0/mavenrepository/org/springframework/spring-aspects/4.3.7.RELEASE/spring-aspects-4.3.7.RELEASE.

    eclipse创建了一个maven项目后,在导入依赖包后返现项目有个红色刺眼的感叹号,再看控制台有个Problem提示 Descriptio Resource Path LocationType Ar ...