以太坊共识DAG笔记

DAG解析

1.什么是DAG ?

  • DAG,中文名"有向无环图"。"有向"指的是有方向,准确的说应该是同一个方向,"无环"则指够不成闭环。
  • 在DAG中,没有区块的概念,他的组成单元是一笔笔的交易,每个单元记录的是单个用户的交易,这样就省去了打包出块的时间。
  • 验证手段则依赖于后一笔交易对前一笔交易的验证,换句话说,你要想进行一笔交易,就必须要验证前面的交易,具体验证几个交易,根据不同的规则来进行。
  • 这种验证手段,使得DAG可以异步并发的写入很多交易,并最终构成一种拓扑的树状结构,能够极大地提高扩展性。

2.实例

在这里插入图片描述

上图左右这两张图都是DAG。但他们是不一样的。左边这张图是IOTA的"缠结Tangle",使用者每发起一笔交易,需要验证前面两笔交易,后面这张图是普通的DAG,对验证次数没有限制。

3:DAG与区块链相比的优缺点

  • 区块链在保证去中心化和安全性的前提下无法大幅度的提高扩展性,导致难以商业化运用。
  • 而DAG,理论状态下是去中心化的、如果网络足够强大,安全性也可以保证,更重要的是能够大幅度的提高扩展性,采用DAG技术的分布式数据库,起步就可以把TPS做到10万+,还能把交易费用做到极低。
    • DAG缺点:
      • 1:交易时长不可控。
      • 2:不支持强一致性。 区块链同步机制能够保证一致排序,DAG是异步的不能保证一个全局的排序机制
      • 3:安全性还没有得到大规模的验证。
    • 发展方向:
      • DAG技术作为区块链的一个有益补充,其异步通讯机制在提高扩展性、缩短确认时间和降低支付费用方面优势明显

4.以太坊中的DAG挖矿算法—ethash

1.挖矿原理
  • 以太坊的共识机制是 PoW(Proof of Work 工作量证明机制),使用的算法是Ethash,这种算法是对 Dagger-Hashimoto算法的改良版本,流程大概如下 :

    • 1.对于每一个块,首先计算一个种子(seed),该种子只和当前块的信息有关;然后根据种子生成一个32M的随机数据集(cache)。
    • 根据Cache生成一个1GB大小的数据集合DAG(有向非循环图),该数据集是使用Dagger算法生成 。它是一个完整的搜索空间,挖矿的过程就是从DAG中随机选择元素(类似于比特币挖矿中查找合适Nonce)再进行哈希运算,可以从Cache快速计算DAG指定位置的元素,进而哈希验证 。
    • 要求对Cache和DAG进行周期性更新,每1000个块更新一次,并且规定DAG的大小随着时间推移线性增长,从1G开始,每年大约增长7G左右。

2.go-ethereum源码

1. 在 miner.go里调用 New方法生成一个矿工。
/**
   利用区块链创建时候的一些配置,以及共识引擎consensus.Engine等参数先是生成一个矿工,然后
   让矿工注册一个cpu运算引擎,同时通过 update 来监听同步状态并更新挖矿状态
**/
func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine) *Miner {
    miner := &Miner{
        eth:      eth,
        mux:      mux,
        engine:   engine,
        worker:   newWorker(config, engine, common.Address{}, eth, mux),
        canStart: 1,
    }
    miner.Register(NewCpuAgent(eth.BlockChain(), engine))
    go miner.update()
 
    return miner
}
  • 在update方法里有一个需要注意:
case downloader.StartEvent:
            atomic.StoreInt32(&self.canStart, 0)
            if self.Mining() {
                self.Stop()
                atomic.StoreInt32(&self.shouldStart, 1)
                log.Info("Mining aborted due to sync")
            }

可以看到如果当前处于 区块的同步中,则挖矿的操作需要停止,直到同步操作结束(同步成功或是失败),如果原来已经执行了挖矿操作的,则继续开启挖矿操作。

