// 工作者是负责将消息应用到新状态的主要对象

type worker struct {config      *ConfigchainConfig *params.ChainConfigengine      consensus.Engineeth         Backendchain       *core.BlockChain// FeedspendingLogsFeed event.Feed// Subscriptionsmux          *event.TypeMuxtxsCh        chan core.NewTxsEvent    // 用来接受txPool里面的交易的通道txsSub       event.Subscription          // 用来接受txPool里面的交易的订阅器chainHeadCh  chan core.ChainHeadEvent  // 用来接受区块头的通道chainHeadSub event.SubscriptionchainSideCh  chan core.ChainSideEvent chainSideSub event.Subscription// ChannelsnewWorkCh          chan *newWorkReqtaskCh             chan *taskresultCh           chan *types.BlockstartCh            chan struct{}exitCh             chan struct{}resubmitIntervalCh chan time.DurationresubmitAdjustCh   chan *intervalAdjustcurrent      *environment                 // An environment for current running cycle.localUncles  map[common.Hash]*types.Block // A set of side blocks generated locally as the possible uncle blocks.remoteUncles map[common.Hash]*types.Block // A set of side blocks as the possible uncle blocks.unconfirmed  *unconfirmedBlocks           // A set of locally mined blocks pending canonicalness confirmations.mu       sync.RWMutex // The lock used to protect the coinbase and extra fieldscoinbase common.Address    extra    []bytependingMu    sync.RWMutexpendingTasks map[common.Hash]*tasksnapshotMu    sync.RWMutex // The lock used to protect the block snapshot and state snapshotsnapshotBlock *types.Block     // 快照 BlocksnapshotState *state.StateDB  // 快照 StateDB// atomic status countersrunning int32 // The indicator whether the consensus engine is running or not.newTxs  int32 // New arrival transaction count since last sealing work submitting.// noempty is the flag used to control whether the feature of pre-seal empty// block is enabled. The default value is false(pre-seal is enabled by default).// But in some special scenario the consensus engine will seal blocks instantaneously,// in this case this feature will add all empty blocks into canonical chain// non-stop and no real transaction will be included.noempty uint32// External functionsisLocalBlock func(block *types.Block) bool // Function used to determine whether the specified block is mined by local miner.// Test hooksnewTaskHook  func(*task)                        // Method to call upon receiving a new sealing task.skipSealHook func(*task) bool                   // Method to decide whether skipping the sealing.fullTaskHook func()                             // Method to call before pushing the full sealing task.resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval.
}
func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(*types.Block) bool, init bool) *worker {worker := &worker{config:             config,chainConfig:        chainConfig,engine:             engine,eth:                eth,mux:                mux,chain:              eth.BlockChain(),isLocalBlock:       isLocalBlock,localUncles:        make(map[common.Hash]*types.Block),remoteUncles:       make(map[common.Hash]*types.Block),unconfirmed:        newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth),pendingTasks:       make(map[common.Hash]*task),txsCh:              make(chan core.NewTxsEvent, txChanSize),chainHeadCh:        make(chan core.ChainHeadEvent, chainHeadChanSize),chainSideCh:        make(chan core.ChainSideEvent, chainSideChanSize),newWorkCh:          make(chan *newWorkReq),taskCh:             make(chan *task),resultCh:           make(chan *types.Block, resultQueueSize),exitCh:             make(chan struct{}),startCh:            make(chan struct{}, 1),resubmitIntervalCh: make(chan time.Duration),resubmitAdjustCh:   make(chan *intervalAdjust, resubmitAdjustChanSize),}// Subscribe NewTxsEvent for tx poolworker.txsSub = eth.TxPool().SubscribeNewTxsEvent(worker.txsCh)// Subscribe events for blockchainworker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh)// Sanitize recommit interval if the user-specified one is too short.recommit := worker.config.Recommitif recommit < minRecommitInterval {log.Warn("Sanitizing miner recommit interval", "provided", recommit, "updated", minRecommitInterval)recommit = minRecommitInterval}go worker.mainLoop()go worker.newWorkLoop(recommit)go worker.resultLoop()go worker.taskLoop()// Submit first work to initialize pending state.if init {worker.startCh <- struct{}{}}return worker
}

