type BlockChain struct {
chainConfig *params.ChainConfig
ctx context.Context
cancel context.CancelFunc
genesisBlock block2.IBlock
blocks []block2.IBlock
headers []block2.IHeader
currentBlock atomic.Pointer[block2.Block]
//state *statedb.StateDB
ChainDB kv.RwDB
engine consensus.Engine
insertLock chan struct{}
latestBlockCh chan block2.IBlock
lock sync.Mutex
peers map[peer.ID]bool
chBlocks chan block2.IBlock
p2p p2p.P2P
errorCh chan error
process Processor
wg sync.WaitGroup //
procInterrupt int32 // insert chain
futureBlocks *lru.Cache[types.Hash, *block2.Block]
receiptCache *lru.Cache[types.Hash, []*block2.Receipt]
blockCache *lru.Cache[types.Hash, *block2.Block]
headerCache *lru.Cache[types.Hash, *block2.Header]
numberCache *lru.Cache[types.Hash, uint64]
tdCache *lru.Cache[types.Hash, *uint256.Int]
forker *ForkChoice
validator Validator
}
这是一个名为BlockChain的结构体,它包含了区块链的各种属性和方法。以下是对各个属性的简要说明:
chainConfig
:链配置ctx
:上下文cancel
:取消函数genesisBlock
:创世区块blocks
:区块列表headers
:区块头列表currentBlock
:当前区块ChainDB
:链数据库engine
:共识引擎,是consensus模块中的接口,用来验证block的接口insertLock
:插入锁latestBlockCh
:最新区块通道lock
:互斥锁peers
:节点映射chBlocks
:区块通道p2p
:P2P协议实例errorCh
:错误通道wg
:同步等待组,类型为sync.WaitGroup。procInterrupt
:进程中断标志futureBlocks
:未来区块缓存,收到的区块时间大于当前头区块时间15s而小于30s的区块,可作为当前节点待处理的区块receiptCache
:收据缓存blockCache
:区块缓存headerCache
:区块头缓存numberCache
:区块号缓存tdCache
:总难度缓存forker
:分叉选择器validator
:验证器func NewBlockChain(ctx context.Context, genesisBlock block2.IBlock, engine consensus.Engine, db kv.RwDB, p2p p2p.P2P, config *params.ChainConfig) (common.IBlockChain, error) {
这个函数用于初始化:
c, cancel := context.WithCancel(ctx)
使用Go语言中的context包创建一个带有取消功能的上下文。其中,传入的参数ctx
是一个已经存在的上下文对象,cancel
是一个用于取消上下文的函数。通过调用context.WithCancel(ctx)
方法,可以创建一个新的上下文对象c
,并返回一个与c
关联的取消函数cancel
。当需要取消上下文时,只需调用cancel()
函数即可。db.View
来读取数据库中的信息,如果读取到的当前区块信息为nil,就创建一个创世区块bc.forker = NewForkChoice(bc, nil)
创建新的分叉选择NewStateProcessor
创建新的状态处理NewBlockValidator
创建新的区块验证func (bc *BlockChain) Config() *params.ChainConfig {
return bc.chainConfig
}
func (bc *BlockChain) CurrentBlock() block2.IBlock {
return bc.currentBlock.Load()
}
func (bc *BlockChain) Blocks() []block2.IBlock {
return bc.blocks
}
func (bc *BlockChain) GenesisBlock() block2.IBlock {
return bc.genesisBlock
}
用于返回区块链配置/当前区块/区块列表和创世区块
func (bc *BlockChain) InsertHeader(headers []block2.IHeader) (int, error) {
//TODO implement me
panic("implement me")
}
panic(“implement me”)表示抛出一个带有消息"implement me"的panic异常。通常,这种异常是为了提醒开发者需要实现某个功能或者修复某个错误。
func (bc *BlockChain) Start() error {
bc.wg.Add(3)
go bc.runLoop()
go bc.updateFutureBlocksLoop()
return nil
}
该方法的作用是启动区块链的运行
bc.wg.Add(3)
向一个工作组(workgroup)中添加了三个任务go bc.runLoop()
启动了一个名为runLoop
的协程(参考1.9),该协程负责处理区块链的运行逻辑go bc.updateFutureBlocksLoop()
启动了一个名为updateFutureBlocksLoop
的协程,该协程负责更新未来区块的信息nil
表示没有错误发生。func (bc *BlockChain) verifyBody(block block2.IBlock) error {
return nil
}
func (bc *BlockChain) verifyState(block block2.IBlock, state *state.IntraBlockState, receipts block2.Receipts, usedGas uint64) error {
return nil
}
用于验证区块体和状态
func (bc *BlockChain) AddPeer(hash string, remoteBlock uint64, peerID peer.ID) error
用于添加节点
_, ok := bc.peers[peerID]
,如果已经存在,就输出错误信息func (bc *BlockChain) GetReceipts(blockHash types.Hash) (block2.Receipts, error)
func (bc *BlockChain) GetLogs(blockHash types.Hash) ([][]*block2.Log, error)
获取收据和日志函数。
rtx, err := bc.ChainDB.BeginRo(bc.ctx)
从bc.ChainDB
中开始一个只读事务。rtx
是一个指向事务的指针,err
是一个错误变量,用于存储可能发生的错误。defer
关键字调用rtx.Rollback()
方法来回滚事务。rawdb.ReadReceiptsByHash(rtx, blockHash)
来通过hash获取收据消息func (bc *BlockChain) LatestBlockCh() (block2.IBlock, error) {
函数从通道中获取信息,对应不同的操作从而得到最新区块
case <-bc.ctx.Done():
表示上下文已经完成之后,就会返回nil和主链已经关闭的信息提示case block, ok := <-bc.latestBlockCh:
读取最新区块通道的信息来判断是否出错func (bc *BlockChain) runLoop() {
defer func() {
bc.wg.Done()
bc.cancel()
bc.StopInsert()
close(bc.errorCh)
bc.wg.Wait()
}()
在runLoop
函数执行完成之后,会执行上述的函数,包括同步完成函数、取消函数、停止插入函数、关闭error通道和开始同步等待
2. 对应不同的select
bc.ctx.Done()
的信号,表示已经完成,就会直接返回case err, ok := <-bc.errorCh
通道的信号,就会判断有无错误func (bc *BlockChain) updateFutureBlocksLoop() {
futureTimer := time.NewTicker(2 * time.Second)
defer futureTimer.Stop()
defer bc.wg.Done()
创建了一个定时器,每隔2秒触发一次。该函数的主要功能是定期检查并更新区块链中的未来区块。然后使用defer
关键字来确保在函数执行完毕后,定时器会被停止,并且等待组bc.wg
的完成状态被设置
2. 执行一个for循环,其中使用select语句监听两个事件:定时器触发事件和上下文取消事件。
case <-futureTimer.C
语句确保在定时器触发时,才会执行,否则会一直阻塞bc.futureBlocks
的所有键。bc.futureBlocks.Get(key)
获取对应的值。如果该键存在且有对应的值,则将该值赋给变量value,并且ok为true。sort.Slice
对一个名为blocks的切片进行排序。排序依据是Number64()方法(区块编号),按照从小到大的顺序排列blocks
中的区块插入到区块链中bc.InsertChain(blocks)
。bc.futureBlocks
中移除这些已插入的区块bc.futureBlocks.Remove(k)
,并记录日志信息case <-bc.ctx.Done():
func (bc *BlockChain) runNewBlockMessage() {
defer sub.Unsubscribe()
当函数执行完成之后取消订阅case <-bc.ctx.Done():
就直接返回case block, ok := <-bc.chBlocks:
,当获取到区块的时候,更新数据库(写区块,写区块头,读当前区块)使用的都是rawdb中的函数block.FromProtoMessage(msg.Block)
没有err信息的时候,也进行数据库的更新func (bc *BlockChain) GetHeader(h types.Hash, number *uint256.Int) block2.IHeader {
获取给定hash和number获取区块的header
rawdb.ReadHeader
函数进行获取bc.ChainDB.BeginRo(bc.ctx)
和延迟回滚defer tx.Rollback()
func (bc *BlockChain) GetHeaderByNumber(number *uint256.Int) block2.IHeader {
函数通过number获取到区块的header
bc.ChainDB.BeginRo(bc.ctx)
rawdb.ReadCanonicalHash
函数根据number获得hash值,并在错误情况下输出错误信息,返回niltypes.Hash{}
,就返回bil表示不存在defer tx.Rollback()
func (bc *BlockChain) GetHeaderByHash(h types.Hash) (block2.IHeader, error) {
number := bc.GetBlockNumber(h)
if number == nil {
return nil, nil
}
return bc.GetHeader(h, uint256.NewInt(*number)), nil
}
bc.GetBlockNumber(h)
通过hash获取到number的值(参考2.5),如果number不存在,就返回nilGetHeader
(参考2.1)来获取defer tx.Rollback()
func (bc *BlockChain) GetCanonicalHash(number *uint256.Int) types.Hash {
tx, err := bc.ChainDB.BeginRo(bc.ctx)
if nil != err {
return types.Hash{}
}
defer tx.Rollback()
hash, err := rawdb.ReadCanonicalHash(tx, number.Uint64())
if nil != err {
return types.Hash{}
}
return hash
}
该函数为传入的number的值得到对应的hash。
bc.ChainDB.BeginRo(bc.ctx)
从数据库中开始读rawdb.ReadCanonicalHash
从数据库中获取到hash数据,有错就返回默认hashtypes.Hash{}
没有错误就返回获取到的hashdefer tx.Rollback()
func (bc *BlockChain) GetBlockNumber(hash types.Hash) *uint64 {
该函数通过给定的hash获取对应的区块号number
bc.ChainDB.BeginRo(bc.ctx)
开启,有错误信息就返回nilnumber := rawdb.ReadHeaderNumber(tx, hash)
来读取bc.numberCache.Add(hash, *number)
来添加一条数据到cache中,方便下次查询defer tx.Rollback()
func (bc *BlockChain) GetBlockByHash(h types.Hash) (block2.IBlock, error) {
number := bc.GetBlockNumber(h)
if nil == number {
return nil, errBlockDoesNotExist
}
return bc.GetBlock(h, *number), nil
}
该函数用过给定的hash来获取对应的block
number := bc.GetBlockNumber(h)
获取对应的区块号number(参考2.5)bc.GetBlock(h, *number)
获取区块并返回(参考2.9)func (bc *BlockChain) GetBlockByNumber(number *uint256.Int) (block2.IBlock, error) {
}
该函数用过给定的number来获取对应的block
bc.ChainDB.View(bc.ctx, func(tx kv.Tx) error {
hash, _ = rawdb.ReadCanonicalHash(tx, number.Uint64())
return nil
})
其中使用到rawdb.ReadCanonicalHash(tx, number.Uint64())
从数据库中来获取对应的hash
2. 如果获取的hash为types.Hash{}
,就返回nil
3. 否则就使用bc.GetBlock(h, *number)
获取区块并返回(参考2.9)
func (bc *BlockChain) GetBlocksFromHash(hash types.Hash, n int) (blocks []block2.IBlock) {
该函数通过传入的hash来获取n个区块中对应的区块小链
bc.numberCache.Get(hash)
来获取对应的区块号number,并在获取到之后将num赋值给numberbc.numberCache.Add(hash, *number)
block := bc.GetBlock(hash, *number)
获取该hash对应的区块block(参考2.9)blocks = append(blocks, block)
hash = block.ParentHash()
*number--
func (bc *BlockChain) GetBlock(hash types.Hash, number uint64) block2.IBlock {
该函数根据给定的hash和number来获取区块Block
types.Hash{}
就返回nil,代表不存在bc.ChainDB.BeginRo(bc.ctx)
开启,有错就返回nilrawdb.ReadBlock
从数据库中读取blockbc.blockCache.Add(hash, block)
defer tx.Rollback()
func (bc *BlockChain) GetTd(hash types.Hash, number *uint256.Int) *uint256.Int {
该函数通过给定的hash和number来获取对应的总难度td
td, ok := bc.tdCache.Get(hash)
,如果有就直接返回td,否则就从数据库中读取rawdb.ReadTd
从数据库中进行读取,如果有错误err,就返回nilbc.tdCache.Add(hash, td)
_ = bc.ChainDB.View(bc.ctx, func(tx kv.Tx) error {
ptd, err := rawdb.ReadTd(tx, hash, number.Uint64())
if nil != err {
return err
}
td = ptd
return nil
})