Skip to content

Commit

Permalink
Merge pull request #118 from nspcc-dev/add-postblock-handling
Browse files Browse the repository at this point in the history
*: support extra dBFT stage
  • Loading branch information
roman-khimov committed Aug 1, 2024
2 parents a2d0843 + 2fca051 commit eddbd6d
Show file tree
Hide file tree
Showing 25 changed files with 1,182 additions and 145 deletions.
7 changes: 6 additions & 1 deletion block.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ type Block[H Hash] interface {

// Transactions returns block's transaction list.
Transactions() []Transaction[H]
// SetTransactions sets block's transaction list.
// SetTransactions sets block's transaction list. For anti-MEV extension
// transactions provided via this call are taken directly from PreBlock level
// and thus, may be out-of-date. Thus, with anti-MEV extension enabled it's
// suggested to use this method as a Block finalizer since it will be called
// right before the block approval. Do not rely on this with anti-MEV extension
// disabled.
SetTransactions([]Transaction[H])
}
48 changes: 48 additions & 0 deletions check.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,57 @@ func (d *DBFT[H]) checkPrepare() {
zap.Int("M", d.M()))

if hasRequest && count >= d.M() {
if d.isAntiMEVExtensionEnabled() {
d.sendPreCommit()
d.changeTimer(d.SecondsPerBlock)
d.checkPreCommit()
} else {
d.sendCommit()
d.changeTimer(d.SecondsPerBlock)
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 create PreBlock", zap.Int("count", count))
return
}

d.preBlock = d.CreatePreBlock()

d.Logger.Info("processing PreBlock",
zap.Uint32("height", d.BlockIndex),
zap.Uint("view", uint(d.ViewNumber)),
zap.Int("tx_count", len(d.preBlock.Transactions())))

if !d.preBlockProcessed {
d.preBlockProcessed = true
d.ProcessPreBlock(d.preBlock)
}

// Require PreCommit sent by self for reliability. This condition may be removed
// in the future.
if d.PreCommitSent() {
d.verifyCommitPayloadsAgainstHeader()
d.sendCommit()
d.changeTimer(d.SecondsPerBlock)
d.checkCommit()
} else {
d.Logger.Debug("can't send commit since self preCommit not yet sent")
}
}