// newWorkLoop 是一个独立goroutine,用于在接收到的事件上提交新的挖掘工作。

func (w *worker) newWorkLoop(recommit time.Duration) {var (interrupt   *int32minRecommit = recommit // minimal resubmit interval specified by user.timestamp   int64      // timestamp for each round of mining.)timer := time.NewTimer(0)defer timer.Stop()<-timer.C // discard the initial tick// commit使用给定信号中止正在执行的事务,并重新提交一个新的事务。commit := func(noempty bool, s int32) {if interrupt != nil {atomic.StoreInt32(interrupt, s)}interrupt = new(int32)w.newWorkCh <- &newWorkReq{interrupt: interrupt, noempty: noempty, timestamp: timestamp}timer.Reset(recommit)atomic.StoreInt32(&w.newTxs, 0)}//清除挂起的陈旧任务。clearPending := func(number uint64) {w.pendingMu.Lock()for h, t := range w.pendingTasks {if t.block.NumberU64()+staleThreshold <= number {delete(w.pendingTasks, h)}}w.pendingMu.Unlock()}for {select {//提交一个新的任务case <-w.startCh:clearPending(w.chain.CurrentBlock().NumberU64())timestamp = time.Now().Unix()commit(false, commitInterruptNewHead)case head := <-w.chainHeadCh:clearPending(head.Block.NumberU64())timestamp = time.Now().Unix()commit(false, commitInterruptNewHead)case <-timer.C:// If mining is running resubmit a new work cycle periodically to pull in// higher priced transactions. Disable this overhead for pending blocks.if w.isRunning() && (w.chainConfig.Clique == nil || w.chainConfig.Clique.Period > 0) {// Short circuit if no new transaction arrives.if atomic.LoadInt32(&w.newTxs) == 0 {timer.Reset(recommit)continue}commit(true, commitInterruptResubmit)}case interval := <-w.resubmitIntervalCh://由用户显式调整重新提交的时间间隔。if interval < minRecommitInterval {log.Warn("Sanitizing miner recommit interval", "provided", interval, "updated", minRecommitInterval)interval = minRecommitInterval}log.Info("Miner recommit interval update", "from", minRecommit, "to", interval)minRecommit, recommit = interval, intervalif w.resubmitHook != nil {w.resubmitHook(minRecommit, recommit)}case adjust := <-w.resubmitAdjustCh://通过反馈调整重新提交的时间间隔。if adjust.inc {before := recommittarget := float64(recommit.Nanoseconds()) / adjust.ratiorecommit = recalcRecommit(minRecommit, recommit, target, true)log.Trace("Increase miner recommit interval", "from", before, "to", recommit)} else {before := recommitrecommit = recalcRecommit(minRecommit, recommit, float64(minRecommit.Nanoseconds()), false)log.Trace("Decrease miner recommit interval", "from", before, "to", recommit)}if w.resubmitHook != nil {w.resubmitHook(minRecommit, recommit)}case <-w.exitCh:return}}
}

// mainLoop是一个独立的goroutine,根据接收到的事件重新生成密封任务。

func (w *worker) mainLoop() {defer w.txsSub.Unsubscribe()defer w.chainHeadSub.Unsubscribe()defer w.chainSideSub.Unsubscribe()for {select {//从newWorkLoop接受一个新任务case req := <-w.newWorkCh://提交新的任务w.commitNewWork(req.interrupt, req.noempty, req.timestamp)case ev := <-w.chainSideCh:// Short circuit for duplicate side blocksif _, exist := w.localUncles[ev.Block.Hash()]; exist {continue}if _, exist := w.remoteUncles[ev.Block.Hash()]; exist {continue}// Add side block to possible uncle block set depending on the author.if w.isLocalBlock != nil && w.isLocalBlock(ev.Block) {w.localUncles[ev.Block.Hash()] = ev.Block} else {w.remoteUncles[ev.Block.Hash()] = ev.Block}// If our mining block contains less than 2 uncle blocks,// add the new uncle block if valid and regenerate a mining block.if w.isRunning() && w.current != nil && w.current.uncles.Cardinality() < 2 {start := time.Now()if err := w.commitUncle(w.current, ev.Block.Header()); err == nil {var uncles []*types.Headerw.current.uncles.Each(func(item interface{}) bool {hash, ok := item.(common.Hash)if !ok {return false}uncle, exist := w.localUncles[hash]if !exist {uncle, exist = w.remoteUncles[hash]}if !exist {return false}uncles = append(uncles, uncle.Header())return false})w.commit(uncles, nil, true, start)}}case ev := <-w.txsCh://如果不进行挖掘,则将事务应用到挂起状态。//注:所有收到的交易可能不是连续的已经包含在当前挖掘块中。这些交易将自动消除。if !w.isRunning() && w.current != nil {// If block is already full, abortif gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas {continue}w.mu.RLock()coinbase := w.coinbasew.mu.RUnlock()txs := make(map[common.Address]types.Transactions)for _, tx := range ev.Txs {acc, _ := types.Sender(w.current.signer, tx)txs[acc] = append(txs[acc], tx)}txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs)tcount := w.current.tcountw.commitTransactions(txset, coinbase, nil)// Only update the snapshot if any new transactons were added// to the pending blockif tcount != w.current.tcount {w.updateSnapshot()}} else {//如果不进行挖掘,则将事务应用到挂起状态。特殊情况,如果共识引擎为0周期派系(dev模式),//在这里提交挖矿,因为所有的空提交将被拒绝,预先密封(空提交)是禁用的。if w.chainConfig.Clique != nil && w.chainConfig.Clique.Period == 0 {w.commitNewWork(nil, true, time.Now().Unix())}}atomic.AddInt32(&w.newTxs, int32(len(ev.Txs)))......}}
}

如果不进行挖掘,则将事务应用到挂起状态。taskLoop是一个独立的goroutine,用于从生成器获取密封任务并将其推送到共识引擎。

// taskLoop is a standalone goroutine to fetch sealing task from the generator and
// push them to consensus engine.
func (w *worker) taskLoop() {var (stopCh chan struct{}prev   common.Hash)// interrupt aborts the in-flight sealing task.interrupt := func() {if stopCh != nil {close(stopCh)stopCh = nil}}for {select {case task := <-w.taskCh:if w.newTaskHook != nil {w.newTaskHook(task)}//因重新提交而拒绝重复密封工作。sealHash := w.engine.SealHash(task.block.Header())if sealHash == prev {continue}// Interrupt previous sealing operationinterrupt()stopCh, prev = make(chan struct{}), sealHashif w.skipSealHook != nil && w.skipSealHook(task) {continue}w.pendingMu.Lock()w.pendingTasks[sealHash] = taskw.pendingMu.Unlock()if err := w.engine.Seal(w.chain, task.block, w.resultCh, stopCh); err != nil {log.Warn("Block sealing failed", "err", err)}case <-w.exitCh:interrupt()return}}
}

resultLoop是一个独立的goroutine处理密封结果提交并将相关数据刷新到数据库。

func (w *worker) resultLoop() {for {select {case block := <-w.resultCh:// Short circuit when receiving empty result.if block == nil {continue}// Short circuit when receiving duplicate result caused by resubmitting.if w.chain.HasBlock(block.Hash(), block.NumberU64()) {continue}var (sealhash = w.engine.SealHash(block.Header())hash     = block.Hash())w.pendingMu.RLock()task, exist := w.pendingTasks[sealhash]w.pendingMu.RUnlock()if !exist {log.Error("Block found but no relative pending task", "number", block.Number(), "sealhash", sealhash, "hash", hash)continue}// Different block could share same sealhash, deep copy here to prevent write-write conflict.var (receipts = make([]*types.Receipt, len(task.receipts))logs     []*types.Log)for i, receipt := range task.receipts {// add block location fieldsreceipt.BlockHash = hashreceipt.BlockNumber = block.Number()receipt.TransactionIndex = uint(i)receipts[i] = new(types.Receipt)*receipts[i] = *receipt// Update the block hash in all logs since it is now available and not when the// receipt/log of individual transactions were created.for _, log := range receipt.Logs {log.BlockHash = hash}logs = append(logs, receipt.Logs...)}// Commit block and state to database._, err := w.chain.WriteBlockWithState(block, receipts, logs, task.state, true)if err != nil {log.Error("Failed writing block to chain", "err", err)continue}log.Info("Successfully sealed new block", "number", block.Number(), "sealhash", sealhash, "hash", hash,"elapsed", common.PrettyDuration(time.Since(task.createdAt)))// 广播块并宣布链插入事件w.mux.Post(core.NewMinedBlockEvent{Block: block})// 将该块插入到resultLoop中等待的块中以进行确认w.unconfirmed.Insert(block.NumberU64(), block.Hash())case <-w.exitCh:return}}
}

为当前周期创建一个新环境。

func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error {state, err := w.chain.StateAt(parent.Root())if err != nil {return err}env := &environment{signer:    types.NewEIP155Signer(w.chainConfig.ChainID),state:     state,ancestors: mapset.NewSet(),family:    mapset.NewSet(),uncles:    mapset.NewSet(),header:    header,}// when 08 is processed ancestors contain 07 (quick block)for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) {for _, uncle := range ancestor.Uncles() {env.family.Add(uncle.Hash())}env.family.Add(ancestor.Hash())env.ancestors.Add(ancestor.Hash())}// Keep track of transactions which return errors so they can be removedenv.tcount = 0w.current = envreturn nil
}

更新挂起的快照块和状态。注意:此函数假设当前变量是线程安全的。

func (w *worker) updateSnapshot() {w.snapshotMu.Lock()defer w.snapshotMu.Unlock()var uncles []*types.Headerw.current.uncles.Each(func(item interface{}) bool {hash, ok := item.(common.Hash)if !ok {return false}uncle, exist := w.localUncles[hash]if !exist {uncle, exist = w.remoteUncles[hash]}if !exist {return false}uncles = append(uncles, uncle.Header())return false})w.snapshotBlock = types.NewBlock(w.current.header,w.current.txs,uncles,w.current.receipts,new(trie.Trie),)w.snapshotState = w.current.state.Copy()
}
func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt *int32) bool {// Short circuit if current is nilif w.current == nil {return true}if w.current.gasPool == nil {w.current.gasPool = new(core.GasPool).AddGas(w.current.header.GasLimit)}var coalescedLogs []*types.Logfor {//在以下三种情况下,我们将中断事务的执行。//(1)新头块事件到达时,中断信号为1//(2)worker启动或重启时,中断信号为1// (3) worker用任何新到达的事务重新创建挖掘块,中断信号为2。//对于前两种情况,半成品将被丢弃。//对于第三种情况,半成品工作将提交给consensus engine。if interrupt != nil && atomic.LoadInt32(interrupt) != commitInterruptNone {// Notify resubmit loop to increase resubmitting interval due to too frequent commits.if atomic.LoadInt32(interrupt) == commitInterruptResubmit {ratio := float64(w.current.header.GasLimit-w.current.gasPool.Gas()) / float64(w.current.header.GasLimit)if ratio < 0.1 {ratio = 0.1}w.resubmitAdjustCh <- &intervalAdjust{ratio: ratio,inc:   true,}}return atomic.LoadInt32(interrupt) == commitInterruptNewHead}// If we don't have enough gas for any further transactions then we're doneif w.current.gasPool.Gas() < params.TxGas {log.Trace("Not enough gas for further transactions", "have", w.current.gasPool, "want", params.TxGas)break}// Retrieve the next transaction and abort if all donetx := txs.Peek()if tx == nil {break}// Error may be ignored here. The error has already been checked// during transaction acceptance is the transaction pool.//// We use the eip155 signer regardless of the current hf.from, _ := types.Sender(w.current.signer, tx)// Check whether the tx is replay protected. If we're not in the EIP155 hf// phase, start ignoring the sender until we do.if tx.Protected() && !w.chainConfig.IsEIP155(w.current.header.Number) {log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block)txs.Pop()continue}// Start executing the transactionw.current.state.Prepare(tx.Hash(), common.Hash{}, w.current.tcount)logs, err := w.commitTransaction(tx, coinbase)......if !w.isRunning() && len(coalescedLogs) > 0 {// We don't push the pendingLogsEvent while we are mining. The reason is that// when we are mining, the worker will regenerate a mining block every 3 seconds.// In order to avoid pushing the repeated pendingLog, we disable the pending log pushing.// make a copy, the state caches the logs and these logs get "upgraded" from pending to mined// logs by filling in the block hash when the block was mined by the local miner. This can// cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed.cpy := make([]*types.Log, len(coalescedLogs))for i, l := range coalescedLogs {cpy[i] = new(types.Log)*cpy[i] = *l}w.pendingLogsFeed.Send(cpy)}//如果当前间隔较大,通知重新提交循环以减少重新提交的时间间隔而不是用户指定的。if interrupt != nil {w.resubmitAdjustCh <- &intervalAdjust{inc: false}}return false
}
func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) {snap := w.current.state.Snapshot()//尝试将一个事务应用到给定的状态数据库并为其环境使用输入参数。它会返回收据对于事务,使用了gas,如果事务失败则出现错误,表示该块无效。receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig())if err != nil {w.current.state.RevertToSnapshot(snap)return nil, err}w.current.txs = append(w.current.txs, tx)w.current.receipts = append(w.current.receipts, receipt)return receipt.Logs, nil
}

基于父块生成几个新的密封任务。

func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64) {w.mu.RLock()defer w.mu.RUnlock()tstart := time.Now()parent := w.chain.CurrentBlock()if parent.Time() >= uint64(timestamp) {timestamp = int64(parent.Time() + 1)}// this will ensure we're not going off too far in the futureif now := time.Now().Unix(); timestamp > now+1 {wait := time.Duration(timestamp-now) * time.Secondlog.Info("Mining too far in the future", "wait", common.PrettyDuration(wait))time.Sleep(wait)}num := parent.Number()header := &types.Header{ParentHash: parent.Hash(),Number:     num.Add(num, common.Big1),GasLimit:   core.CalcGasLimit(parent, w.config.GasFloor, w.config.GasCeil),Extra:      w.extra,Time:       uint64(timestamp),}// 只有在我们的共识引擎运行时才设置coinbase(避免虚假的块奖励)if w.isRunning() {if w.coinbase == (common.Address{}) {log.Error("Refusing to mine without etherbase")return}header.Coinbase = w.coinbase}if err := w.engine.Prepare(w.chain, header); err != nil {log.Error("Failed to prepare header for mining", "err", err)return}// 如果硬分叉,请检查是否覆盖额外数据if daoBlock := w.chainConfig.DAOForkBlock; daoBlock != nil {// Check whether the block is among the fork extra-override rangelimit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)if header.Number.Cmp(daoBlock) >= 0 && header.Number.Cmp(limit) < 0 {// Depending whether we support or oppose the fork, override differentlyif w.chainConfig.DAOForkSupport {header.Extra = common.CopyBytes(params.DAOForkBlockExtra)} else if bytes.Equal(header.Extra, params.DAOForkBlockExtra) {header.Extra = []byte{} // If miner opposes, don't let it use the reserved extra-data}}}// Could potentially happen if starting to mine in an odd state.err := w.makeCurrent(parent, header)if err != nil {log.Error("Failed to create mining context", "err", err)return}// Create the current work task and check any fork transitions neededenv := w.currentif w.chainConfig.DAOForkSupport && w.chainConfig.DAOForkBlock != nil && w.chainConfig.DAOForkBlock.Cmp(header.Number) == 0 {misc.ApplyDAOHardFork(env.state)}// Accumulate the uncles for the current blockuncles := make([]*types.Header, 0, 2)commitUncles := func(blocks map[common.Hash]*types.Block) {// Clean up stale uncle blocks firstfor hash, uncle := range blocks {if uncle.NumberU64()+staleThreshold <= header.Number.Uint64() {delete(blocks, hash)}}for hash, uncle := range blocks {if len(uncles) == 2 {break}if err := w.commitUncle(env, uncle.Header()); err != nil {log.Trace("Possible uncle rejected", "hash", hash, "reason", err)} else {log.Debug("Committing new uncle to block", "hash", hash)uncles = append(uncles, uncle.Header())}}}// Prefer to locally generated unclecommitUncles(w.localUncles)commitUncles(w.remoteUncles)//根据临时复制的状态创建一个空块提前封口,无需等待封口执行完成。if !noempty && atomic.LoadUint32(&w.noempty) == 0 {w.commit(uncles, nil, false, tstart)}// Fill the block with all available pending transactions.pending, err := w.eth.TxPool().Pending()if err != nil {log.Error("Failed to fetch pending transactions", "err", err)return}// Short circuit if there is no available pending transactions.// But if we disable empty precommit already, ignore it. Since// empty block is necessary to keep the liveness of the network.if len(pending) == 0 && atomic.LoadUint32(&w.noempty) == 0 {w.updateSnapshot()return}// Split the pending transactions into locals and remoteslocalTxs, remoteTxs := make(map[common.Address]types.Transactions), pendingfor _, account := range w.eth.TxPool().Locals() {if txs := remoteTxs[account]; len(txs) > 0 {delete(remoteTxs, account)localTxs[account] = txs}}if len(localTxs) > 0 {txs := types.NewTransactionsByPriceAndNonce(w.current.signer, localTxs)if w.commitTransactions(txs, w.coinbase, interrupt) {return}}if len(remoteTxs) > 0 {txs := types.NewTransactionsByPriceAndNonce(w.current.signer, remoteTxs)if w.commitTransactions(txs, w.coinbase, interrupt) {return}}w.commit(uncles, w.fullTaskHook, true, tstart)
}

计算ETH的总消费费用。区块交易和收据必须有相同的顺序。

func totalFees(block *types.Block, receipts []*types.Receipt) *big.Float {feesWei := new(big.Int)for i, tx := range block.Transactions() {feesWei.Add(feesWei, new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), tx.GasPrice()))}return new(big.Float).Quo(new(big.Float).SetInt(feesWei), new(big.Float).SetInt(big.NewInt(params.Ether)))
}

