以太坊源码系列之miner解析(2)
// 工作者是负责将消息应用到新状态的主要对象
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)相关推荐
- 以太坊源码系列之miner解析(1)
miner用来对worker进行管理, 订阅外部事件,控制worker的启动和停止. type Miner struct {mux *event.TypeMuxworker *workercoinba ...
- 以太坊源码分析(2)——以太坊APP对象
前言 从这一节开始,我将开始以太坊代码全覆盖讲解,讲解的流程是: 以太坊程序入口 基本框架 以太坊协议 发送一笔交易后发生了什么 启动挖矿 以太坊共识 p2p 网络 阅读本系列文章,将默认读者具备一定 ...
- 3 v4 中心节点固定_死磕以太坊源码分析之p2p节点发现
死磕以太坊源码分析之p2p节点发现 在阅读节点发现源码之前必须要理解kadmilia算法,可以参考:KAD算法详解. 节点发现概述 节点发现,使本地节点得知其他节点的信息,进而加入到p2p网络中. 以 ...
- 以太坊源码学习(一) 正本清源
以太坊源码学习(一)正本清源 背景 geth源码一直在不断增加,优化,发展到现在已经非常庞大,第一次看geth源码,会有不小的难度.虽然如此,还是可以从geth仓库的第一个commit开始,这时的代码 ...
- 以太坊源码阅读2——RLP编码
以太坊源码阅读2--RLP编码 RLP介绍 目前网上的资料都是RLP(Recursive Length prefix),叫递归长度前缀编码,但目前源码的doc.go的第20行里面的注释写的是 The ...
- 以太坊源码阅读5——POW源码分析
以太坊源码阅读5--POW源码分析 介绍 POW,proof of work,即工作量证明,是著名公bitcoin所采用的共识算法.简单来说,pow就是一个证明,由矿工使用算力进行计算(挖矿),竞争记 ...
- kademlia java_死磕以太坊源码分析之Kademlia算法
死磕以太坊源码分析之Kademlia算法 KAD 算法概述 Kademlia是一种点对点分布式哈希表(DHT),它在容易出错的环境中也具有可证明的一致性和性能.使用一种基于异或指标的拓扑结构来路由查询 ...
- 以太坊源码学习 -- EVM
以太坊源码学习 – EVM 学习文档链接:here 一.虚拟机外 主要功能: 执行前将Transaction类型转化成Message,创建虚拟机(EVM)对象,计算一些Gas消耗,以及执行交易完毕后创 ...
- 以太坊源码分析-交易
以太坊源码分析-交易 机理 先说一点区块链转账的基本概念和流程 用户输入转账的地址和转入的地址和转出的金额 系统通过转出的地址的私钥对转账信息进行签名(用于证明这 笔交易确实有本人进行) 系统对交易信 ...
最新文章
- WebStorm荣获InfoWorld2014年度科技奖
- 计算机科学,我觉得最可靠的排名
- java io 结构_java 的IO类库的基本架构
- export default (imported as router) was not found_HTC 5G Hub 流动Router 评测分享
- ImageLightbox.js – 响应式的图片 Lightbox 插件
- QT 开发openSSL CSR证书请求工具
- 计算机网络应用平面设计广告设计,互联网时代平面广告设计
- 新旧时代的更替——Turbo码/TCM码
- 通过授权微信,达到软件登录账号的效果~~未完
- lol计算机内存,电脑内存快满了,在玩LOL是弹出内存不足。然后清理了下内存设置了下虚拟内存后电脑出现滴咚的声音并卡机...
- DNS 的A记录、CNAME记录、mx记录
- 仿京东或淘宝的订单中心页面
- 全志V3s学习记录(13)OV2640的使用
- 教你如何暴力破解wifii密码
- exe4j工具使jar包生成exe可执行文件
- matlab使用CVX求解优化问题时,如果变量搜索空间过大,导致求解的数值解相当不准确,通过变量替换,缩小搜索空间
- iOS开发-调用手机浏览器打开网址
- 芋道源码的周八(2018.04.08)
- Docker--cgroup
- 北京市通信管理局关于29款问题App的通报