【源码阅读】blockchain Ⅲ

1

1.1 writeKnownBlock

func (bc *BlockChain) writeKnownBlock(tx kv.RwTx, block block2.IBlock) error {

writeKnownBlock使用已知块更新头块标志并在必要时引入链式重组。

  1. 定义非外部事务标记notExternalTx
  2. 如果事务tx为nil,就创建一个事务bc.ChainDB.BeginRw(bc.ctx),有错就返回err。使用延迟回滚,并将notExternalTx标记为true
  3. 获得当前区块current := bc.CurrentBlock()
  4. 判断传入的区块对象的父哈希值是否与当前区块的哈希值相同。如果不同,则调用bc.reorg(tx, current, block)(参考1.2)进行重组织操作。如果重组织失败,则返回错误信息
  5. 如果相同,就不需要reorg。调用bc.writeHeadBlock(tx, block)将区块写入区块链头部。如果写入失败,则返回错误信息
  6. 最后,如果是内部事务(即notExternalTx为true),则提交事务tx.Commit()。如果提交失败,则返回错误信息。
    整个函数执行完毕后,返回nil表示成功

1.2 reorg

func (bc *BlockChain) reorg(tx kv.RwTx, oldBlock, newBlock block2.IBlock) error {

reorg获取两个块,一个旧链和一个新链,并将重构这些块并将它们插入到新规范链中,积累潜在的丢失事务并发布有关它们的事件。
注意,这里不会处理新的头块,调用者需要从外部处理它。
当两个区块链之间的差异较大时,这个函数会被调用来合并它们。

  1. 将较长的链减少到与较短的链相同的数量,所以要确定新链与旧链中更长的那一条链。将较长的链中的数据记录为已删除, 将较短的链存储起来以便后续插入
  • 如果旧链较长 if oldBlock.Number64().Uint64() > newBlock.Number64().Uint64(),将所有事务和日志收集为已删除的事务和日志,不断向前遍历(在不与新链相同且不为nil的情况下),将区块加入lodchain中,并且将每一个区块中的交易都记录为deleted:deletedTxs = append(deletedTxs, hash)
  • 如果新链较长,将所有区块都藏起来以备后续插入,也就是不断向前遍历(在不与旧链相同且不为nil的情况下),将区块加入newchain中
  1. 如果有一个区块为Nil,就会输出对应的错误信息
  2. 在事务上:如果事务为nil,就通过bc.ChainDB.BeginRw(bc.ctx)开启一个事务,有错就返回err,使用延迟回滚。同时将非外部事务标志useExternalTx标记为false
  3. 找到两个链的共同祖先,并减少两个链,直到找到公共祖先:
  • 如果新旧块的hash一样,就代表找到了共同祖先commonblock,就跳出for循环
  • 否则就一直执行,移除一个旧区块deletedTxs = append(deletedTxs, h)同时记录一个新区块newChain = append(newChain, newBlock)
  • 双链都向后退,使用rawdb.ReadBlock
  1. 会输出reorg的提示信息
  2. 如果找到了公共祖先,函数会将新的链(除了头块)插入到区块链中bc.writeHeadBlock(tx, newChain[i]),并收集新添加的交易addedTxs = append(addedTxs, h),注意添加顺序i := len(newChain) - 1
  3. 删除无用的索引,包括非规范事务索引和高于头块的规范链索引rawdb.DeleteTxLookupEntry(tx, t)
  4. 删除所有不是新规范链一部分的哈希标记,其中通过rawdb.ReadCanonicalHash获取hash,通过rawdb.TruncateCanonicalHash进行删除
  5. 如果没有使用外部事务,函数会提交事务tx.Commit()。如果在提交过程中发生错误,函数会返回错误。否则,它会返回nil表示成功完成重组织操作

1.3 Close

func (bc *BlockChain) Close() error {
	bc.Quit()
	return nil
}

调用Quit来进行退出(参考1.4)

1.4 Quit

func (bc *BlockChain) Quit() <-chan struct{} {
	return bc.ctx.Done()
}

该方法返回一个类型为chan struct{}的通道,该通道通过调用bc.ctx.Done()方法来获取。
这个方法的作用是提供一个退出信号给BlockChain结构体的上下文(ctx),以便在需要时停止执行相关操作。当调用此方法时,它会返回一个通道,可以通过该通道向上下文发送一个信号,表示需要退出。

1.5 DB

func (bc *BlockChain) DB() kv.RwDB {
	return bc.ChainDB
}

用该函数来返回一个数据库,用于执行数据库的操作

1.6 GetDepositInfo

func (bc *BlockChain) GetDepositInfo(address types.Address) (*uint256.Int, *uint256.Int) {
	var info *deposit.Info
	bc.ChainDB.View(bc.ctx, func(tx kv.Tx) error {
		info = deposit.GetDepositInfo(tx, address)
		return nil
	})
	if nil == info {
		return nil, nil
	}
	return info.RewardPerBlock, info.MaxRewardPerEpoch
}
  1. 声明了一个名为info的指针变量
  2. 使用bc.ChainDB.View方法来访问区块链数据库,并在回调函数中获取指定地址的存款信息。回调函数通过调用deposit.GetDepositInfo方法来获取存款信息,并将结果赋值给info变量
  3. 如果info为nil,则返回两个nil值
  4. 否则,返回存款信息中的奖励每块和每个周期的最大奖励。

1.7 GetAccountRewardUnpaid

func (bc *BlockChain) GetAccountRewardUnpaid(account types.Address) (*uint256.Int, error) {
	var value *uint256.Int
	var err error
	bc.ChainDB.View(bc.ctx, func(tx kv.Tx) error {
		value, err = rawdb.GetAccountReward(tx, account)
		return nil
	})
	return value, err
}

该函数的作用是获取指定账户未支付的奖励金额。

  1. 函数内部首先声明了一个变量value,用于存储未支付的奖励金额,以及一个变量err,用于存储错误信息。
  2. 通过调用bc.ChainDB.View方法来访问区块链数据库。在回调函数中,使用rawdb包中的rawdb.GetAccountReward方法从数据库中获取指定账户的未支付奖励金额,并将其赋值给value变量。同时,将错误信息赋值给err变量
  3. 函数返回valueerr作为结果。

2、验证

type BlockValidator struct {
	bc     *BlockChain      // Canonical block chain
	engine consensus.Engine // Consensus engine used for validating
	config *params.ChainConfig
}

这是一个名为BlockValidator的结构体,它包含以下字段:

  1. bc:指向BlockChain类型的指针,表示规范的区块链。
  2. engine:consensus.Engine类型的变量,表示用于验证共识引擎。
  3. config:指向params.ChainConfig类型的指针,表示链的配置参数。

2.1 NewBlockValidator

func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engine consensus.Engine) *BlockValidator {
	validator := &BlockValidator{
		engine: engine,
		bc:     blockchain,
		config: config,
	}
	return validator
}

该函数用于初始化一个验证器

2.2 ValidateBody

func (v *BlockValidator) ValidateBody(b block.IBlock) error {

ValidateBody验证给定块的叔叔,并验证块头的事务和叔叔根。假设此时已经对标头进行了验证。

  1. 检查区块的签名是否有效:
  • 首先获取区块的验证者列表vfs := b.Body().Verifier()
  • 遍历每个验证者,bls.PublicKeyFromBytes创建一个公钥,PublicKeyFromBytes从BigEndian字节切片创建BLS公钥。并将其存储在ss数组中ss[i] = blsP
  • 如果配置为v.config.IsBeijing(b.Number64().Uint64()),(IsBeijing返回num是否等于IsBeijingfork块或更大)则使用bls.SignatureFromBytes方法从从LittleEndian字节切片创建BLS签名,并使用FastAggregateVerify方法验证签名是否有效。如果验证失败,记录日志并返回错误。
  1. 检查区块是否已知bc.HasBlockAndState。如果区块链中已经存在该区块及其状态,则返回ErrKnownBlock错误。
  2. 检查交易根哈希是否匹配。计算区块中所有交易的SHA-256哈希值,并与区块头中的交易根哈希进行比较if hash := DeriveSha。如果不匹配,则返回错误
  3. 通过bc.HasBlockAndState检查父区块是否存在
  • 如果区块链中不存在父区块及其状态,则根据父区块是否在区块链中v.bc.HasBlock,返回不同的错误
  • 如果父区块存在但已被修剪(即被删除),则返回ErrPrunedAncestor错误;否则返回ErrUnknownAncestor错误。
    如果以上所有检查都通过,则返回nil表示区块有效。

2.3 ValidateState

func (v *BlockValidator) ValidateState(iBlock block.IBlock, statedb *state.IntraBlockState, receipts block.Receipts, usedGas uint64) error {

ValidateState验证状态转换后发生的各种变化,如gasused、接收根和状态根本身。如果验证成功,ValidateState将返回数据库批处理,否则为零并返回错误。
ValidateState用于验证区块的状态。它接收四个参数:iBlock(待验证的区块)、statedb(状态数据库)、receipts(交易收据)和usedGas(已使用的gas)。

  1. 检查区块头中的GasUsed字段是否与传入的usedGas相等。如果不相等,返回一个错误信息,表示远程和本地的gas使用量不一致。
  2. 创建一个布隆过滤器block.CreateBloom(receipts),并将其与区块头中的Bloom字段进行比较。如果两者不相等if rbloom != header.Bloom,返回一个错误信息,表示远程和本地的布隆过滤器不一致。
  3. 计算交易收据的哈希值receiptSha := DeriveSha(receipts),并将其与区块头中的ReceiptHash字段进行比较。如果不相等,遍历区块中的所有交易,并记录相关信息,最后返回一个错误信息,表示远程和本地的交易收据根哈希不一致
  4. 验证状态根receiptSha := DeriveSha(receipts)是否与接收到的状态根相匹配。如果不匹配,返回一个错误信息,表示远程和本地的默克尔根不一致
  5. 如果所有验证都通过,方法返回nil

3、blockchain_insert.go

type insertIterator struct {
	chain []block2.IBlock // Chain of blocks being iterated over

	results <-chan error // Verification result sink from the consensus engine
	errors  []error      // Header verification errors for the blocks

	index     int       // Current offset of the iterator
	validator Validator // Validator to run if verification succeeds
}

这是一个名为insertIterator的结构体,它包含以下字段:

  1. chain []block2.IBlock:一个block2.IBlock类型的切片,表示正在迭代的区块链。
  2. results <-chan error:一个错误类型的通道,用于从共识引擎接收验证结果。
  3. errors []error:一个错误类型的切片,用于存储区块头验证错误。
  4. index int:一个整数,表示迭代器的当前偏移量,索引值
  5. validator Validator:一个Validator类型的变量,如果验证成功,将运行此验证器。

3.1 newInsertIterator

func newInsertIterator(chain []block2.IBlock, results <-chan error, validator Validator) *insertIterator {
	return &insertIterator{
		chain:     chain,
		results:   results,
		errors:    make([]error, 0, len(chain)),
		index:     -1,
		validator: validator,
	}
}

该函数用于创建一个迭代器,基于给定的块创建一个新的迭代器。

3.2 next

func (it *insertIterator) next() (block2.IBlock, error) {

next返回迭代器中的下一个块,以及该块的任何潜在验证错误。当到达终点时,它将返回(nil,nil)。在循环迭代器中有很大的用处
该方法的作用是获取下一个区块并进行验证。

  1. 检查是否已经到达了区块链的末尾if it.index+1 >= len(it.chain),如果是,则将索引设置为链的长度并返回空值和nil错误
  2. 否则,索引递增,并检查当前索引是否有错误
  • 如果存在错误if it.errors[it.index] != nil,则直接返回该区块和错误信息
  • 如果没有错误,则调用it.validator.ValidateBody方法对区块的主体进行验证,并返回验证后的区块和验证结果。

3.3 peek

func (it *insertIterator) peek() (block2.IBlock, error) {

peek返回迭代器中的下一个块,以及该块的任何潜在验证错误,但不会推进迭代器。
头和正文验证错误(也为nil)都缓存到迭代器中,以避免重复以下next()调用的工作。该方法的作用是预览下一个区块(block)以及可能的错误信息。

方法的实现逻辑如下:

  1. 检查是否已经到达了区块链的末尾if it.index+1 >= len(it.chain),如果是,则返回空nil,表示没有下一个区块可以预览。
  2. 如果当前索引还没有到达链表的末尾,那么需要等待验证结果
  • 等待结果,将验证结果添加到错误列表中it.errors = append(it.errors, <-it.results)
  • 如果当前索引对应的错误信息是否不为nil,说明验证失败,直接返回该索引对应的区块和错误信息
  1. 如果当前索引对应的错误信息为nil,说明验证成功,忽略主体验证(因为我们没有父区块),直接返回该索引对应的区块和nil错误return it.chain[it.index+1], nil

3.4 previous

func (it *insertIterator) previous() block2.IHeader {
	if it.index < 1 {
		return nil
	}
	return it.chain[it.index-1].Header()
}

previor返回正在处理的前一个标头return it.chain[it.index-1].Header(),或nil(如果当前Index<1)

3.5 current

func (it *insertIterator) current() block2.IHeader {
	if it.index == -1 || it.index >= len(it.chain) {
		return nil
	}
	return it.chain[it.index].Header()
}

current返回正在处理的当前标头it.chain[it.index].Header(),或nil(如果当前Index=-1太靠前,或者index已经超过链的长度了it.index >= len(it.chain)

3.6 first

func (it *insertIterator) first() block2.IBlock {
	return it.chain[0]
}

该函数返回第一个区块return it.chain[0]

3.7 remaining

func (it *insertIterator) remaining() int {
	return len(it.chain) - it.index
}

remaining返回剩余块的数量。通过总长度减去当前索引值得到剩余的区块数量

3.8 processed

func (it *insertIterator) processed() int {
	return it.index + 1
}

该函数返回已经处理过的区块的数量,return it.index + 1为当前Index+1

你可能感兴趣的:(区块链,区块链)