Expand Down
3 changes: 2 additions & 1 deletion commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package dbft
// Commit is an interface for dBFT Commit message.
type Commit interface {
// Signature returns commit's signature field
// which is a block signature for the current epoch.
// which is a final block signature for the current epoch for both dBFT 2.0 and
// for anti-MEV extension.
Signature() []byte
}
75 changes: 75 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@ type Config[H Hash] struct {
// if current time is less than that of previous context.
// By default use millisecond precision.
TimestampIncrement uint64
// AntiMEVExtensionEnablingHeight denotes the height starting from which dBFT
// Anti-MEV extensions should be enabled. -1 means no extension is enabled.
AntiMEVExtensionEnablingHeight int64
// GetKeyPair returns an index of the node in the list of validators
// together with it's key pair.
GetKeyPair func([]PublicKey) (int, PrivateKey, PublicKey)
// NewPreBlockFromContext should allocate, fill from Context and return new block.PreBlock.
NewPreBlockFromContext func(ctx *Context[H]) PreBlock[H]
// NewBlockFromContext should allocate, fill from Context and return new block.Block.
NewBlockFromContext func(ctx *Context[H]) Block[H]
// RequestTx is a callback which is called when transaction contained
Expand All @@ -36,10 +41,14 @@ type Config[H Hash] struct {
// GetVerified returns a slice of verified transactions
// to be proposed in a new block.
GetVerified func() []Transaction[H]
// VerifyPreBlock verifies if preBlock is valid.
VerifyPreBlock func(b PreBlock[H]) bool
// VerifyBlock verifies if block is valid.
VerifyBlock func(b Block[H]) bool
// Broadcast should broadcast payload m to the consensus nodes.
Broadcast func(m ConsensusPayload[H])
// ProcessBlock is called every time new preBlock is accepted.
ProcessPreBlock func(b PreBlock[H])
// ProcessBlock is called every time new block is accepted.
ProcessBlock func(b Block[H])
// GetBlock should return block with hash.
Expand All @@ -63,6 +72,8 @@ type Config[H Hash] struct {
NewPrepareResponse func(preparationHash H) PrepareResponse[H]
// NewChangeView is a constructor for payload.ChangeView.
NewChangeView func(newViewNumber byte, reason ChangeViewReason, timestamp uint64) ChangeView
// NewPreCommit is a constructor for payload.PreCommit.
NewPreCommit func(data []byte) PreCommit
// NewCommit is a constructor for payload.Commit.
NewCommit func(signature []byte) Commit
// NewRecoveryRequest is a constructor for payload.RecoveryRequest.
Expand All @@ -73,6 +84,10 @@ type Config[H Hash] struct {
VerifyPrepareRequest func(p ConsensusPayload[H]) error
// VerifyPrepareResponse performs external PrepareResponse verification and returns nil if it's successful.
VerifyPrepareResponse func(p ConsensusPayload[H]) error
// VerifyPreCommit performs external PreCommit verification and returns nil if it's successful.
// Note that PreBlock-dependent PreCommit verification should be performed inside PreBlock.Verify
// callback.
VerifyPreCommit func(p ConsensusPayload[H]) error
}

const defaultSecondsPerBlock = time.Second * 15
Expand Down Expand Up @@ -101,6 +116,10 @@ func defaultConfig[H Hash]() *Config[H] {

VerifyPrepareRequest: func(ConsensusPayload[H]) error { return nil },
VerifyPrepareResponse: func(ConsensusPayload[H]) error { return nil },

AntiMEVExtensionEnablingHeight: -1,
VerifyPreBlock: func(PreBlock[H]) bool { return true },
VerifyPreCommit: func(ConsensusPayload[H]) error { return nil },
}
}

Expand Down Expand Up @@ -131,6 +150,20 @@ func checkConfig[H Hash](cfg *Config[H]) error {
return errors.New("NewRecoveryRequest is nil")
} else if cfg.NewRecoveryMessage == nil {
return errors.New("NewRecoveryMessage is nil")
} else if cfg.AntiMEVExtensionEnablingHeight >= 0 {
if cfg.NewPreBlockFromContext == nil {
return errors.New("NewPreBlockFromContext is nil")
} else if cfg.ProcessPreBlock == nil {
return errors.New("ProcessPreBlock is nil")
} else if cfg.NewPreCommit == nil {
return errors.New("NewPreCommit is nil")
}
} else if cfg.NewPreBlockFromContext != nil {
return errors.New("NewPreBlockFromContext is set, but AntiMEVExtensionEnablingHeight is not specified")
} else if cfg.ProcessPreBlock != nil {
return errors.New("ProcessPreBlock is set, but AntiMEVExtensionEnablingHeight is not specified")
} else if cfg.NewPreCommit != nil {
return errors.New("NewPreCommit is set, but AntiMEVExtensionEnablingHeight is not specified")
}

return nil
Expand Down Expand Up @@ -164,13 +197,27 @@ func WithSecondsPerBlock[H Hash](d time.Duration) func(config *Config[H]) {
}
}

// WithAntiMEVExtensionEnablingHeight sets AntiMEVExtensionEnablingHeight.
func WithAntiMEVExtensionEnablingHeight[H Hash](h int64) func(config *Config[H]) {
return func(cfg *Config[H]) {
cfg.AntiMEVExtensionEnablingHeight = h
}
}

// WithTimestampIncrement sets TimestampIncrement.
func WithTimestampIncrement[H Hash](u uint64) func(config *Config[H]) {
return func(cfg *Config[H]) {
cfg.TimestampIncrement = u
}
}

// WithNewPreBlockFromContext sets NewPreBlockFromContext.
func WithNewPreBlockFromContext[H Hash](f func(ctx *Context[H]) PreBlock[H]) func(config *Config[H]) {
return func(cfg *Config[H]) {
cfg.NewPreBlockFromContext = f
}
}

// WithNewBlockFromContext sets NewBlockFromContext.
func WithNewBlockFromContext[H Hash](f func(ctx *Context[H]) Block[H]) func(config *Config[H]) {
return func(cfg *Config[H]) {
Expand Down Expand Up @@ -206,6 +253,13 @@ func WithGetVerified[H Hash](f func() []Transaction[H]) func(config *Config[H])
}
}

// WithVerifyPreBlock sets VerifyPreBlock.
func WithVerifyPreBlock[H Hash](f func(b PreBlock[H]) bool) func(config *Config[H]) {
return func(cfg *Config[H]) {
cfg.VerifyPreBlock = f
}
}

// WithVerifyBlock sets VerifyBlock.
func WithVerifyBlock[H Hash](f func(b Block[H]) bool) func(config *Config[H]) {
return func(cfg *Config[H]) {
Expand All @@ -227,6 +281,13 @@ func WithProcessBlock[H Hash](f func(b Block[H])) func(config *Config[H]) {
}
}

// WithProcessPreBlock sets ProcessPreBlock.
func WithProcessPreBlock[H Hash](f func(b PreBlock[H])) func(config *Config[H]) {
return func(cfg *Config[H]) {
cfg.ProcessPreBlock = f
}
}

// WithGetBlock sets GetBlock.
func WithGetBlock[H Hash](f func(h H) Block[H]) func(config *Config[H]) {
return func(cfg *Config[H]) {
Expand Down Expand Up @@ -297,6 +358,13 @@ func WithNewCommit[H Hash](f func(signature []byte) Commit) func(config *Config[
}
}

// WithNewPreCommit sets NewPreCommit.
func WithNewPreCommit[H Hash](f func(signature []byte) PreCommit) func(config *Config[H]) {
return func(cfg *Config[H]) {
cfg.NewPreCommit = f
}
}

// WithNewRecoveryRequest sets NewRecoveryRequest.
func WithNewRecoveryRequest[H Hash](f func(ts uint64) RecoveryRequest) func(config *Config[H]) {
return func(cfg *Config[H]) {
Expand Down Expand Up @@ -324,3 +392,10 @@ func WithVerifyPrepareResponse[H Hash](f func(prepareResp ConsensusPayload[H]) e
cfg.VerifyPrepareResponse = f
}
}

// WithVerifyPreCommit sets VerifyPreCommit.
func WithVerifyPreCommit[H Hash](f func(preCommit ConsensusPayload[H]) error) func(config *Config[H]) {
return func(cfg *Config[H]) {
cfg.VerifyPreCommit = f
}
}
2 changes: 2 additions & 0 deletions consensus_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ type ConsensusMessage[H Hash] interface {
GetPrepareRequest() PrepareRequest[H]
// GetPrepareResponse returns payload as if it was PrepareResponse.
GetPrepareResponse() PrepareResponse[H]
// GetPreCommit returns payload as if it was PreCommit.
GetPreCommit() PreCommit
// GetCommit returns payload as if it was Commit.
GetCommit() Commit
// GetRecoveryRequest returns payload as if it was RecoveryRequest.
Expand Down
5 changes: 4 additions & 1 deletion consensus_message_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import "fmt"
// MessageType is a type for dBFT consensus messages.
type MessageType byte

// 6 following constants enumerate all possible type of consensus message.
// 7 following constants enumerate all possible type of consensus message.
const (
ChangeViewType MessageType = 0x00
PrepareRequestType MessageType = 0x20
PrepareResponseType MessageType = 0x21
PreCommitType MessageType = 0x31
CommitType MessageType = 0x30
RecoveryRequestType MessageType = 0x40
RecoveryMessageType MessageType = 0x41
Expand All @@ -26,6 +27,8 @@ func (m MessageType) String() string {
return "PrepareResponse"
case CommitType:
return "Commit"
case PreCommitType:
return "PreCommit"
case RecoveryRequestType:
return "RecoveryRequest"
case RecoveryMessageType:
Expand Down
Loading

0 comments on commit eddbd6d

Please sign in to comment.