以太坊源码系列之miner解析(2)相关推荐

  1. 以太坊源码系列之miner解析(1)

    miner用来对worker进行管理, 订阅外部事件,控制worker的启动和停止. type Miner struct {mux *event.TypeMuxworker *workercoinba ...

  2. 以太坊源码分析(2)——以太坊APP对象

    前言 从这一节开始,我将开始以太坊代码全覆盖讲解,讲解的流程是: 以太坊程序入口 基本框架 以太坊协议 发送一笔交易后发生了什么 启动挖矿 以太坊共识 p2p 网络 阅读本系列文章,将默认读者具备一定 ...

  3. 3 v4 中心节点固定_死磕以太坊源码分析之p2p节点发现

    死磕以太坊源码分析之p2p节点发现 在阅读节点发现源码之前必须要理解kadmilia算法,可以参考:KAD算法详解. 节点发现概述 节点发现,使本地节点得知其他节点的信息,进而加入到p2p网络中. 以 ...

  4. 以太坊源码学习(一) 正本清源

    以太坊源码学习(一)正本清源 背景 geth源码一直在不断增加,优化,发展到现在已经非常庞大,第一次看geth源码,会有不小的难度.虽然如此,还是可以从geth仓库的第一个commit开始,这时的代码 ...

  5. 以太坊源码阅读2——RLP编码

    以太坊源码阅读2--RLP编码 RLP介绍 目前网上的资料都是RLP(Recursive Length prefix),叫递归长度前缀编码,但目前源码的doc.go的第20行里面的注释写的是 The ...

  6. 以太坊源码阅读5——POW源码分析

    以太坊源码阅读5--POW源码分析 介绍 POW,proof of work,即工作量证明,是著名公bitcoin所采用的共识算法.简单来说,pow就是一个证明,由矿工使用算力进行计算(挖矿),竞争记 ...

  7. kademlia java_死磕以太坊源码分析之Kademlia算法

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

  8. 以太坊源码学习 -- EVM

    以太坊源码学习 – EVM 学习文档链接:here 一.虚拟机外 主要功能: 执行前将Transaction类型转化成Message,创建虚拟机(EVM)对象,计算一些Gas消耗,以及执行交易完毕后创 ...

  9. 以太坊源码分析-交易

    以太坊源码分析-交易 机理 先说一点区块链转账的基本概念和流程 用户输入转账的地址和转入的地址和转出的金额 系统通过转出的地址的私钥对转账信息进行签名(用于证明这 笔交易确实有本人进行) 系统对交易信 ...

最新文章

  1. WebStorm荣获InfoWorld2014年度科技奖
  2. 计算机科学,我觉得最可靠的排名
  3. java io 结构_java 的IO类库的基本架构
  4. export default (imported as router) was not found_HTC 5G Hub 流动Router 评测分享
  5. ImageLightbox.js – 响应式的图片 Lightbox 插件
  6. QT 开发openSSL CSR证书请求工具
  7. 计算机网络应用平面设计广告设计,互联网时代平面广告设计
  8. 新旧时代的更替——Turbo码/TCM码
  9. 通过授权微信,达到软件登录账号的效果~~未完
  10. lol计算机内存,电脑内存快满了,在玩LOL是弹出内存不足。然后清理了下内存设置了下虚拟内存后电脑出现滴咚的声音并卡机...
  11. DNS 的A记录、CNAME记录、mx记录
  12. 仿京东或淘宝的订单中心页面
  13. 全志V3s学习记录(13)OV2640的使用
  14. 教你如何暴力破解wifii密码
  15. exe4j工具使jar包生成exe可执行文件
  16. matlab使用CVX求解优化问题时,如果变量搜索空间过大,导致求解的数值解相当不准确,通过变量替换,缩小搜索空间
  17. iOS开发-调用手机浏览器打开网址
  18. 芋道源码的周八(2018.04.08)
  19. Docker--cgroup
  20. 北京市通信管理局关于29款问题App的通报

热门文章

  1. 苹果ios14正式版发布有必要升级吗
  2. 大数据风控用了什么模型?有效性如何?
  3. 基址寻址与变址寻址的区别
  4. oracle update indexs,update global indexes的online的程度研究
  5. 关于端午节的网页设计HTML,2017端午节祝福
  6. 【ArchSummit】小红书缓存服务多云建设之路
  7. 【数值分析】学习笔记2——最小线性二乘
  8. 位图索引(Bitmap Index)——位图索引与数据DML锁定
  9. 吴恩达《机器学习》笔记汇总
  10. 【翻】What Is Undo? 2017-11-28