• 以太坊挖矿原理


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

    1.对于每一个块,首先计算一个种子(seed),该种子只和当前块的信息有关;然后根据种子生成一个32M的随机数据集(cache)

    2. 根据Cache生成一个1GB大小的数据集合DAG(有向非循环图),它是一个完整的搜索空间,挖矿的过程就是从DAG中随机选择元素(类似于比特币挖矿中查找合适Nonce)再进行哈希运算,可以从Cache快速计算DAG指定位置的元素,进而哈希验证


    要求对Cache和DAG进行周期性更新,每1000个块更新一次,并且规定DAG的大小随着时间推移线性增长,从1G开始,每年大约增长7G左右。

    为了更好的了解这部分。我们可以简单的看下 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进行挖矿和结果比对,查找是否挖矿成功。

       通过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)
    	)
    

     具体过程如下:

      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.

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

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

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

     


     欢迎大家关注微信号:蜗牛讲技术。扫下面的二维码

  • 相关阅读:
    安装windows系统(win7)
    三大跨平台网盘--dropbox
    三大跨平台网盘--google driver
    三大跨平台网盘--ubuntu one
    ubuntu制作usb启动盘
    高斯滤镜
    gimp之旅
    JAVAOOP集合框架
    JAVAOOP异常
    JAVAOOP多态
  • 原文地址:https://www.cnblogs.com/StephenWu/p/7587134.html
Copyright © 2020-2023  润新知