181 lines
4.4 KiB
Go
181 lines
4.4 KiB
Go
package dbft
|
|
|
|
import (
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func (d *DBFT[H]) checkPrepare() {
|
|
if d.lastBlockIndex != d.BlockIndex || d.lastBlockView != d.ViewNumber {
|
|
// Notice that lastBlockTimestamp is left unchanged because
|
|
// this must be the value from the last header.
|
|
d.lastBlockTime = d.Timer.Now()
|
|
d.lastBlockIndex = d.BlockIndex
|
|
d.lastBlockView = d.ViewNumber
|
|
}
|
|
if !d.hasAllTransactions() {
|
|
d.Logger.Debug("check prepare: some transactions are missing", zap.Any("hashes", d.MissingTransactions))
|
|
return
|
|
}
|
|
|
|
count := 0
|
|
hasRequest := false
|
|
|
|
for _, msg := range d.PreparationPayloads {
|
|
if msg != nil {
|
|
if msg.ViewNumber() == d.ViewNumber {
|
|
count++
|
|
}
|
|
|
|
if msg.Type() == PrepareRequestType {
|
|
hasRequest = true
|
|
}
|
|
}
|
|
}
|
|
|
|
d.Logger.Debug("check preparations", zap.Bool("hasReq", hasRequest),
|
|
zap.Int("count", count),
|
|
zap.Int("M", d.M()))
|
|
|
|
if hasRequest && count >= d.M() {
|
|
if d.isAntiMEVExtensionEnabled() {
|
|
d.sendPreCommit()
|
|
d.changeTimer(d.timePerBlock)
|
|
d.checkPreCommit()
|
|
} else {
|
|
d.sendCommit()
|
|
d.changeTimer(d.timePerBlock)
|
|
d.checkCommit()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (d *DBFT[H]) checkPreCommit() {
|
|
if !d.hasAllTransactions() {
|
|
d.Logger.Debug("check preCommit: some transactions are missing", zap.Any("hashes", d.MissingTransactions))
|
|
return
|
|
}
|
|
|
|
count := 0
|
|
for _, msg := range d.PreCommitPayloads {
|
|
if msg != nil && msg.ViewNumber() == d.ViewNumber {
|
|
count++
|
|
}
|
|
}
|
|
|
|
if count < d.M() {
|
|
d.Logger.Debug("not enough PreCommits to process PreBlock", zap.Int("count", count))
|
|
return
|
|
}
|
|
|
|
d.preBlock = d.CreatePreBlock()
|
|
|
|
if !d.preBlockProcessed {
|
|
d.Logger.Info("processing PreBlock",
|
|
zap.Uint32("height", d.BlockIndex),
|
|
zap.Uint("view", uint(d.ViewNumber)),
|
|
zap.Int("tx_count", len(d.preBlock.Transactions())),
|
|
zap.Int("preCommit_count", count))
|
|
|
|
err := d.ProcessPreBlock(d.preBlock)
|
|
if err != nil {
|
|
d.Logger.Info("can't process PreBlock, waiting for more PreCommits to be collected",
|
|
zap.Error(err),
|
|
zap.Int("count", count))
|
|
return
|
|
}
|
|
d.preBlockProcessed = true
|
|
}
|
|
|
|
// Require PreCommit sent by self for reliability. This condition must not be
|
|
// removed because:
|
|
// 1) we need to filter out WatchOnly nodes;
|
|
// 2) CNs that have not sent PreCommit must not skip this stage (although it's OK
|
|
// from the DKG/TPKE side to build final Block based only on other CN's data).
|
|
if d.PreCommitSent() {
|
|
d.verifyCommitPayloadsAgainstHeader()
|
|
d.sendCommit()
|
|
d.changeTimer(d.timePerBlock)
|
|
d.checkCommit()
|
|
} else {
|
|
if !d.Context.WatchOnly() {
|
|
d.Logger.Debug("can't send commit since self preCommit not yet sent")
|
|
}
|
|
}
|
|
}
|
|
|
|
func (d *DBFT[H]) checkCommit() {
|
|
if !d.hasAllTransactions() {
|
|
d.Logger.Debug("check commit: some transactions are missing", zap.Any("hashes", d.MissingTransactions))
|
|
return
|
|
}
|
|
|
|
// return if we received commits from other nodes
|
|
// before receiving PrepareRequest from Speaker
|
|
count := 0
|
|
|
|
for _, msg := range d.CommitPayloads {
|
|
if msg != nil && msg.ViewNumber() == d.ViewNumber {
|
|
count++
|
|
}
|
|
}
|
|
|
|
if count < d.M() {
|
|
d.Logger.Debug("not enough to commit", zap.Int("count", count))
|
|
return
|
|
}
|
|
|
|
d.block = d.CreateBlock()
|
|
hash := d.block.Hash()
|
|
|
|
d.Logger.Info("approving block",
|
|
zap.Uint32("height", d.BlockIndex),
|
|
zap.Stringer("hash", hash),
|
|
zap.Int("tx_count", len(d.block.Transactions())),
|
|
zap.Stringer("merkle", d.block.MerkleRoot()),
|
|
zap.Stringer("prev", d.block.PrevHash()))
|
|
|
|
err := d.ProcessBlock(d.block)
|
|
if err != nil {
|
|
if d.isAntiMEVExtensionEnabled() {
|
|
d.Logger.Info("can't process Block, waiting for more Commits to be collected",
|
|
zap.Error(err),
|
|
zap.Int("count", count))
|
|
return
|
|
}
|
|
d.Logger.Fatal("block processing failed", zap.Error(err))
|
|
}
|
|
|
|
d.blockProcessed = true
|
|
|
|
// Do not initialize consensus process immediately. It's the caller's duty to
|
|
// start the new block acceptance process and call Reset at the
|
|
// new height.
|
|
}
|
|
|
|
func (d *DBFT[H]) checkChangeView(view byte) {
|
|
if d.ViewNumber >= view {
|
|
return
|
|
}
|
|
|
|
count := 0
|
|
|
|
for _, msg := range d.ChangeViewPayloads {
|
|
if msg != nil && msg.GetChangeView().NewViewNumber() >= view {
|
|
count++
|
|
}
|
|
}
|
|
|
|
if count < d.M() {
|
|
return
|
|
}
|
|
|
|
if !d.Context.WatchOnly() {
|
|
msg := d.ChangeViewPayloads[d.MyIndex]
|
|
if msg != nil && msg.GetChangeView().NewViewNumber() < view {
|
|
d.broadcast(d.makeChangeView(uint64(d.Timer.Now().UnixNano()), CVChangeAgreement))
|
|
}
|
|
}
|
|
|
|
d.initializeConsensus(view, d.lastBlockTimestamp)
|
|
}
|