#####2.在 Register方法中调用worker的Agent接口里的Start方法,该方法在agent.go里实现。在agent.go里调用 mine进行挖矿操作。

func (self *CpuAgent) mine(work *Work, stop <-chan struct{}) {
       //调用Seal接口,在sealer.go里实现,进行Ethash算法的实现
    if result, err := self.engine.Seal(self.chain, work.Block, stop); result != nil {
        log.Info("Successfully sealed new block", "number", result.Number(), "hash", result.Hash())
        self.returnCh <- &Result{work, result}
    } else {
        if err != nil {
            log.Warn("Block sealing failed", "err", err)
        }
        self.returnCh <- nil
    }
}
3.在sealer.go的miner进行挖矿和结果比对,查找是否挖矿成功。
  • sealer.go尝试找到一个nonce值能够满足区块难度需求。mine方法主要就是对nonce的操作 。
 func (ethash *Ethash) Seal(chain consensus.ChainReader, block types.Block, stop <-chan struct{}) (types.Block, error) { }
  • 通过256/difficulty 生成一个target值,该值用于后面和计算出来的随机数比较,如果计算出来的随机数比target更小,则挖矿成功。同时通过当前所在的区块号,生成一个完整的dataset。
  var (
            
        //target的计算方法是 256/difficulty 的一个int值
    target = new(big.Int).Div(maxUint256, header.Difficulty)
 
            //当前是第几块
    number  = header.Number.Uint64()
            //生成一个dataset,也就是我们说的搜索或是匹配空间
    dataset = ethash.dataset(number)
)
4.具体流程

1)通过number号得到当前块处于第几个epoch.(每30000个区块为一个epoch,时间窗口为125小时,大约5.2天),通过所在的epoch为索引获取当前内存中是否有dataset

2)如果没有,先会看内存里的总dataset是否大于 dagsinmemory(默认为1),如果大于,则需要把最早的一个dataset删除

3)同时查看是否有pre-generated dataset cache,该数据存在与磁盘空间中。如果有这个数据,并且和当前区块在同一个 epoch. 就用这个pre-generated dataset作为当前dataset.

3)同时查看是否有pre-generated dataset cache,该数据存在与磁盘空间中。如果有这个数据,并且和当前区块在同一个 epoch. 就用这个pre-generated dataset作为当前dataset.

4)如果上述不符合,则重新生成一个dataset. 如果在这个过程中,发现原来pre-generated dataset为空,或是它的epoch和当前所在区块的epoch不一致,则需要用新生成的dataset作为pre-generated dataset,赋值给它

5) 生成dataset后,通过dataset,利用keccak512算法,生成一个1GB大小的数据集合DAG

6) 接下来就是不停循环的利用hashimoto算法(基于Keccak256算法)计算出一个结果值,然后和target进行比较。如果比target小则成功,否则就继续

#####5.hashimoto函数

  • 该函数与hashimotoFull有着相同的愿景:

    在传入的数据集中通过hash和nonce值计算出一个加密值。(digest ,result)

     func hashimoto(hash []byte, nonce uint64, size uint64, lookup func(index uint32) []uint32) ([]byte, []byte) { }
    
6.验证方式

我们的核心计算nonce对应的加密值digest方法hashimoto算法返回了一个digest和一个result两个值,

这行表达式很简单,主要含义就是将result值和target值进行比较,如果小于等于0,即为成功。

new(big.Int).SetBytes(result).Cmp(target) <= 0
  • 那么target是什么?

    target被定义在mine方法体中靠前的变量声明部分,

target = new(big.Int).Div(maxUint256, header.Difficulty)

可以看出,target的定义是根据区块头中的难度值运算而得出的。所以,这就验证了我们最早在概念部分中提到的,我们可以通过调整Difficulty值,来控制pow运算难度,生成正确nonce的难度,达到pow工作量可控的目标。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值