heyuan 发布的文章 - 六币之门
首页
视频教程
网站导航
活动日历
关于我们
用户投稿
推荐
新闻动态
搜 索
1
融资周报 | 公开融资事件11起;加密技术公司Toposware完成500万美元融资,Polygon联创参投
107 阅读
2
六币日报 | 九只比特币ETF在6天内积累了9.5万枚BTC;贝莱德决定停止推出XRP现货ETF计划
74 阅读
3
六币日报 | 美国SEC再次推迟对灰度以太坊期货ETF做出决定;Do Kwon已出黑山监狱等待引渡
69 阅读
4
融资周报 | 公开融资事件27起;L1区块链Monad Labs完成2.25亿美元融资,Paradigm领投
68 阅读
5
【ETH钱包开发06】查询某个地址的交易记录
43 阅读
新闻动态
每日快报
一周精选
融资情况
项目投研
自治组织
数字藏品
去中心化应用
去中心化游戏
去中心化社交
去中心化金融
区块链交易所
科普知识
小白入门
用户手册
开发文档
行业报告
技术前沿
登录
搜 索
标签搜索
新闻
日报
元歌Eden
累计撰写
1,087
篇文章
累计收到
0
条评论
首页
栏目
新闻动态
每日快报
一周精选
融资情况
项目投研
自治组织
数字藏品
去中心化应用
去中心化游戏
去中心化社交
去中心化金融
区块链交易所
科普知识
小白入门
用户手册
开发文档
行业报告
技术前沿
页面
视频教程
网站导航
活动日历
关于我们
用户投稿
推荐
新闻动态
用户登录
登录
找到
1087
篇与
heyuan
相关的结果
2023-03-03
【ETH钱包开发06】查询某个地址的交易记录
问这个问题的朋友挺多的,包括我自己之前也困惑,因为在web3j没有找到合适的API。还好,经过一番研究,找到了一些可行的办法,仅供大家参考,希望大家多多探讨。1、etherscan api提供了查询历史交易记录的接口,返回json。2、用web3j filter过滤器监听交易,将所有的交易数据存储到本地数据库中,需要查询的时候直接从本地数据库中查询。方案一:etherscan API查询http://api.etherscan.io/api?module=account&action=txlist&address=0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae&startblock=0&endblock=99999999&sort=asc&apikey=YourApiKeyToken只能返回最近的1000条交易信息,不过应该够用了吧。或者https://api.etherscan.io/api?module=account&action=txlist&address=0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae&startblock=0&endblock=99999999&page=1&offset=10&sort=asc&apikey=YourApiKeyToken这种方式返回指定个数的交易信息注意:1、module、action、module、sort都是固定值。2、startblock和endblock是指查询2个区块区间的所有指定地址交易,通常startblock为0,endblock设置为"latest"即可。3、apikey需要申请,apikey 是在etherscan调用那些接口都需要用到的一个参数,它是需要你去申请的,注册账号之后就能得到。每个账户最多持有 3 个 token, 请求 API service 服务, 仅需其中一个即可。返回结果方案二:Filter过滤器这种方式需要后端来实现,我在android端试过,会有一些异常。大家可以参考官方文档https://docs.web3j.io/getting_started.html#gradle
2023年03月03日
43 阅读
0 评论
0 点赞
2023-03-03
【ETH智能合约】--05 Solidity 事件Event详解
什么是Evnet事件事件是以太坊虚拟机(EVM)日志基础设施提供的一个接口,事件可以用来做操作记录,存储为日志。如果监听了某事件,当事件发生时,会进行回调。当定义的事件触发时,我们可以将事件存储到EVM的交易的日志中(日志是区块链上的一种特殊数据结构)。日志是与合约地址关联的,并存储在区块链中。注意:区块链上的交易往往会有日志记录,日志代表着智能合约所触发的事件。 不能直接访问日志和事件数据,即使是创建日志的合约。 在Solidity中,使用event 关键字来定义一个事件。event EventName(address bidder, uint amount); 事件在合约中可以被继承。触发一个事件使用emit(之前的版本里并不需要使用emit)emit EventName(msg.sender, msg.value); 触发事件的调用function testEvent() public { // 触发一个事件 emit EventName(msg.sender, msg.value); } 监听事件在Web3.js与智能合约交互中,可以通过Event来实现一些交互功能。接下来一起来看看在之前web3的代码中,加入Event,并触发事件在之前的合约中添加了一个Event事件pragma solidity ^0.4.21; contract InfoContract { string fName; uint age; event Instructor( string name, uint age ); function setInfo(string _fName, uint _age) public { fName = _fName; age = _age; } function getInfo() public constant returns (string, uint) { return (fName, age); } } 然后修改setInfo函数,触发Instructor事件function setInfo(string _fName, uint _age) public { fName = _fName; age = _age; emit Instructor(_fName, _age); } 当调用setInfo函数,函数时触发Instructor事件。现在通过Web3.js监听事件,刷新UI。<script> if (typeof web3 !== 'undefined') { web3 = new Web3(web3.currentProvider); } else { // set the provider you want from Web3.providers web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:7545")); } web3.eth.defaultAccount = web3.eth.accounts[0]; var infoContract = web3.eth.contract(ABI INFO); var info = infoContract.at('CONTRACT ADDRESS'); info.getInfo(function(error, result){ if(!error) { $("#info").html(result[0]+' ('+result[1]+' years old)'); console.log(result); } else console.error(error); }); $("#button").click(function() { info.setInfo($("#name").val(), $("#age").val()); }); </script> 现在,我们改一下代码,通过监听事件获取信息,所以不需要info.getInfo()来获取信息了var instructorEvent = info.Instructor(); instructorEvent.watch(function(error, result) { if (!error) { $("#info").html(result.args.name + ' (' + result.args.age + ' years old)'); } else { console.log(error); } }); 这样修改以后,就可以刷新UI了。参考:https://coursetro.com/posts/code/100/Solidity-Events-Tutorial---Using-Web3.js-to-Listen-for-Smart-Contract-Eventshttps://github.com/ethereum/wiki/wiki/JavaScript-API#contract-events
2023年03月03日
12 阅读
0 评论
0 点赞
2023-03-03
Go-ethereum 源码解析之 miner/worker.go (上)
Go-ethereum 源码解析之 miner/worker.go (上)Source Code// Copyright 2015 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. package miner import ( "bytes" "errors" "math/big" "sync" "sync/atomic" "time" mapset "github.com/deckarep/golang-set" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" ) const ( // resultQueueSize is the size of channel listening to sealing result. resultQueueSize = 10 // txChanSize is the size of channel listening to NewTxsEvent. // The number is referenced from the size of tx pool. txChanSize = 4096 // chainHeadChanSize is the size of channel listening to ChainHeadEvent. chainHeadChanSize = 10 // chainSideChanSize is the size of channel listening to ChainSideEvent. chainSideChanSize = 10 // resubmitAdjustChanSize is the size of resubmitting interval adjustment channel. resubmitAdjustChanSize = 10 // miningLogAtDepth is the number of confirmations before logging successful mining. miningLogAtDepth = 7 // minRecommitInterval is the minimal time interval to recreate the mining block with // any newly arrived transactions. minRecommitInterval = 1 * time.Second // maxRecommitInterval is the maximum time interval to recreate the mining block with // any newly arrived transactions. maxRecommitInterval = 15 * time.Second // intervalAdjustRatio is the impact a single interval adjustment has on sealing work // resubmitting interval. intervalAdjustRatio = 0.1 // intervalAdjustBias is applied during the new resubmit interval calculation in favor of // increasing upper limit or decreasing lower limit so that the limit can be reachable. intervalAdjustBias = 200 * 1000.0 * 1000.0 // staleThreshold is the maximum depth of the acceptable stale block. staleThreshold = 7 ) // environment is the worker's current environment and holds all of the current state information. type environment struct { signer types.Signer state *state.StateDB // apply state changes here ancestors mapset.Set // ancestor set (used for checking uncle parent validity) family mapset.Set // family set (used for checking uncle invalidity) uncles mapset.Set // uncle set tcount int // tx count in cycle gasPool *core.GasPool // available gas used to pack transactions header *types.Header txs []*types.Transaction receipts []*types.Receipt } // task contains all information for consensus engine sealing and result submitting. type task struct { receipts []*types.Receipt state *state.StateDB block *types.Block createdAt time.Time } const ( commitInterruptNone int32 = iota commitInterruptNewHead commitInterruptResubmit ) // newWorkReq represents a request for new sealing work submitting with relative interrupt notifier. type newWorkReq struct { interrupt *int32 noempty bool timestamp int64 } // intervalAdjust represents a resubmitting interval adjustment. type intervalAdjust struct { ratio float64 inc bool } // worker is the main object which takes care of submitting new work to consensus engine // and gathering the sealing result. type worker struct { config *params.ChainConfig engine consensus.Engine eth Backend chain *core.BlockChain gasFloor uint64 gasCeil uint64 // Subscriptions mux *event.TypeMux txsCh chan core.NewTxsEvent txsSub event.Subscription chainHeadCh chan core.ChainHeadEvent chainHeadSub event.Subscription chainSideCh chan core.ChainSideEvent chainSideSub event.Subscription // Channels newWorkCh chan *newWorkReq taskCh chan *task resultCh chan *types.Block startCh chan struct exitCh chan struct resubmitIntervalCh chan time.Duration resubmitAdjustCh chan *intervalAdjust current *environment // An environment for current running cycle. possibleUncles 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 fields coinbase common.Address extra []byte pendingMu sync.RWMutex pendingTasks map[common.Hash]*task snapshotMu sync.RWMutex // The lock used to protect the block snapshot and state snapshot snapshotBlock *types.Block snapshotState *state.StateDB // atomic status counters running int32 // The indicator whether the consensus engine is running or not. newTxs int32 // New arrival transaction count since last sealing work submitting. // Test hooks newTaskHook 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 *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, recommit time.Duration, gasFloor, gasCeil uint64) *worker { worker := &worker{ config: config, engine: engine, eth: eth, mux: mux, chain: eth.BlockChain(), gasFloor: gasFloor, gasCeil: gasCeil, possibleUncles: 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 pool worker.txsSub = eth.TxPool().SubscribeNewTxsEvent(worker.txsCh) // Subscribe events for blockchain worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh) // Sanitize recommit interval if the user-specified one is too short. if 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. worker.startCh <- struct return worker } // setEtherbase sets the etherbase used to initialize the block coinbase field. func (w *worker) setEtherbase(addr common.Address) { w.mu.Lock() defer w.mu.Unlock() w.coinbase = addr } // setExtra sets the content used to initialize the block extra field. func (w *worker) setExtra(extra []byte) { w.mu.Lock() defer w.mu.Unlock() w.extra = extra } // setRecommitInterval updates the interval for miner sealing work recommitting. func (w *worker) setRecommitInterval(interval time.Duration) { w.resubmitIntervalCh <- interval } // pending returns the pending state and corresponding block. func (w *worker) pending() (*types.Block, *state.StateDB) { // return a snapshot to avoid contention on currentMu mutex w.snapshotMu.RLock() defer w.snapshotMu.RUnlock() if w.snapshotState == nil { return nil, nil } return w.snapshotBlock, w.snapshotState.Copy() } // pendingBlock returns pending block. func (w *worker) pendingBlock() *types.Block { // return a snapshot to avoid contention on currentMu mutex w.snapshotMu.RLock() defer w.snapshotMu.RUnlock() return w.snapshotBlock } // start sets the running status as 1 and triggers new work submitting. func (w *worker) start() { atomic.StoreInt32(&w.running, 1) w.startCh <- struct } // stop sets the running status as 0. func (w *worker) stop() { atomic.StoreInt32(&w.running, 0) } // isRunning returns an indicator whether worker is running or not. func (w *worker) isRunning() bool { return atomic.LoadInt32(&w.running) == 1 } // close terminates all background threads maintained by the worker. // Note the worker does not support being closed multiple times. func (w *worker) close() { close(w.exitCh) } // newWorkLoop is a standalone goroutine to submit new mining work upon received events. func (w *worker) newWorkLoop(recommit time.Duration) { var ( interrupt *int32 minRecommit = recommit // minimal resubmit interval specified by user. timestamp int64 // timestamp for each round of mining. ) timer := time.NewTimer(0) <-timer.C // discard the initial tick // commit aborts in-flight transaction execution with given signal and resubmits a new one. commit := func(noempty bool, s int32) { if interrupt != nil { atomic.StoreInt32(interrupt, s) } interrupt = new(int32) w.newWorkCh <- &newWorkReq timer.Reset(recommit) atomic.StoreInt32(&w.newTxs, 0) } // recalcRecommit recalculates the resubmitting interval upon feedback. recalcRecommit := func(target float64, inc bool) { var ( prev = float64(recommit.Nanoseconds()) next float64 ) if inc { next = prev*(1-intervalAdjustRatio) + intervalAdjustRatio*(target+intervalAdjustBias) // Recap if interval is larger than the maximum time interval if next > float64(maxRecommitInterval.Nanoseconds()) { next = float64(maxRecommitInterval.Nanoseconds()) } } else { next = prev*(1-intervalAdjustRatio) + intervalAdjustRatio*(target-intervalAdjustBias) // Recap if interval is less than the user specified minimum if next < float64(minRecommit.Nanoseconds()) { next = float64(minRecommit.Nanoseconds()) } } recommit = time.Duration(int64(next)) } // clearPending cleans the stale pending tasks. 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.config.Clique == nil || w.config.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: // Adjust resubmit interval explicitly by user. 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, interval if w.resubmitHook != nil { w.resubmitHook(minRecommit, recommit) } case adjust := <-w.resubmitAdjustCh: // Adjust resubmit interval by feedback. if adjust.inc { before := recommit recalcRecommit(float64(recommit.Nanoseconds())/adjust.ratio, true) log.Trace("Increase miner recommit interval", "from", before, "to", recommit) } else { before := recommit recalcRecommit(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 is a standalone goroutine to regenerate the sealing task based on the received event. func (w *worker) mainLoop() { defer w.txsSub.Unsubscribe() defer w.chainHeadSub.Unsubscribe() defer w.chainSideSub.Unsubscribe() for { select { case req := <-w.newWorkCh: w.commitNewWork(req.interrupt, req.noempty, req.timestamp) case ev := <-w.chainSideCh: if _, exist := w.possibleUncles[ev.Block.Hash()]; exist { continue } // Add side block to possible uncle block set. w.possibleUncles[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.Header w.current.uncles.Each(func(item interface) bool { hash, ok := item.(common.Hash) if !ok { return false } uncle, exist := w.possibleUncles[hash] if !exist { return false } uncles = append(uncles, uncle.Header()) return false }) w.commit(uncles, nil, true, start) } } case ev := <-w.txsCh: // Apply transactions to the pending state if we're not mining. // // Note all transactions received may not be continuous with transactions // already included in the current mining block. These transactions will // be automatically eliminated. if !w.isRunning() && w.current != nil { w.mu.RLock() coinbase := w.coinbase w.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) w.commitTransactions(txset, coinbase, nil) w.updateSnapshot() } else { // If we're mining, but nothing is being processed, wake on new transactions if w.config.Clique != nil && w.config.Clique.Period == 0 { w.commitNewWork(nil, false, time.Now().Unix()) } } atomic.AddInt32(&w.newTxs, int32(len(ev.Txs))) // System stopped case <-w.exitCh: return case <-w.txsSub.Err(): return case <-w.chainHeadSub.Err(): return case <-w.chainSideSub.Err(): return } } } // 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) } // Reject duplicate sealing work due to resubmitting. sealHash := w.engine.SealHash(task.block.Header()) if sealHash == prev { continue } // Interrupt previous sealing operation interrupt() stopCh, prev = make(chan struct), sealHash if w.skipSealHook != nil && w.skipSealHook(task) { continue } w.pendingMu.Lock() w.pendingTasks[w.engine.SealHash(task.block.Header())] = task w.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 is a standalone goroutine to handle sealing result submitting // and flush relative data to the database. 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 { 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. stat, err := w.chain.WriteBlockWithState(block, receipts, task.state) 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))) // Broadcast the block and announce chain insertion event w.mux.Post(core.NewMinedBlockEvent) var events []interface switch stat { case core.CanonStatTy: events = append(events, core.ChainEvent) events = append(events, core.ChainHeadEvent) case core.SideStatTy: events = append(events, core.ChainSideEvent) } w.chain.PostChainEvents(events, logs) // Insert the block into the set of pending ones to resultLoop for confirmations w.unconfirmed.Insert(block.NumberU64(), block.Hash()) case <-w.exitCh: return } } } // makeCurrent creates a new environment for the current cycle. 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.config.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 removed env.tcount = 0 w.current = env return nil } // commitUncle adds the given block to uncle block set, returns error if failed to add. func (w *worker) commitUncle(env *environment, uncle *types.Header) error { hash := uncle.Hash() if env.uncles.Contains(hash) { return errors.New("uncle not unique") } if env.header.ParentHash == uncle.ParentHash { return errors.New("uncle is sibling") } if !env.ancestors.Contains(uncle.ParentHash) { return errors.New("uncle's parent unknown") } if env.family.Contains(hash) { return errors.New("uncle already included") } env.uncles.Add(uncle.Hash()) return nil } // updateSnapshot updates pending snapshot block and state. // Note this function assumes the current variable is thread safe. func (w *worker) updateSnapshot() { w.snapshotMu.Lock() defer w.snapshotMu.Unlock() var uncles []*types.Header w.current.uncles.Each(func(item interface) bool { hash, ok := item.(common.Hash) if !ok { return false } uncle, exist := w.possibleUncles[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, ) w.snapshotState = w.current.state.Copy() } func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) { snap := w.current.state.Snapshot() receipt, _, err := core.ApplyTransaction(w.config, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, vm.Config) 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) commitTransactions(txs *types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt *int32) bool { // Short circuit if current is nil if w.current == nil { return true } if w.current.gasPool == nil { w.current.gasPool = new(core.GasPool).AddGas(w.current.header.GasLimit) } var coalescedLogs []*types.Log for { // In the following three cases, we will interrupt the execution of the transaction. // (1) new head block event arrival, the interrupt signal is 1 // (2) worker start or restart, the interrupt signal is 1 // (3) worker recreate the mining block with any newly arrived transactions, the interrupt signal is 2. // For the first two cases, the semi-finished work will be discarded. // For the third case, the semi-finished work will be submitted to the 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 done if 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 done tx := 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.config.IsEIP155(w.current.header.Number) { log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.config.EIP155Block) txs.Pop() continue } // Start executing the transaction w.current.state.Prepare(tx.Hash(), common.Hash, w.current.tcount) logs, err := w.commitTransaction(tx, coinbase) switch err { case core.ErrGasLimitReached: // Pop the current out-of-gas transaction without shifting in the next from the account log.Trace("Gas limit exceeded for current block", "sender", from) txs.Pop() case core.ErrNonceTooLow: // New head notification data race between the transaction pool and miner, shift log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) txs.Shift() case core.ErrNonceTooHigh: // Reorg notification data race between the transaction pool and miner, skip account = log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) txs.Pop() case nil: // Everything ok, collect the logs and shift in the next transaction from the same account coalescedLogs = append(coalescedLogs, logs...) w.current.tcount++ txs.Shift() default: // Strange error, discard the transaction and get the next in line (note, the // nonce-too-high clause will prevent us from executing in vain). log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) txs.Shift() } } 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 } go w.mux.Post(core.PendingLogsEvent) } // Notify resubmit loop to decrease resubmitting interval if current interval is larger // than the user-specified one. if interrupt != nil { w.resubmitAdjustCh <- &intervalAdjust } return false } // commitNewWork generates several new sealing tasks based on the parent block. 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().Cmp(new(big.Int).SetInt64(timestamp)) >= 0 { timestamp = parent.Time().Int64() + 1 } // this will ensure we're not going off too far in the future if now := time.Now().Unix(); timestamp > now+1 { wait := time.Duration(timestamp-now) * time.Second log.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.gasFloor, w.gasCeil), Extra: w.extra, Time: big.NewInt(timestamp), } // Only set the coinbase if our consensus engine is running (avoid spurious block rewards) 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 we are care about TheDAO hard-fork check whether to override the extra-data or not if daoBlock := w.config.DAOForkBlock; daoBlock != nil { // Check whether the block is among the fork extra-override range limit := 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 differently if w.config.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 needed env := w.current if w.config.DAOForkSupport && w.config.DAOForkBlock != nil && w.config.DAOForkBlock.Cmp(header.Number) == 0 { misc.ApplyDAOHardFork(env.state) } // Accumulate the uncles for the current block for hash, uncle := range w.possibleUncles { if uncle.NumberU64()+staleThreshold <= header.Number.Uint64() { delete(w.possibleUncles, hash) } } uncles := make([]*types.Header, 0, 2) for hash, uncle := range w.possibleUncles { 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()) } } if !noempty { // Create an empty block based on temporary copied state for sealing in advance without waiting block // execution finished. 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 if len(pending) == 0 { w.updateSnapshot() return } // Split the pending transactions into locals and remotes localTxs, remoteTxs := make(map[common.Address]types.Transactions), pending for _, 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) } // commit runs any post-transaction state modifications, assembles the final block // and commits new work if consensus engine is running. func (w *worker) commit(uncles []*types.Header, interval func(), update bool, start time.Time) error { // Deep copy receipts here to avoid interaction between different tasks. receipts := make([]*types.Receipt, len(w.current.receipts)) for i, l := range w.current.receipts { receipts[i] = new(types.Receipt) *receipts[i] = *l } s := w.current.state.Copy() block, err := w.engine.Finalize(w.chain, w.current.header, s, w.current.txs, uncles, w.current.receipts) if err != nil { return err } if w.isRunning() { if interval != nil { interval() } select { case w.taskCh <- &task: w.unconfirmed.Shift(block.NumberU64() - 1) 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())) } feesEth := new(big.Float).Quo(new(big.Float).SetInt(feesWei), new(big.Float).SetInt(big.NewInt(params.Ether))) log.Info("Commit new mining work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()), "uncles", len(uncles), "txs", w.current.tcount, "gas", block.GasUsed(), "fees", feesEth, "elapsed", common.PrettyDuration(time.Since(start))) case <-w.exitCh: log.Info("Worker has exited") } } if update { w.updateSnapshot() } return nil } Appendix A. 协程批注本附录中用于描述本文件中启动了哪些协程,各自又是通过哪些通道进行消息交互的。同时,对于会被使用到的外部协程也进行了简单的描述。1. 命名协程在对象 miner.worker 的构造函数 newWorker() 中启动新的独立协程运行方法 worker.mainLoop(),不妨将此协程称作命名协程 worker.mainLoop()。 在对象 miner.worker 的构造函数 newWorker() 中启动新的独立协程运行方法 worker.newWorkLoop(recommit),不妨将此协程称作命名协程 worker.newWorkLoop()。 在对象 miner.worker 的构造函数 newWorker() 中启动新的独立协程运行方法 worker.resultLoop(),不妨将此协程称作命名协程 worker.resultLoop()。 在对象 miner.worker 的构造函数 newWorker() 中启动新的独立协程运行方法 worker.taskLoop(),不妨将此协程称作命名协程 worker.taskLoop()。 消息在上述四个命名协程中的流转方向:命名协程 worker.newWorkLoop() 基于接收到的消息向命名协程 worker.mainLoop() 提交事件 miner.newWorkReq,事件的提交最终是在命名协程 worker.newWorkLoop() 的内置函数 commit() 中完成。 命名协程 worker.mainLoop() 基于接收到的消息向命名协程 worker.taskLoop() 提交任务 miner.task,任务的提交最终是在命名协程 worker.mainLoop() 调用的方法 worker.commitNewWork() 和方法 worker.commit() 中完成。 命名协程 worker.taskLoop() 基于接收到的消息向命名协程 worker.resultLoop() 提交已签名区块 types.Block,已签名区块的提交最终是在共识引擎的签名方法 clique.Seal() 的匿名协程中完成。 各命名协程接收消息和发送消息的具体描述:命名协程 worker.newWorkLoop() 从通道 worker.startCh 接收驱动 worker 的开始事件 struct,从通道 worker.chainHeadCh 接收事件 core.ChainHeadEvent,从通道 timer.C 接收事件 time.Time,从通道 worker.resubmitIntervalCh 接收事件 time.Duration,从通道 worker.resubmitAdjustCh 接收事件 intervalAdjust。命名协程 worker.newWorkLoop() 向通道 worker.newWorkCh 发送事件 miner.newWorkReq。 命名协程 worker.mainLoop() 从通道 worker.newWorkCh 接收事件 miner.newWorkReq,从通道 worker.chainSideCh 接收事件 core.ChainSideEvent,从通道 worker.txsCh 接收事件 core.NewTxsEvent。命名协程 worker.mainLoop() 向通道 worker.taskCh 发送事件 miner.task。 命名协程 worker.taskLoop() 从通道 worker.taskCh 接收事件 miner.task。命名协程 worker.taskLoop() 通过共识引擎的签名方法 clique.Seal() 最终向通道 worker.resultCh 发送消息 types.Block。 命名协程 worker.resultLoop() 从通道 worker.resultCh 接收事件 types.Block。命名协程 worker.resultLoop() 通过方法 TypeMux.Post() 将最终的签名区块广播给网络中其它节点,通过 BlockChain.PostChainEvents() 将签名区块及其对应的事件向本地节点的事件订阅者通过 JSON-RPC 的方式发送事件。 2. 匿名协程在共识引擎的签名方法 Cilque.Seal() 中启动了匿名协程,用于将已签名区块发送给通道 worker.resultCh。 在方法 commitTransactions() 中启动一个独立的匿名协程,将所有得到正常处理的交易产生的日志集合通过方法 TypeMux.Post() 发送给订阅者。方法 commitTransactions() 由命名协程 worker.mainLoop() 调用。 Appendix B. 日志信息这些日志记录了关键的流程,同时记录了可能的出错原因。1. 需要重点关注的日志log.Info("Successfully sealed new block", "number", block.Number(), "sealhash", sealhash, "hash", hash, "elapsed", common.PrettyDuration(time.Since(task.createdAt))) log.Trace("Not enough gas for further transactions", "have", w.current.gasPool, "want", params.TxGas) log.Trace("Gas limit exceeded for current block", "sender", from) log.Info("Mining too far in the future", "wait", common.PrettyDuration(wait)) log.Error("Refusing to mine without etherbase") log.Error("Failed to prepare header for mining", "err", err) log.Debug("Committing new uncle to block", "hash", hash) log.Info("Commit new mining work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()), "uncles", len(uncles), "txs", w.current.tcount, "gas", block.GasUsed(), "fees", feesEth, "elapsed", common.PrettyDuration(time.Since(start))) 2. 其它日志log.Warn("Sanitizing miner recommit interval", "provided", recommit, "updated", minRecommitInterval) log.Warn("Sanitizing miner recommit interval", "provided", interval, "updated", minRecommitInterval) log.Info("Miner recommit interval update", "from", minRecommit, "to", interval) log.Trace("Increase miner recommit interval", "from", before, "to", recommit) log.Trace("Decrease miner recommit interval", "from", before, "to", recommit) log.Warn("Block sealing failed", "err", err) log.Error("Block found but no relative pending task", "number", block.Number(), "sealhash", sealhash, "hash", hash) log.Error("Failed writing block to chain", "err", err) log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.config.EIP155Block) log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) log.Error("Failed to create mining context", "err", err) log.Trace("Possible uncle rejected", "hash", hash, "reason", err) log.Error("Failed to fetch pending transactions", "err", err) log.Info("Worker has exited") Appendix C. 总体批注1. const定义了挖矿流程相关的一些常数。特别需要注意两个以共识协议相关的常量 miningLogAtDepth 和 staleThreshold,目前都是 7。可以简单地这样理解,对于给定区块,该区块在经过 miningLogAtDepth 个区块之后被整个链确认。2. type environment struct定义了数据结构 environment,用于描述当前挖矿所需的环境。3. type task struct定义了数据结构 task,用于描述发送给共识引擎进行签名的待签名区块,以及从共识引擎接收的已签名区块。4. const定义了中断相关的一些枚举值,用于描述中断信号。5. type newWorkReq struct定义了数据结构 newWorkReq,用于描述如何开始一个新任务。任务所需的具体信息包含在当前环境 environment 中。6. type intervalAdjust struct定义了数据结构 intervalAdjust,描述重新提交间隔调整所需的参数。同时,需要注意,另外一些参数是通过 timer.Time 定时器提供的。7. type worker struct定义了数据结构 worker。对象 worker 是挖矿的主要实现,启动了多个协程来执行独立的逻辑流程:构建挖矿的当前环境 接收交易 接收已签名区块 提交交易 构建当前正在挖的区块及任务 组装区块头 使用共识引擎设定区块头中的共识相关字段、对整个区块进行最终的签名 将经共识引擎已签名的区块进行广播 构造函数 newWorker() 用于根据给定参数构建 worker。 方法 setEtherbase() 设置用于初始化区块 coinbase 字段的 etherbase。 方法 setExtra() 设置用于初始化区块额外字段的内容。 方法 setRecommitInterval() 更新矿工签名工作重新提交的间隔。 方法 pending() 返回待处理的状态和相应的区块。 方法 pendingBlock() 返回待处理的区块。 方法 start() 采用原子操作将 running 字段置为 1,并触发新工作的提交。 方法 stop() 采用原子操作将 running 字段置为 0。 方法 isRunning() 返回 worker 是否正在运行的指示符。 方法 close() 终止由 worker 维护的所有后台线程。注意 worker 不支持被关闭多次,这是由 Go 语言不允许多次关闭同一个通道决定的。 方法 newWorkLoop() 是一个独立的协程,基于接收到的事件提交新的挖矿工作。不妨将此协程称作命名协程 worker.newWorkLoop()。 方法 mainLoop() 是一个独立的协程,用于根据接收到的事件重新生成签名任务。不妨将此协程称作命名协程 worker.mainLoop()。 方法 taskLoop() 是一个独立的协程,用于从生成器中获取待签名任务,并将它们提交给共识引擎。不妨将此协程称作命名协程 worker.taskLoop()。 方法 resultLoop() 是一个独立的协程,用于处理签名区块的提交和广播,以及更新相关数据到数据库。不妨将此协程称作命名协程 worker.resultLoop()。 方法 makeCurrent() 为当前周期创建新的环境 environment。 方法 commitUncle() 将给定的区块添加至叔区块集合中,如果添加失败则返回错误。 方法 updateSnapshot() 更新待处理区块和状态的快照。注意,此函数确保当前变量是线程安全的。 方法 commitTransactions() 提交交易列表 txs,并附上交易的发起者地址。根据整个交易列表 txs 是否都被有效提交,返回 true 或 false。 方法 commitNewWork() 基于父区块生成几个新的签名任务。 方法 commit() 运行任何交易的后续状态修改,组装最终区块,并在共识引擎运行时提交新工作。 Referencehttps://github.com/ethereum/go-ethereum/blob/master/miner/worker.go ContributorWindstamp, https://github.com/windstamp
2023年03月03日
31 阅读
0 评论
0 点赞
2023-03-03
Go-ethereum 源码解析之重要的数据结构和算法
Go-ethereum 源码解析之重要的数据结构和算法1. 数据结构(1) 哈希32 个字节,256 位。由算法 Keccak-256 计算。文件:go-ethereum/common/types.goconst ( HashLength = 32 ) type Hash [HashLength]byte (2) 地址20 个字节,160 位。由算法 RIPEMD-160 计算。文件:go-ethereum/common/types.goconst ( AddressLength = 20 ) type Address [AddressLength]byte (3) 区块头文件:go-ethereum/core/types/block.gotype Header struct { ParentHash common.Hash `json:"parentHash" gencodec:"required"` UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` Coinbase common.Address `json:"miner" gencodec:"required"` Root common.Hash `json:"stateRoot" gencodec:"required"` TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` Bloom Bloom `json:"logsBloom" gencodec:"required"` Difficulty *big.Int `json:"difficulty" gencodec:"required"` Number *big.Int `json:"number" gencodec:"required"` GasLimit uint64 `json:"gasLimit" gencodec:"required"` GasUsed uint64 `json:"gasUsed" gencodec:"required"` Time *big.Int `json:"timestamp" gencodec:"required"` Extra []byte `json:"extraData" gencodec:"required"` MixDigest common.Hash `json:"mixHash" gencodec:"required"` Nonce BlockNonce `json:"nonce" gencodec:"required"` } (4) 交易文件:go-ethereum/core/types/transaction.gotype Transaction struct { data txdata // caches hash atomic.Value size atomic.Value from atomic.Value } type txdata struct { AccountNonce uint64 `json:"nonce" gencodec:"required"` Price *big.Int `json:"gasPrice" gencodec:"required"` GasLimit uint64 `json:"gas" gencodec:"required"` Recipient *common.Address `json:"to" rlp:"nil"` // nil means contract creation Amount *big.Int `json:"value" gencodec:"required"` Payload []byte `json:"input" gencodec:"required"` // Signature values V *big.Int `json:"v" gencodec:"required"` R *big.Int `json:"r" gencodec:"required"` S *big.Int `json:"s" gencodec:"required"` // This is only used when marshaling to JSON. Hash *common.Hash `json:"hash" rlp:"-"` } (5) 交易回执文件:go-ethereum/core/types/receipt.gotype Receipt struct { // Consensus fields PostState []byte `json:"root"` Status uint64 `json:"status"` CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"` Bloom Bloom `json:"logsBloom" gencodec:"required"` Logs []*Log `json:"logs" gencodec:"required"` // Implementation fields (don't reorder!) TxHash common.Hash `json:"transactionHash" gencodec:"required"` ContractAddress common.Address `json:"contractAddress"` GasUsed uint64 `json:"gasUsed" gencodec:"required"` } (6) 日志项文件:go-ethereum/core/types/log.gotype Log struct { // Consensus fields: // address of the contract that generated the event Address common.Address `json:"address" gencodec:"required"` // list of topics provided by the contract. Topics []common.Hash `json:"topics" gencodec:"required"` // supplied by the contract, usually ABI-encoded Data []byte `json:"data" gencodec:"required"` // Derived fields. These fields are filled in by the node // but not secured by consensus. // block in which the transaction was included BlockNumber uint64 `json:"blockNumber"` // hash of the transaction TxHash common.Hash `json:"transactionHash" gencodec:"required"` // index of the transaction in the block TxIndex uint `json:"transactionIndex" gencodec:"required"` // hash of the block in which the transaction was included BlockHash common.Hash `json:"blockHash"` // index of the log in the receipt Index uint `json:"logIndex" gencodec:"required"` // The Removed field is true if this log was reverted due to a chain reorganisation. // You must pay attention to this field if you receive logs through a filter query. Removed bool `json:"removed"` } (7) 区块文件:go-ethereum/core/types/block.gotype Block struct { header *Header uncles []*Header transactions Transactions // caches hash atomic.Value size atomic.Value // Td is used by package core to store the total difficulty // of the chain up to and including the block. td *big.Int // These fields are used by package eth to track // inter-peer block relay. ReceivedAt time.Time ReceivedFrom interface } 2. 算法(1) keccak256计算 Ethereum-SHA-3(Keccak-256)散列值。也被用来计算 Solidity 中的函数签名(仅使用散列值的前 4 个字节,8 个十六进制数)。(2) sha3keccak256 的别名。(3) sha256计算 SHA-256 散列值。(4) ripemd160计算 RIPEMD-160 散列值。这是非对称加密?公钥和私钥?(5) secp256k1签名算法,生成 65 个字节的签名信息,或者 R, S, V?签名方法:secp256k1.Sign() 恢复签名方法:secp256k1.RecoverPubkey() 【备注】在比特币中,采用非对称加密算法 secp256k1 根据给定的密码求出公钥和私钥,然后对公钥采用 SHA3 家族的散列算法 ripemd160 对公钥进行二次散列,散列为 20 字节,并将散列值作为账户地址。 而在以太坊中,采用非对称加密算法 secp256k1 根据给定的密码求出公钥和私钥,然后对公钥采用 SHA3 家族的散列算法 keccak256 对公钥进行二次散列,散列为 32 字节,将将散列值的前 20 个字节作为账户地址。 3. 重要的概念(1) 区块头哈希区块头哈希是指采用散列算法 keccak256 对区块头中所有数据计算出的散列值。(2) 区块头签名哈希区块头签名哈希是指采用散列算法 keccak256 对区块头中除了额外数据中的最后 65 个字节之外所有数据计算出的散列值。(3) 区块哈希区块哈希,即区块头哈希。(4) 区块签名哈希区块签名哈希即区块头签名哈希。(5) 账户地址 & 以太坊账户地址 & 以太坊账户以太坊账户地址即 common.Address,包含 20 个字节。(6) 智能合约地址 & 以太坊智能合约地址以太坊智能合约地址即 common.Address,包含 20 个字节。在以太坊中账户地址和智能合约地址基本相同,但也有一些显著的差别:账户地址由人操控,或者说由以太坊的外部操控。 智能合约地址由账户地址操控。 (7) 签名者签名者即以太坊账户地址。(8) 签名区块 & 待确定区块签名区块是本地节点最新挖出来的区块,存入本地节点的待确定区块列表,需要等待网络中其它节点的验证。签名区块中,区块头、交易列表、交易回执列表都已经组装完成,并且区块头中也已经包含了签名。Referencehttps://github.com/ethereum/go-ethereum/blob/master/miner/worker.go ContributorWindstamp, https://github.com/windstamp
2023年03月03日
18 阅读
0 评论
0 点赞
2023-03-03
Go-ethereum 源码解析之 go-ethereum/consensus/errors.go
Go-ethereum 源码解析之 go-ethereum/consensus/errors.goSource code// Copyright 2017 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. package consensus import "errors" var ( // ErrUnknownAncestor is returned when validating a block requires an ancestor // that is unknown. ErrUnknownAncestor = errors.New("unknown ancestor") // ErrPrunedAncestor is returned when validating a block requires an ancestor // that is known, but the state of which is not available. ErrPrunedAncestor = errors.New("pruned ancestor") // ErrFutureBlock is returned when a block's timestamp is in the future according // to the current node. ErrFutureBlock = errors.New("block in the future") // ErrInvalidNumber is returned if a block's number doesn't equal it's parent's // plus one. ErrInvalidNumber = errors.New("invalid block number") ) Appendix A. 总体批注本文件中定义了包 consensus 中的通用错误。对于特定的共识引擎 Clique 和 Ethash,其子包中将定义特定共识引擎内部使用的错误。Appendix B. 详细批注1. varErrUnknownAncestor = errors.New("unknown ancestor"):当验证区块需要的一个祖先区块未知时返回 ErrUnknownAncestor。 ErrPrunedAncestor = errors.New("pruned ancestor"):当验证区块需要的一个祖先区块已知但状态不可用时返回 ErrPrunedAncestor。 ErrFutureBlock = errors.New("block in the future"):对于本地节点,如果区块的时间戳在当前时间戳之后,将返回 ErrFutureBlock。 ErrInvalidNumber = errors.New("invalid block number"):如果区块的编号不等于它的父区块的编号加 1,则返回 ErrInvalidNumber。 Referencehttps://github.com/ethereum/go-ethereum/blob/master/consensus/errors.go ContributorWindstamp, https://github.com/windstamp
2023年03月03日
5 阅读
0 评论
0 点赞
2023-03-03
Go-ethereum 源码解析之 go-ethereum/core/types.go
Go-ethereum 源码解析之 go-ethereum/core/types.goSource code// Copyright 2017 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. package core import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" ) // Validator is an interface which defines the standard for block validation. It // is only responsible for validating block contents, as the header validation is // done by the specific consensus engines. // type Validator interface { // ValidateBody validates the given block's content. ValidateBody(block *types.Block) error // ValidateState validates the given statedb and optionally the receipts and // gas used. ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error } // Processor is an interface for processing blocks using a given initial state. // // Process takes the block to be processed and the statedb upon which the // initial state is based. It should return the receipts generated, amount // of gas used in the process and return an error if any of the internal rules // failed. type Processor interface { Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) } Appendix A. 总体批注本文件定义了两个接口。一个是用于验证区块的接口 Validator。另一个是用于处理区块的接口 Processor。需要注意,这些操作都需要用到状态数据库。Appendix B. 详细批注1. type Validator interface接口 Validator 定义了区块验证的标准接口。它只负责验证区块内容,因为区块头验证由特定的共识引擎完成。(1) ValidateBody(block *types.Block) error方法 ValidateBody() 验证给定区块的内容。参数:block *types.Block: 给定区块 返回值:error: 错误消息或 nil (2) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error方法 ValidateState() 验证给定的状态数据库以及可选的交易回执列表和使用的 Gas。参数:block *types.Block: 区块 parent *types.Block: 父区块 state *state.StateDB: 状态数据库 receipts types.Receipts: 交易回执列表 usedGas uint64: 已使用的 Gas 返回值:error: 错误消息或 nil 2. type Processor interface接口 Processor 使用一个给定的初始状态数据库处理区块。(1) Process(block *types.Block, statedb state.StateDB, cfg vm.Config) (types.Receipts, []types.Log, uint64, error)方法 Process() 基于给定的初始状态数据库处理给定的区块。它应该返回生成的交易回执列表,消耗的 Gas,如果任一内部规则失败返回错误。参数:block *types.Block: 区块 statedb *state.StateDB: 状态数据库 cfg vm.Config: EVM 配置信息 返回值:types.Receipts: 交易回执列表 []*types.Log: 日志列表 uint64: 消耗的 Gas error: 错误消息或 nil Referencehttps://github.com/ethereum/go-ethereum/blob/master/core/types.go ContributorWindstamp, https://github.com/windstamp
2023年03月03日
6 阅读
0 评论
0 点赞
2023-03-03
Go-ethereum 源码解析之 go-ethereum/core/events.go
Go-ethereum 源码解析之 go-ethereum/core/events.go// Copyright 2017 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. package core import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" ) // NewTxsEvent is posted when a batch of transactions enter the transaction pool. type NewTxsEvent struct // PendingLogsEvent is posted pre mining and notifies of pending logs. type PendingLogsEvent struct { Logs []*types.Log } // NewMinedBlockEvent is posted when a block has been imported. type NewMinedBlockEvent struct // RemovedLogsEvent is posted when a reorg happens type RemovedLogsEvent struct type ChainEvent struct { Block *types.Block Hash common.Hash Logs []*types.Log } type ChainSideEvent struct { Block *types.Block } type ChainHeadEvent struct Appendix A. 总体批注文件 core/events.go 定义了打包新区块时需要关注的各种事件。这些事件有些是由网络中其它节点向本地节点广播的,有些是由本地节点向网络中其它节点广播的。网络节点向本地节点广播的事件NewTxsEvent 本地节点向网络节点广播的事件ChainHeadEvent ChainSideEvent NewMinedBlockEvent NewTxsEvent Appendix B. 详细批注1. type NewTxsEvent struct事件 NewTxsEvent 在一组交易进入交易池时被发布。Txs []*types.Transaction: 交易列表 2. type PendingLogsEvent struct事件 PendingLogsEvent 在挖矿前发布,并通知待处理日志。3. type NewMinedBlockEvent struct事件 NewMinedBlockEvent 在一个新区块被导入时发布。本地节点在接收到网络中其它节点打包出的新区块时,本地节点触发此事件。4. type RemovedLogsEvent struct事件 RemovedLogsEvent 在重新组织时被发布。5. type ChainEvent struct事件 ChainEvent 表示本地节点挖出了新区块。Block *types.Block: 区块 Hash common.Hash: 区块哈希 Logs []*types.Log: 日志列表 6. type ChainSideEvent struct事件 ChainSideEvent 表示本地节点挖出了叔区块。Block *types.Block: 叔区块 7. type ChainHeadEvent struct事件 ChainHeadEvent 表示本地节点挖出了新区块。Block *types.Block: 区块 Referencehttps://github.com/ethereum/go-ethereum/blob/master/core/events.go ContributorWindstamp, https://github.com/windstamp
2023年03月03日
8 阅读
0 评论
0 点赞
2023-03-03
Go-ethereum 源码解析之 go-ethereum/ethdb/interface.go
Go-ethereum 源码解析之 go-ethereum/ethdb/interface.goSource code// Copyright 2017 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. package ethdb // Code using batches should try to add this much data to the batch. // The value was determined empirically. const IdealBatchSize = 100 * 1024 // Putter wraps the database write operation supported by both batches and regular databases. type Putter interface { Put(key []byte, value []byte) error } // Deleter wraps the database delete operation supported by both batches and regular databases. type Deleter interface { Delete(key []byte) error } // Database wraps all database operations. All methods are safe for concurrent use. type Database interface { Putter Deleter Get(key []byte) ([]byte, error) Has(key []byte) (bool, error) Close() NewBatch() Batch } // Batch is a write-only database that commits changes to its host database // when Write is called. Batch cannot be used concurrently. type Batch interface { Putter Deleter ValueSize() int // amount of data in the batch Write() error // Reset resets the batch for reuse Reset() } Appendix A. 总体批注此文件描述了包 ethdb 提供的基本接口。Appendix B. 详细批注1. const IdealBatchSize = 100 * 1024使用批处理的代码应该尝试将这么多数据添加到批处理中。该值是根据经验确定的。2. type Putter interface接口 Putter 包装了批处理和常规数据库支持的数据库写入操作。2.1 Put(key []byte, value []byte) error方法 Put() 存储给定的 key 和 value 到数据库。参数:key []byte: key value []byte: value 3. type Deleter interface接口 Deleter 包装批处理和常规数据库支持的数据库删除操作。3.1 Delete(key []byte) error方法 Delete() 从数据库中删除给定的 key。4. type Database interface接口 Database 包装所有数据库操作。所有方法都能够安全地支持并发。4.1 Putter继承了接口 Putter。提供了方法 Put()。4.2 Deleter继承了接口 Deleter。提供了方法 Delete()。4.3 Get(key []byte) ([]byte, error)方法 Get() 从数据库中获取 key 对应的 value。参数:key []byte: key 返回值[]byte: value error: 错误消息或 nil 4.4 Has(key []byte) (bool, error)方法 Has() 查询给定 key 是否存在于数据库中。参数:key []byte: key 返回值bool: 存在为 true,不存在则为 false error: 错误消息或 nil 5. type Batch interface接口 Batch 是一个只写数据库,在调用方法 Write() 时会将更改提交到其主机数据库。批处理不支付并发。5.1 Putter继承了接口 Putter。提供了方法 Put()。5.2 Deleter继承了接口 Deleter。提供了方法 Delete()。5.3 ValueSize() int方法 ValueSize() 返回批处理中的数据量。5.4 Write() error方法 Write() 将更改提交到其主机数据库。5.5 Reset()方法 Reset() 重置批处理以便重复使用。Referencehttps://github.com/ethereum/go-ethereum/blob/master/ethdb/interface.go ContributorWindstamp, https://github.com/windstamp
2023年03月03日
7 阅读
0 评论
0 点赞
2023-03-03
Go-ethereum 源码解析之 go-ethereum/ethdb/memory_database.go
Go-ethereum 源码解析之 go-ethereum/ethdb/memory_database.goSource code// Copyright 2017 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. package ethdb import ( "errors" "sync" "github.com/ethereum/go-ethereum/common" ) /* * This is a test memory database. Do not use for any production it does not get persisted */ type MemDatabase struct { db map[string][]byte lock sync.RWMutex } func NewMemDatabase() *MemDatabase { return &MemDatabase{ db: make(map[string][]byte), } } func NewMemDatabaseWithCap(size int) *MemDatabase { return &MemDatabase{ db: make(map[string][]byte, size), } } func (db *MemDatabase) Put(key []byte, value []byte) error { db.lock.Lock() defer db.lock.Unlock() db.db[string(key)] = common.CopyBytes(value) return nil } func (db *MemDatabase) Has(key []byte) (bool, error) { db.lock.RLock() defer db.lock.RUnlock() _, ok := db.db[string(key)] return ok, nil } func (db *MemDatabase) Get(key []byte) ([]byte, error) { db.lock.RLock() defer db.lock.RUnlock() if entry, ok := db.db[string(key)]; ok { return common.CopyBytes(entry), nil } return nil, errors.New("not found") } func (db *MemDatabase) Keys() [][]byte { db.lock.RLock() defer db.lock.RUnlock() keys := [][]byte for key := range db.db { keys = append(keys, []byte(key)) } return keys } func (db *MemDatabase) Delete(key []byte) error { db.lock.Lock() defer db.lock.Unlock() delete(db.db, string(key)) return nil } func (db *MemDatabase) Close() func (db *MemDatabase) NewBatch() Batch { return &memBatch } func (db *MemDatabase) Len() int type kv struct { k, v []byte del bool } type memBatch struct { db *MemDatabase writes []kv size int } func (b *memBatch) Put(key, value []byte) error { b.writes = append(b.writes, kv) b.size += len(value) return nil } func (b *memBatch) Delete(key []byte) error { b.writes = append(b.writes, kv) b.size += 1 return nil } func (b *memBatch) Write() error { b.db.lock.Lock() defer b.db.lock.Unlock() for _, kv := range b.writes { if kv.del { delete(b.db.db, string(kv.k)) continue } b.db.db[string(kv.k)] = kv.v } return nil } func (b *memBatch) ValueSize() int { return b.size } func (b *memBatch) Reset() { b.writes = b.writes[:0] b.size = 0 } Appendix A. 总体批注实现了一个内存数据库 MemDatabase 用于测试环境,但不能将其用于生产环境。ethdb.MemDatabase 实现了接口 ethdb.Database。ethdb.memBatch 在 ethdb.MemDatabase 的基础上提供了批处理能力。这里将基于接口编程的思想展现的淋漓尽致。Appendix B. 详细批注1. type MemDatabase struct数据结构 MemDatabase 是一个测试内存数据库。不要将其用于任何生产环境,因为它不会被持久化。db map[string][]byte: key-value 对? lock sync.RWMutex: 锁 1.1 func NewMemDatabase() *MemDatabase构造函数 NewMemDatabase() 创建对象 MemDatabase,并使用默认值初始化。1.2 func NewMemDatabaseWithCap(size int) *MemDatabase构造函数 NewMemDatabaseWithCap() 创建对象 MemDatabase,并设定 db 的大小。1.3 func (db *MemDatabase) Put(key []byte, value []byte) error方法 Put() 实现了接口 ethdb.Putter 和接口 ethdb.Database。参数:key []byte: key value []byte: value 返回值:出错返回错误消息 error,否则返回 nil 主要实现:加锁。代码为: db.lock.Lock() defer 解锁。代码为:defer db.lock.Unlock() 将 (key, value) 对存储数据库 db。db.db[string(key)] = common.CopyBytes(value) 1.4 func (db *MemDatabase) Has(key []byte) (bool, error)方法 Has() 实现了接口 ethdb.Database。参数:key []byte: key 返回值:存在返回 true,否则返回 false 出错返回错误消息 error,否则返回 nil 主要实现:加锁。代码为: db.lock.RLock() defer 解锁。代码为:defer db.lock.RUnlock() 是否存在。_, ok := db.db[string(key)] 1.5 func (db *MemDatabase) Get(key []byte) ([]byte, error)方法 Get() 实现了接口 ethdb.Database。参数:key []byte: key 返回值:存在返回 key 对应的 value 出错返回错误消息 error,否则返回 nil 主要实现:加锁。代码为:db.lock.RLock() defer 解锁。代码为:defer db.lock.RUnlock() 获取 key 对应的值 entry。代码为:entry, ok := db.db[string(key)] 将 entry 的副本返回。代码为:return common.CopyBytes(entry) 1.6 func (db *MemDatabase) Keys() [][]byte方法 Keys() 返回数据库中的所有 key。返回值:所有的 key 构成的列表 主要实现:加锁。代码为:db.lock.RLock() defer 解锁。代码为:defer db.lock.RUnlock() 定义所有 key 的列表 keys 遍历数据库 db.db 中的所有 key 将 key 添加到 keys 1.7 func (db *MemDatabase) Delete(key []byte) error方法 Put() 实现了接口 ethdb.Deleter 和接口 ethdb.Database。参数:key []byte: key 返回值:出错返回错误消息 error,否则返回 nil 主要实现:加锁。代码为:db.lock.Lock() defer 解锁。代码为:defer db.lock.Unlock() 通过 Go 内置函数 delete() 从数据库 db.db 中删除对应的 key。代码为:delete(db.db, string(key)) 1.8 func (db *MemDatabase) Close() 方法 Close() 实现了接口 ethdb.Database。主要实现:空实现。 1.9 func (db *MemDatabase) NewBatch() Batch方法 NewBatch() 实现了接口 ethdb.Database。主要实现:return &memBatch 1.10 func (db *MemDatabase) Len() int方法 Len() 返回数据库包含的数据量。返回值:数据量 主要实现:return len(db.db) 2. type kv struct数据结构 kv 用于描述批处理的值 k, v 和操作类型是 add 还是 del。k, v []byte: Key & Value del bool: 操作类型是插入还是删除 3. type memBatch struct数据结构 memBatch 是具有批处理能力的内存数据库。db *MemDatabase: 内存数据库 writes []kv: 批处理数据 size int: 批处理的字节数 3.1 func (b *memBatch) Put(key, value []byte) error方法 Put() 实现了接口 ethdb.Putter,用于将给定的 key & value 插入数据库。参数:key []byte: key value []byte: value 返回值:出错返回错误消息 error,否则返回 nil 主要实现:将 key & value & false 构建的 kv 插入批处理数据 writes b.writes = append(b.writes, kv) 增加批处理字节数 size b.size += len(value) 3.2 func (b *memBatch) Delete(key []byte) error方法 Delete() 实现了接口 ethdb.Deleter,用于从数据库中删除给定的 key。参数:key []byte: key 返回值:出错返回错误消息 error,否则返回 nil 主要实现:将 key & nil & true 构建的 kv 插入批处理数据 writes b.writes = append(b.writes, kv) 更新批处理字节数 size b.size += 1 3.3 func (b *memBatch) Write() error方法 Write() 一次性将批处理数据更新到数据库。返回值:出错返回错误消息 error,否则返回 nil 主要实现:加锁。代码为:db.lock.Lock() defer 解锁。代码为:defer db.lock.Unlock() 遍历批处理数据 b.writes 的每个 kv 如果 kv.del 从数据库中删除 kv.k delete(b.db.db, string(kv.k)) 退出本轮迭代 否则,将 kv.k & kv.v 插入数据库 b.db.db[string(kv.k)] = kv.v 3.4 func (b *memBatch) ValueSize() int方法 ValueSize() 返回批处理字节数。返回值:批处理字节数。 主要实现:return b.size 3.5 func (b *memBatch) Reset()方法 Reset() 重置批处理操作。主要实现:清空批处理操作 b.writes = b.writes[:0] b.size = 0 Referencehttps://github.com/ethereum/go-ethereum/blob/master/ethdb/memory_database.go ContributorWindstamp, https://github.com/windstamp
2023年03月03日
6 阅读
0 评论
0 点赞
2023-03-03
Go-ethereum 源码解析之 go-ethereum/ethdb/database.go
Go-ethereum 源码解析之 go-ethereum/ethdb/database.goSource code// Copyright 2017 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. Appendix A. 总体批注实现了底层数据库 LevelDB 的抽象层 LDBDatabase,用于生产环境。ethdb.LDBDatabase 实现了接口 ethdb.Database,并且将实际的操作转发给 LevelDB 的接口。同时,基于 metrics.Meter 测试各操作的性能。ethdb.ldbBatch 在 ethdb.LDBDatabase 的基础上提供了批处理能力。暂时不深入与性能相关的实现 metrics.Meter。Appendix B. 详细批注1. constwritePauseWarningThrottler = 1 * time.Minute: ??? 性能相关指标 2. varvar OpenFileLimit = 64: ??? LevelDB 一次能打开的文件数量上限? 3. type LDBDatabase struct数据结构 LDBDatabase 实现了接口 ethdb.Database,并且将实际的操作转发给 LevelDB 的接口。同时,基于 metrics.Meter 测试各操作的性能。fn string: 文件名。??? 干什么的文件名呢? db *leveldb.DB: LevelDB 实例 compTimeMeter metrics.Meter // Meter for measuring the total time spent in database compaction compReadMeter metrics.Meter // Meter for measuring the data read during compaction compWriteMeter metrics.Meter // Meter for measuring the data written during compaction writeDelayNMeter metrics.Meter // Meter for measuring the write delay number due to database compaction writeDelayMeter metrics.Meter // Meter for measuring the write delay duration due to database compaction diskReadMeter metrics.Meter // Meter for measuring the effective amount of data read diskWriteMeter metrics.Meter // Meter for measuring the effective amount of data written quitLock sync.Mutex // Mutex protecting the quit channel access quitChan chan chan error // Quit channel to stop the metrics collection before closing the database log log.Logger // Contextual logger tracking the database path 3.1 func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error)构造函数 NewLDBDatabase() 创建 LDBDatabase 的一个实例,该实例是 LevelDB 的包装器。参数:file string: 文件名 cache int: ??? 缓存? handles int: ??? 处理器? 返回值:LDBDatabase 实例 出错返回错误消息 error,否则返回 nil 主要实现:构建日志器 logger logger := log.New("database", file) 调整 cache,确保其最小值为 16 调整 handles,确保其最小值为 16 输出日志信息:logger.Info("Allocated cache and file handles", "cache", cache, "handles", handles) 打开 LevelDB,并从可能的错误恢复 db, err := leveldb.OpenFile(file, ...) corrupted := err.(*errors.ErrCorrupted) db, err = leveldb.RecoverFile(file, nil) 如果 err 不为 nil,则直接退出 return nil, err 构建 LDBDatabase 的实例并返回 return &LDBDatabase, nil 注意,这里并没有初始化任何性能计时器。3.2 func (db *LDBDatabase) Path() string方法 Path() 返回数据库目录的路径。3.3 func (db *LDBDatabase) Put(key []byte, value []byte) error方法 Put() 实现了接口 ethdb.Putter 和接口 ethdb.Database,将 key & value 写入数据库。参数:key []byte: key value []byte: value 返回值:出错返回错误消息 error,否则返回 nil 主要实现:转发给 LevelDB 的方法 Put() return db.db.Put(key, value, nil) 3.4 func (db *LDBDatabase) Has(key []byte) (bool, error)方法 Has() 实现了接口 ethdb.Database,查询给定的 key 是否存在于数据库。参数:key []byte: key 返回值:存在返回 true,否则返回 false 出错返回错误消息 error,否则返回 nil 主要实现:转发给 LevelDB 的方法 Has() return db.db.Has(key, nil) 3.5 func (db *LDBDatabase) Get(key []byte) ([]byte, error)方法 Get() 实现了接口 ethdb.Database,从数据库中获取给定 key 对应的 value。参数:key []byte: key 返回值:存在返回 key 对应的 value 出错返回错误消息 error,否则返回 nil 主要实现:转发给 LevelDB 的方法 Get() dat, err := db.db.Get(key, nil) 3.6 func (db *LDBDatabase) Delete(key []byte) error方法 Delete() 实现了接口 ethdb.Database,从数据库中删除指定的 key。参数:key []byte: key 返回值:出错返回错误消息 error,否则返回 nil 主要实现:转发给 LevelDB 的方法 Delete() return db.db.Delete(key, nil) 3.7 func (db *LDBDatabase) NewIterator() iterator.Iterator方法 NewIterator() 返回 LevelDB 的迭代器 iterator.Iterator。返回值:LevelDB 的迭代器 iterator.Iterator 主要实现:return db.db.NewIterator(nil, nil) 3.8 func (db *LDBDatabase) NewIteratorWithPrefix(prefix []byte) iterator.Iterator方法 NewIteratorWithPrefix() 返回 LevelDB 的迭代器 iterator.Iterator,这个迭代器指向具有指定前缀的数据库子集。参数:prefix []byte: 前缀 返回值:LevelDB 的迭代器 iterator.Iterator 主要实现:return db.db.NewIterator(util.BytesPrefix(prefix), nil) 3.9 func (db *LDBDatabase) Close()方法 Close() 实现了接口 ethdb.Database。主要实现:??? 停止性能指标器 将实际的关闭操作转发给 LevelDB 的方法 Close() err := db.db.Close() 如果 err == nil db.log.Info("Database closed") 否则 db.log.Error("Failed to close database", "err", err) 3.10 func (db *LDBDatabase) LDB() *leveldb.DB方法 LDB() 返回 LDBDatabase 中的底层 LevelDB 数据库。返回值:LDBDatabase 中的底层 LevelDB 数据库。 主要实现:return db.db 3.11 func (db *LDBDatabase) Meter(prefix string)性能指标相关内容,暂时不关注。3.12 func (db *LDBDatabase) meter(refresh time.Duration)性能指标相关内容,暂时不关注。3.13 func (db *LDBDatabase) NewBatch() Batch方法 NewBatch() 返回批处理器。返回值:批处理器 ldbBatch 主要实现:return &ldbBatch 4. type ldbBatch struct数据结构 ldbBatch 在 LevelDB 的基础上提供批处理能力。db *leveldb.DB: 底层的 LevelDB b *leveldb.Batch: LevelDB 的批处理器 size int: 字节数 4.1 func (b *ldbBatch) Put(key, value []byte) error方法 Put() 实现了接口 ethdb.Putter 和接口 ethdb.Batch,将 key & value 写入数据库。参数:key []byte: key value []byte: value 返回值:出错返回错误消息 error,否则返回 nil 主要实现:转发给 leveldb.Batch 的方法 Put() b.b.Put(key, value) 更新字节数 b.size += len(value) 4.2 func (b *ldbBatch) Delete(key []byte) error方法 Delete() 实现了接口 ethdb.Deleter 和接口 ethdb.Batch,从数据库中删除指定的 key。参数:key []byte: key 返回值:出错返回错误消息 error,否则返回 nil 主要实现:转发给 leveldb.Batch 的方法 Delete() b.b.Delete(key) 更新字节数 b.size += 1 4.3 func (b *ldbBatch) Write() error方法 Write() 实现了接口 ethdb.Batch,将批量数据一次性写入数据库。返回值:出错返回错误消息 error,否则返回 nil 主要实现:return b.db.Write(b.b, nil) 4.4 func (b *ldbBatch) ValueSize() int方法 ValueSize() 实现了接口 ethdb.Batch,返回批量字节数。返回值:批量字节数 主要实现:return b.size 4.5 func (b *ldbBatch) Reset()方法 Reset() 实现了接口 ethdb.Batch,重置数据库。主要实现:转发给 leveldb.Batch 的方法 Reset() b.b.Reset() 更新字节数 b.size = 0 5. type table struct数据结构 table 封装了数据库 Database,描述 Database 中的 key 具有相同的前缀 prefix。db Database: 数据库 prefix string: 前缀 5.1 func NewTable(db Database, prefix string) Database构造函数 NewTable() 创建数据库,同时数据库中的 key 具有相同的前缀 prefix。参数:db Database: 数据库 prefix string: 前缀 返回值:数据库 主要实现:return &table 5.2 func (dt *table) Put(key []byte, value []byte) error方法 Put() 实现了接口 ethdb.Putter 和接口 ethdb.Database。参数:key []byte: key value []byte: value 返回值:出错返回错误消息 error,否则返回 nil 主要实现:给 key 加上前缀 prefix,同时转发给 Database 的方法 Put() dt.db.Put(append([]byte(dt.prefix), key...), value) 5.3 func (dt *table) Has(key []byte) (bool, error)方法 Has() 实现了接口 ethdb.Database。参数:key []byte: key 返回值:存在返回 true,否则返回 false 出错返回错误消息 error,否则返回 nil 主要实现:给 key 加上前缀 prefix,同时转发给 Database 的方法 Has() dt.db.Has(append([]byte(dt.prefix), key...)) 5.4 func (dt *table) Get(key []byte) ([]byte, error)方法 Get() 实现了接口 ethdb.Database。参数:key []byte: key 返回值:存在返回 key 对应的 value 出错返回错误消息 error,否则返回 nil 主要实现:给 key 加上前缀 prefix,同时转发给 Database 的方法 Get() dt.db.Get(append([]byte(dt.prefix), key...)) 5.5 func (dt *table) Delete(key []byte) error方法 Delete() 实现了接口 ethdb.Deleter 和接口 ethdb.Database。参数:key []byte: key 返回值:出错返回错误消息 error,否则返回 nil 主要实现:给 key 加上前缀 prefix,同时转发给 Database 的方法 Delete() dt.db.Delete(append([]byte(dt.prefix), key...)) 5.6 func (dt *table) Close()方法 Close() 不执行任何操作。注意,这里并不会关闭底层数据库。5.7 func (dt *table) NewBatch() Batch方法 NewBatch() 返回具有批处理能力的 ethdb.table。返回值:具有批处理能力的 ethdb.tableBatch 主要实现:return &tableBatch 6. type tableBatch struct数据结构 tableBatch 封装了数据库 Database,描述 Database 中的 key 具有相同的前缀 prefix。同时,提供批处理能力。batch Batch: 批处理 prefix string: 前缀 6.1 func NewTableBatch(db Database, prefix string) Batch构造函数 NewTableBatch() 创建具有批处理能力的数据库,同时数据库中的 key 具有相同的前缀 prefix。参数:db Database: 数据库 prefix string: 前缀 返回值:具有批处理能力数据库 主要实现:return &tableBatch 6.2 func (tb *tableBatch) Put(key, value []byte) error方法 Put() 实现了接口 ethdb.Putter 和接口 ethdb.Batch,将 key & value 插入数据库。参数:key []byte: key value []byte: value 返回值:出错返回错误消息 error,否则返回 nil 主要实现:给 key 加上前缀 prefix,同时转发给 ethdb.Batch 的方法 Put() return tb.batch.Put(append([]byte(tb.prefix), key...), value) 6.3 func (tb *tableBatch) Delete(key []byte) error方法 Delete() 实现了接口 ethdb.Deleter 和接口 ethdb.Batch,从数据库中删除给定的 key。参数:key []byte: key 返回值:出错返回错误消息 error,否则返回 nil 主要实现:给 key 加上前缀 prefix,同时转发给 ethdb.Batch 的方法 Delete() return tb.batch.Delete(append([]byte(tb.prefix), key...)) 6.4 func (tb *tableBatch) Write() error方法 Write() 实现了接口 ethdb.Batch,将批处理数据一次性写入数据库。返回值:出错返回错误消息 error,否则返回 nil 主要实现:转发给 ethdb.Batch 的方法 Write() return tb.batch.Write() 6.5 func (tb *tableBatch) ValueSize() int方法 ValueSize() 实现了接口 ethdb.Batch,返回批处理数据的字节数。返回值:批处理数据字节数 主要实现:转发给 ethdb.Batch 的方法 ValueSize() return tb.batch.ValueSize() 6.6 func (tb *tableBatch) Reset()方法 Reset() 实现了接口 ethdb.Batch,清空批处理数据。主要实现:转发给 ethdb.Batch 的方法 Reset() tb.batch.Reset() Referencehttps://github.com/ethereum/go-ethereum/blob/master/ethdb/database.go ContributorWindstamp, https://github.com/windstamp
2023年03月03日
19 阅读
0 评论
0 点赞
1
...
92
93
94
...
109