From 506e19b60c2b9a2057b0f93bb82401fdf1d847ad Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Thu, 1 Oct 2026 19:38:17 +0300 Subject: [PATCH] dbft: use designated type instead of `[]Transaction[H]` Allows to drop useless slice allocation code at the caller's side. Signed-off-by: Anna Shaleva --- CHANGELOG.md | 2 + README.md | 10 +- block.go | 6 +- check.go | 8 +- config.go | 202 ++++++++++++------------ consensus_message.go | 6 +- consensus_payload.go | 4 +- context.go | 86 +++++----- dbft.go | 64 ++++---- dbft_test.go | 178 ++++++++++----------- helpers.go | 38 ++--- helpers_test.go | 11 +- internal/consensus/amev_block.go | 10 +- internal/consensus/amev_preBlock.go | 10 +- internal/consensus/block.go | 10 +- internal/consensus/block_test.go | 3 +- internal/consensus/consensus.go | 56 +++---- internal/consensus/consensus_message.go | 10 +- internal/consensus/constructors.go | 15 +- internal/consensus/message.go | 2 +- internal/consensus/message_test.go | 4 +- internal/consensus/prepare_request.go | 10 +- internal/consensus/recovery_message.go | 32 ++-- internal/simulation/main.go | 26 +-- pre_block.go | 6 +- prepare_request.go | 4 +- recovery_message.go | 14 +- send.go | 32 ++-- 28 files changed, 427 insertions(+), 432 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 536bf4247..899faf14f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ New features: Behaviour changes: * transactions management refactoring implied library API changes (#164) + * add a separate `DBFT` generic parameter for transaction slice type + specificatoin (#165) Improvements: * minimum required Go version is 1.26 (#144, #156, #162) diff --git a/README.md b/README.md index 95d5491ab..9ce118ac7 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,11 @@ in `config.go`. 2. `dbft` package contains `PrivateKey`/`PublicKey` interfaces which permits usage of one's own cryptography for signing blocks on `Commit` stage. Refer to `identity.go` for `PrivateKey`/`PublicKey` description. No default implementation is provided. -3. `dbft` package contains `Hash` interface which permits usage of one's own -hash implementation without additional overhead on conversions. Instantiate dBFT with -custom hash implementation that matches requirements specified in the corresponding -documentation. Refer to `identity.go` for `Hash` description. No default implementation is -provided. +3. `dbft` package contains `Hash` and `Transaction` interfaces which permits usage of one's own +hash and transaction implementation without additional overhead on conversions. Instantiate dBFT with +custom hash and transaction implementation that matches requirements specified in the corresponding +documentation. Refer to `identity.go` for `Hash` description. Refer to `transaction.go` for `Transaction` +description. No default implementation is provided neither for `Hash` nor for `Transaction`. 4. `dbft` package contains `Block` and `Transaction` abstractions located at the `block.go` and `transaction.go` files. Every block must be able to be signed and verified as well as implement getters for main fields. `Transaction` is an entity which can be hashed. Two entities having diff --git a/block.go b/block.go index 4705673fa..97181d571 100644 --- a/block.go +++ b/block.go @@ -1,7 +1,7 @@ package dbft // Block is a generic interface for a block used by dbft. -type Block[H Hash] interface { +type Block[H Hash, Tx Transaction[H]] interface { // Hash returns block hash. Hash() H // PrevHash returns previous block hash. @@ -19,12 +19,12 @@ type Block[H Hash] interface { Verify(key PublicKey, sign []byte) error // Transactions returns block's transaction list. - Transactions() []Transaction[H] + Transactions() []Tx // 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]) + SetTransactions([]Tx) } diff --git a/check.go b/check.go index 6f8aea8c7..95f8acabc 100644 --- a/check.go +++ b/check.go @@ -4,7 +4,7 @@ import ( "go.uber.org/zap" ) -func (d *DBFT[H]) checkPrepare() { +func (d *DBFT[H, Tx]) 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. @@ -49,7 +49,7 @@ func (d *DBFT[H]) checkPrepare() { } } -func (d *DBFT[H]) checkPreCommit() { +func (d *DBFT[H, Tx]) checkPreCommit() { if !d.hasAllTransactions() { d.Logger.Debug("check preCommit: some transactions are missing", zap.Any("hashes", d.MissingTransactions)) return @@ -103,7 +103,7 @@ func (d *DBFT[H]) checkPreCommit() { } } -func (d *DBFT[H]) checkCommit() { +func (d *DBFT[H, Tx]) checkCommit() { if !d.hasAllTransactions() { d.Logger.Debug("check commit: some transactions are missing", zap.Any("hashes", d.MissingTransactions)) return @@ -152,7 +152,7 @@ func (d *DBFT[H]) checkCommit() { // new height. } -func (d *DBFT[H]) checkChangeView(view byte) { +func (d *DBFT[H, Tx]) checkChangeView(view byte) { if d.ViewNumber >= view { return } diff --git a/config.go b/config.go index 43fcfd216..3947786ba 100644 --- a/config.go +++ b/config.go @@ -9,7 +9,7 @@ import ( ) // Config contains initialization and working parameters for dBFT. -type Config[H Hash] struct { +type Config[H Hash, Tx Transaction[H]] struct { // Logger Logger *zap.Logger // Timer @@ -37,9 +37,9 @@ type Config[H Hash] struct { // 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] + NewPreBlockFromContext func(ctx *Context[H, Tx]) PreBlock[H, Tx] // NewBlockFromContext should allocate, fill from Context and return new block.Block. - NewBlockFromContext func(ctx *Context[H]) Block[H] + NewBlockFromContext func(ctx *Context[H, Tx]) Block[H, Tx] // RequestTx is a callback which is called when transaction contained // in current block can't be found in memory pool. The slice received by // this callback MUST NOT be changed. @@ -54,19 +54,19 @@ type Config[H Hash] struct { StopTxFlow func() // GetVerified returns a slice of verified transactions // to be proposed in a new block. - GetVerified func() []Transaction[H] + GetVerified func() []Tx // VerifyPreBlock verifies if preBlock is valid. - VerifyPreBlock func(b PreBlock[H]) bool + VerifyPreBlock func(b PreBlock[H, Tx]) bool // VerifyBlock verifies if block is valid. - VerifyBlock func(b Block[H]) bool + VerifyBlock func(b Block[H, Tx]) bool // Broadcast should broadcast payload m to the consensus nodes. - Broadcast func(m ConsensusPayload[H]) + Broadcast func(m ConsensusPayload[H, Tx]) // ProcessBlock is called every time new preBlock is accepted. - ProcessPreBlock func(b PreBlock[H]) error + ProcessPreBlock func(b PreBlock[H, Tx]) error // ProcessBlock is called every time new block is accepted. - ProcessBlock func(b Block[H]) error + ProcessBlock func(b Block[H, Tx]) error // GetBlock should return block with hash. - GetBlock func(h H) Block[H] + GetBlock func(h H) Block[H, Tx] // WatchOnly tells if a node should only watch. WatchOnly func() bool // CurrentHeight returns index of the last accepted block. @@ -77,11 +77,11 @@ type Config[H Hash] struct { // When called with a transaction list it must return // list of the validators of the next block. // If this function ever returns 0-length slice, dbft will panic. - GetValidators func(...Transaction[H]) []PublicKey + GetValidators func(...Tx) []PublicKey // NewConsensusPayload is a constructor for payload.ConsensusPayload. - NewConsensusPayload func(*Context[H], MessageType, any) ConsensusPayload[H] + NewConsensusPayload func(*Context[H, Tx], MessageType, any) ConsensusPayload[H, Tx] // NewPrepareRequest is a constructor for payload.PrepareRequest. - NewPrepareRequest func(ts uint64, nonce uint64, txes []Transaction[H]) PrepareRequest[H] + NewPrepareRequest func(ts uint64, nonce uint64, txes []Tx) PrepareRequest[H, Tx] // NewPrepareResponse is a constructor for payload.PrepareResponse. NewPrepareResponse func(preparationHash H) PrepareResponse[H] // NewChangeView is a constructor for payload.ChangeView. @@ -93,55 +93,55 @@ type Config[H Hash] struct { // NewRecoveryRequest is a constructor for payload.RecoveryRequest. NewRecoveryRequest func(ts uint64) RecoveryRequest // NewRecoveryMessage is a constructor for payload.RecoveryMessage. - NewRecoveryMessage func() RecoveryMessage[H] + NewRecoveryMessage func() RecoveryMessage[H, Tx] // VerifyPrepareRequest can perform external payload verification and returns true iff it was successful. - VerifyPrepareRequest func(p ConsensusPayload[H]) error + VerifyPrepareRequest func(p ConsensusPayload[H, Tx]) error // VerifyPrepareResponse performs external PrepareResponse verification and returns nil if it's successful. - VerifyPrepareResponse func(p ConsensusPayload[H]) error + VerifyPrepareResponse func(p ConsensusPayload[H, Tx]) 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 + VerifyPreCommit func(p ConsensusPayload[H, Tx]) error // VerifyCommit performs external Commit verification and returns nil if it's successful. // Note that Block-dependent Commit verification should be performed inside Block.Verify // callback. - VerifyCommit func(p ConsensusPayload[H]) error + VerifyCommit func(p ConsensusPayload[H, Tx]) error } const defaultSecondsPerBlock = time.Second * 15 const defaultTimestampIncrement = uint64(time.Millisecond / time.Nanosecond) -func defaultConfig[H Hash]() *Config[H] { +func defaultConfig[H Hash, Tx Transaction[H]]() *Config[H, Tx] { // fields which are set to nil must be provided from client - return &Config[H]{ + return &Config[H, Tx]{ Logger: zap.NewNop(), TimePerBlock: func() time.Duration { return defaultSecondsPerBlock }, TimestampIncrement: defaultTimestampIncrement, GetKeyPair: nil, RequestTx: func(iter.Seq[H]) {}, StopTxFlow: func() {}, - GetVerified: func() []Transaction[H] { return make([]Transaction[H], 0) }, - VerifyBlock: func(Block[H]) bool { return true }, - Broadcast: func(ConsensusPayload[H]) {}, - ProcessBlock: func(Block[H]) error { return nil }, - GetBlock: func(H) Block[H] { return nil }, + GetVerified: func() []Tx { return make([]Tx, 0) }, + VerifyBlock: func(Block[H, Tx]) bool { return true }, + Broadcast: func(ConsensusPayload[H, Tx]) {}, + ProcessBlock: func(Block[H, Tx]) error { return nil }, + GetBlock: func(H) Block[H, Tx] { return nil }, WatchOnly: func() bool { return false }, CurrentHeight: nil, CurrentBlockHash: nil, GetValidators: nil, - VerifyPrepareRequest: func(ConsensusPayload[H]) error { return nil }, - VerifyPrepareResponse: func(ConsensusPayload[H]) error { return nil }, - VerifyCommit: func(ConsensusPayload[H]) error { return nil }, + VerifyPrepareRequest: func(ConsensusPayload[H, Tx]) error { return nil }, + VerifyPrepareResponse: func(ConsensusPayload[H, Tx]) error { return nil }, + VerifyCommit: func(ConsensusPayload[H, Tx]) error { return nil }, AntiMEVExtensionEnablingHeight: -1, - VerifyPreBlock: func(PreBlock[H]) bool { return true }, - VerifyPreCommit: func(ConsensusPayload[H]) error { return nil }, + VerifyPreBlock: func(PreBlock[H, Tx]) bool { return true }, + VerifyPreCommit: func(ConsensusPayload[H, Tx]) error { return nil }, } } -func checkConfig[H Hash](cfg *Config[H]) error { +func checkConfig[H Hash, Tx Transaction[H]](cfg *Config[H, Tx]) error { if cfg.GetKeyPair == nil { return errors.New("private key is nil") } @@ -210,247 +210,247 @@ func checkConfig[H Hash](cfg *Config[H]) error { } // WithGetKeyPair sets GetKeyPair. -func WithGetKeyPair[H Hash](f func(pubs []PublicKey) (int, PrivateKey, PublicKey)) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithGetKeyPair[H Hash, Tx Transaction[H]](f func(pubs []PublicKey) (int, PrivateKey, PublicKey)) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.GetKeyPair = f } } // WithLogger sets Logger. -func WithLogger[H Hash](log *zap.Logger) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithLogger[H Hash, Tx Transaction[H]](log *zap.Logger) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.Logger = log } } // WithTimer sets Timer. -func WithTimer[H Hash](t Timer) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithTimer[H Hash, Tx Transaction[H]](t Timer) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.Timer = t } } // WithTimePerBlock sets TimePerBlock. -func WithTimePerBlock[H Hash](f func() time.Duration) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithTimePerBlock[H Hash, Tx Transaction[H]](f func() time.Duration) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.TimePerBlock = f } } // WithMaxTimePerBlock sets MaxTimePerBlock. -func WithMaxTimePerBlock[H Hash](f func() time.Duration) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithMaxTimePerBlock[H Hash, Tx Transaction[H]](f func() time.Duration) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.MaxTimePerBlock = f } } // WithAntiMEVExtensionEnablingHeight sets AntiMEVExtensionEnablingHeight. -func WithAntiMEVExtensionEnablingHeight[H Hash](h int64) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithAntiMEVExtensionEnablingHeight[H Hash, Tx Transaction[H]](h int64) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.AntiMEVExtensionEnablingHeight = h } } // WithTimestampIncrement sets TimestampIncrement. -func WithTimestampIncrement[H Hash](u uint64) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithTimestampIncrement[H Hash, Tx Transaction[H]](u uint64) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { 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]) { +func WithNewPreBlockFromContext[H Hash, Tx Transaction[H]](f func(ctx *Context[H, Tx]) PreBlock[H, Tx]) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { 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]) { +func WithNewBlockFromContext[H Hash, Tx Transaction[H]](f func(ctx *Context[H, Tx]) Block[H, Tx]) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.NewBlockFromContext = f } } // WithRequestTx sets RequestTx. -func WithRequestTx[H Hash](f func(hs iter.Seq[H])) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithRequestTx[H Hash, Tx Transaction[H]](f func(hs iter.Seq[H])) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.RequestTx = f } } // WithSubscribeForTxs sets SubscribeForTxs. -func WithSubscribeForTxs[H Hash](f func()) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithSubscribeForTxs[H Hash, Tx Transaction[H]](f func()) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.SubscribeForTxs = f } } // WithStopTxFlow sets StopTxFlow. -func WithStopTxFlow[H Hash](f func()) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithStopTxFlow[H Hash, Tx Transaction[H]](f func()) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.StopTxFlow = f } } // WithGetVerified sets GetVerified. -func WithGetVerified[H Hash](f func() []Transaction[H]) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithGetVerified[H Hash, Tx Transaction[H]](f func() []Tx) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.GetVerified = f } } // WithVerifyPreBlock sets VerifyPreBlock. -func WithVerifyPreBlock[H Hash](f func(b PreBlock[H]) bool) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithVerifyPreBlock[H Hash, Tx Transaction[H]](f func(b PreBlock[H, Tx]) bool) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { 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]) { +func WithVerifyBlock[H Hash, Tx Transaction[H]](f func(b Block[H, Tx]) bool) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.VerifyBlock = f } } // WithBroadcast sets Broadcast. -func WithBroadcast[H Hash](f func(m ConsensusPayload[H])) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithBroadcast[H Hash, Tx Transaction[H]](f func(m ConsensusPayload[H, Tx])) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.Broadcast = f } } // WithProcessBlock sets ProcessBlock callback. Note that for anti-MEV extension // disabled non-nil error return is a no-op. -func WithProcessBlock[H Hash](f func(b Block[H]) error) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithProcessBlock[H Hash, Tx Transaction[H]](f func(b Block[H, Tx]) error) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.ProcessBlock = f } } // WithProcessPreBlock sets ProcessPreBlock. -func WithProcessPreBlock[H Hash](f func(b PreBlock[H]) error) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithProcessPreBlock[H Hash, Tx Transaction[H]](f func(b PreBlock[H, Tx]) error) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { 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]) { +func WithGetBlock[H Hash, Tx Transaction[H]](f func(h H) Block[H, Tx]) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.GetBlock = f } } // WithWatchOnly sets WatchOnly. -func WithWatchOnly[H Hash](f func() bool) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithWatchOnly[H Hash, Tx Transaction[H]](f func() bool) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.WatchOnly = f } } // WithCurrentHeight sets CurrentHeight. -func WithCurrentHeight[H Hash](f func() uint32) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithCurrentHeight[H Hash, Tx Transaction[H]](f func() uint32) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.CurrentHeight = f } } // WithCurrentBlockHash sets CurrentBlockHash. -func WithCurrentBlockHash[H Hash](f func() H) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithCurrentBlockHash[H Hash, Tx Transaction[H]](f func() H) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.CurrentBlockHash = f } } // WithGetValidators sets GetValidators. -func WithGetValidators[H Hash](f func(txs ...Transaction[H]) []PublicKey) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithGetValidators[H Hash, Tx Transaction[H]](f func(txs ...Tx) []PublicKey) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.GetValidators = f } } // WithNewConsensusPayload sets NewConsensusPayload. -func WithNewConsensusPayload[H Hash](f func(ctx *Context[H], typ MessageType, msg any) ConsensusPayload[H]) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithNewConsensusPayload[H Hash, Tx Transaction[H]](f func(ctx *Context[H, Tx], typ MessageType, msg any) ConsensusPayload[H, Tx]) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.NewConsensusPayload = f } } // WithNewPrepareRequest sets NewPrepareRequest. -func WithNewPrepareRequest[H Hash](f func(ts uint64, nonce uint64, transactionHashes []Transaction[H]) PrepareRequest[H]) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithNewPrepareRequest[H Hash, Tx Transaction[H]](f func(ts uint64, nonce uint64, transactionHashes []Tx) PrepareRequest[H, Tx]) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.NewPrepareRequest = f } } // WithNewPrepareResponse sets NewPrepareResponse. -func WithNewPrepareResponse[H Hash](f func(preparationHash H) PrepareResponse[H]) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithNewPrepareResponse[H Hash, Tx Transaction[H]](f func(preparationHash H) PrepareResponse[H]) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.NewPrepareResponse = f } } // WithNewChangeView sets NewChangeView. -func WithNewChangeView[H Hash](f func(newViewNumber byte, reason ChangeViewReason, ts uint64) ChangeView) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithNewChangeView[H Hash, Tx Transaction[H]](f func(newViewNumber byte, reason ChangeViewReason, ts uint64) ChangeView) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.NewChangeView = f } } // WithNewCommit sets NewCommit. -func WithNewCommit[H Hash](f func(signature []byte) Commit) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithNewCommit[H Hash, Tx Transaction[H]](f func(signature []byte) Commit) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.NewCommit = f } } // WithNewPreCommit sets NewPreCommit. -func WithNewPreCommit[H Hash](f func(signature []byte) PreCommit) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithNewPreCommit[H Hash, Tx Transaction[H]](f func(signature []byte) PreCommit) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.NewPreCommit = f } } // WithNewRecoveryRequest sets NewRecoveryRequest. -func WithNewRecoveryRequest[H Hash](f func(ts uint64) RecoveryRequest) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithNewRecoveryRequest[H Hash, Tx Transaction[H]](f func(ts uint64) RecoveryRequest) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.NewRecoveryRequest = f } } // WithNewRecoveryMessage sets NewRecoveryMessage. -func WithNewRecoveryMessage[H Hash](f func() RecoveryMessage[H]) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithNewRecoveryMessage[H Hash, Tx Transaction[H]](f func() RecoveryMessage[H, Tx]) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.NewRecoveryMessage = f } } // WithVerifyPrepareRequest sets VerifyPrepareRequest. -func WithVerifyPrepareRequest[H Hash](f func(prepareReq ConsensusPayload[H]) error) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithVerifyPrepareRequest[H Hash, Tx Transaction[H]](f func(prepareReq ConsensusPayload[H, Tx]) error) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.VerifyPrepareRequest = f } } // WithVerifyPrepareResponse sets VerifyPrepareResponse. -func WithVerifyPrepareResponse[H Hash](f func(prepareResp ConsensusPayload[H]) error) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithVerifyPrepareResponse[H Hash, Tx Transaction[H]](f func(prepareResp ConsensusPayload[H, Tx]) error) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { 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]) { +func WithVerifyPreCommit[H Hash, Tx Transaction[H]](f func(preCommit ConsensusPayload[H, Tx]) error) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.VerifyPreCommit = f } } // WithVerifyCommit sets VerifyCommit. -func WithVerifyCommit[H Hash](f func(commit ConsensusPayload[H]) error) func(config *Config[H]) { - return func(cfg *Config[H]) { +func WithVerifyCommit[H Hash, Tx Transaction[H]](f func(commit ConsensusPayload[H, Tx]) error) func(config *Config[H, Tx]) { + return func(cfg *Config[H, Tx]) { cfg.VerifyCommit = f } } diff --git a/consensus_message.go b/consensus_message.go index 01c9d2b7b..b4e026554 100644 --- a/consensus_message.go +++ b/consensus_message.go @@ -1,7 +1,7 @@ package dbft // ConsensusMessage is an interface for generic dBFT message. -type ConsensusMessage[H Hash] interface { +type ConsensusMessage[H Hash, Tx Transaction[H]] interface { // ViewNumber returns view number when this message was originated. ViewNumber() byte // Type returns type of this message. @@ -12,7 +12,7 @@ type ConsensusMessage[H Hash] interface { // GetChangeView returns payload as if it was ChangeView. GetChangeView() ChangeView // GetPrepareRequest returns payload as if it was PrepareRequest. - GetPrepareRequest() PrepareRequest[H] + GetPrepareRequest() PrepareRequest[H, Tx] // GetPrepareResponse returns payload as if it was PrepareResponse. GetPrepareResponse() PrepareResponse[H] // GetPreCommit returns payload as if it was PreCommit. @@ -22,5 +22,5 @@ type ConsensusMessage[H Hash] interface { // GetRecoveryRequest returns payload as if it was RecoveryRequest. GetRecoveryRequest() RecoveryRequest // GetRecoveryMessage returns payload as if it was RecoveryMessage. - GetRecoveryMessage() RecoveryMessage[H] + GetRecoveryMessage() RecoveryMessage[H, Tx] } diff --git a/consensus_payload.go b/consensus_payload.go index c24699efa..0a13af025 100644 --- a/consensus_payload.go +++ b/consensus_payload.go @@ -2,8 +2,8 @@ package dbft // ConsensusPayload is a generic payload type which is exchanged // between the nodes. -type ConsensusPayload[H Hash] interface { - ConsensusMessage[H] +type ConsensusPayload[H Hash, Tx Transaction[H]] interface { + ConsensusMessage[H, Tx] // ValidatorIndex returns index of validator from which // payload was originated from. diff --git a/context.go b/context.go index b0e609106..56ccdc9f3 100644 --- a/context.go +++ b/context.go @@ -14,19 +14,19 @@ type HeightView struct { // Context is a main dBFT structure which // contains all information needed for performing transitions. -type Context[H Hash] struct { +type Context[H Hash, Tx Transaction[H]] struct { // Config is dBFT's Config instance. - Config *Config[H] + Config *Config[H, Tx] // Priv is node's private key. Priv PrivateKey // Pub is node's public key. Pub PublicKey - preBlock PreBlock[H] - preHeader PreBlock[H] - block Block[H] - header Block[H] + preBlock PreBlock[H, Tx] + preHeader PreBlock[H, Tx] + block Block[H, Tx] + header Block[H, Tx] // blockProcessed denotes whether Config.ProcessBlock callback was called for the current // height. If so, then no second call must happen. After new block is received by the user, // dBFT stops any new transaction or messages processing as far as timeouts handling till @@ -56,14 +56,14 @@ type Context[H Hash] struct { Timestamp uint64 Nonce uint64 // Transactions is a slice containing actual transactions for the current block. - Transactions []Transaction[H] + Transactions []Tx // MissingTransactions is a map of a missing transaction hash to its index in // the slice of proposed transactions for the current block. This map is // managed by dBFT and should not be changed by the user. MissingTransactions map[H]int // PreparationPayloads stores consensus Prepare* payloads for the current epoch. - PreparationPayloads []ConsensusPayload[H] + PreparationPayloads []ConsensusPayload[H, Tx] // PreCommitPayloads stores consensus PreCommit payloads sent through all epochs // as a part of anti-MEV dBFT extension. It is assumed that valid PreCommit // payloads can only be sent once by a single node per the whole set of consensus @@ -71,18 +71,18 @@ type Context[H Hash] struct { // list immediately (if PrepareRequest was received for the current round, so // it's possible to verify PreCommit against PreBlock built on PrepareRequest) // or stored till the corresponding PrepareRequest receiving. - PreCommitPayloads []ConsensusPayload[H] + PreCommitPayloads []ConsensusPayload[H, Tx] // CommitPayloads stores consensus Commit payloads sent throughout all epochs. It // is assumed that valid Commit payload can only be sent once by a single node per // the whole set of consensus epochs for particular block. Invalid commit payloads // are kicked off this list immediately (if PrepareRequest was received for the // current round, so it's possible to verify Commit against it) or stored till // the corresponding PrepareRequest receiving. - CommitPayloads []ConsensusPayload[H] + CommitPayloads []ConsensusPayload[H, Tx] // ChangeViewPayloads stores consensus ChangeView payloads for the current epoch. - ChangeViewPayloads []ConsensusPayload[H] + ChangeViewPayloads []ConsensusPayload[H, Tx] // LastChangeViewPayloads stores consensus ChangeView payloads for the last epoch. - LastChangeViewPayloads []ConsensusPayload[H] + LastChangeViewPayloads []ConsensusPayload[H, Tx] // LastSeenMessage array stores the height and view of the last seen message, for each validator. // If this node never heard a thing from validator i, LastSeenMessage[i] will be nil. LastSeenMessage []*HeightView @@ -100,16 +100,16 @@ type Context[H Hash] struct { } // N returns total number of validators. -func (c *Context[H]) N() int { return len(c.Validators) } +func (c *Context[H, Tx]) N() int { return len(c.Validators) } // F returns number of validators which can be faulty. -func (c *Context[H]) F() int { return (len(c.Validators) - 1) / 3 } +func (c *Context[H, Tx]) F() int { return (len(c.Validators) - 1) / 3 } // M returns number of validators which must function correctly. -func (c *Context[H]) M() int { return len(c.Validators) - c.F() } +func (c *Context[H, Tx]) M() int { return len(c.Validators) - c.F() } // GetPrimaryIndex returns index of a primary node for the specified view. -func (c *Context[H]) GetPrimaryIndex(viewNumber byte) uint { +func (c *Context[H, Tx]) GetPrimaryIndex(viewNumber byte) uint { p := (int(c.BlockIndex) - int(viewNumber)) % len(c.Validators) if p >= 0 { return uint(p) @@ -119,19 +119,19 @@ func (c *Context[H]) GetPrimaryIndex(viewNumber byte) uint { } // IsPrimary returns true iff node is primary for current height and view. -func (c *Context[H]) IsPrimary() bool { return c.MyIndex == int(c.PrimaryIndex) } +func (c *Context[H, Tx]) IsPrimary() bool { return c.MyIndex == int(c.PrimaryIndex) } // IsBackup returns true iff node is backup for current height and view. -func (c *Context[H]) IsBackup() bool { +func (c *Context[H, Tx]) IsBackup() bool { return c.MyIndex >= 0 && !c.IsPrimary() } // WatchOnly returns true iff node takes no active part in consensus. -func (c *Context[H]) WatchOnly() bool { return c.MyIndex < 0 || c.Config.WatchOnly() } +func (c *Context[H, Tx]) WatchOnly() bool { return c.MyIndex < 0 || c.Config.WatchOnly() } // CountCommitted returns number of received Commit (or PreCommit for anti-MEV // extension) messages not only for the current epoch but also for any other epoch. -func (c *Context[H]) CountCommitted() (count int) { +func (c *Context[H, Tx]) CountCommitted() (count int) { for i := range c.CommitPayloads { // Consider both Commit and PreCommit payloads since both Commit and PreCommit // phases are one-directional (do not impose view change). @@ -145,7 +145,7 @@ func (c *Context[H]) CountCommitted() (count int) { // CountFailed returns number of nodes with which no communication was performed // for this view and that hasn't sent the Commit message at the previous views. -func (c *Context[H]) CountFailed() (count int) { +func (c *Context[H, Tx]) CountFailed() (count int) { for i, hv := range c.LastSeenMessage { if (c.CommitPayloads[i] == nil && c.PreCommitPayloads[i] == nil) && (hv == nil || hv.Height < c.BlockIndex || hv.View < c.ViewNumber) { @@ -158,24 +158,24 @@ func (c *Context[H]) CountFailed() (count int) { // RequestSentOrReceived returns true iff PrepareRequest // was sent or received for the current epoch. -func (c *Context[H]) RequestSentOrReceived() bool { +func (c *Context[H, Tx]) RequestSentOrReceived() bool { return c.PreparationPayloads[c.PrimaryIndex] != nil } // ResponseSent returns true iff Prepare* message was sent for the current epoch. -func (c *Context[H]) ResponseSent() bool { +func (c *Context[H, Tx]) ResponseSent() bool { return !c.WatchOnly() && c.PreparationPayloads[c.MyIndex] != nil } // PreCommitSent returns true iff PreCommit message was sent for the current epoch // assuming that the node can't go further than current epoch after PreCommit was sent. -func (c *Context[H]) PreCommitSent() bool { +func (c *Context[H, Tx]) PreCommitSent() bool { return !c.WatchOnly() && c.PreCommitPayloads[c.MyIndex] != nil } // CommitSent returns true iff Commit message was sent for the current epoch // assuming that the node can't go further than current epoch after commit was sent. -func (c *Context[H]) CommitSent() bool { +func (c *Context[H, Tx]) CommitSent() bool { return !c.WatchOnly() && c.CommitPayloads[c.MyIndex] != nil } @@ -192,10 +192,10 @@ func (c *Context[H]) CommitSent() bool { // several places where the call to CreateBlock happens (one of them is right after // PrepareRequest receiving). Thus, we have a separate Context.blockProcessed field // for the described purpose. -func (c *Context[H]) BlockSent() bool { return c.blockProcessed } +func (c *Context[H, Tx]) BlockSent() bool { return c.blockProcessed } // ViewChanging returns true iff node is in a process of changing view. -func (c *Context[H]) ViewChanging() bool { +func (c *Context[H, Tx]) ViewChanging() bool { if c.WatchOnly() { return false } @@ -206,7 +206,7 @@ func (c *Context[H]) ViewChanging() bool { } // NotAcceptingPayloadsDueToViewChanging returns true if node should not accept new payloads. -func (c *Context[H]) NotAcceptingPayloadsDueToViewChanging() bool { +func (c *Context[H, Tx]) NotAcceptingPayloadsDueToViewChanging() bool { return c.ViewChanging() && !c.MoreThanFNodesCommittedOrLost() } @@ -217,30 +217,30 @@ func (c *Context[H]) NotAcceptingPayloadsDueToViewChanging() bool { // asking change views loses network or crashes and comes back when nodes are committed in more than one higher // numbered view, it is possible for the node accepting recovery to commit in any of the higher views, thus // potentially splitting nodes among views and stalling the network. -func (c *Context[H]) MoreThanFNodesCommittedOrLost() bool { +func (c *Context[H, Tx]) MoreThanFNodesCommittedOrLost() bool { return c.CountCommitted()+c.CountFailed() > c.F() } // Header returns current header from context. May be nil in case if no // header is constructed yet. Do not change the resulting header. -func (c *Context[H]) Header() Block[H] { +func (c *Context[H, Tx]) Header() Block[H, Tx] { return c.header } // PreHeader returns current preHeader from context. May be nil in case if no // preHeader is constructed yet. Do not change the resulting preHeader. -func (c *Context[H]) PreHeader() PreBlock[H] { +func (c *Context[H, Tx]) PreHeader() PreBlock[H, Tx] { return c.preHeader } // PreBlock returns current PreBlock from context. May be nil in case if no // PreBlock is constructed yet (even if PreHeader is already constructed). // External changes in the PreBlock will be seen by dBFT. -func (c *Context[H]) PreBlock() PreBlock[H] { +func (c *Context[H, Tx]) PreBlock() PreBlock[H, Tx] { return c.preBlock } -func (c *Context[H]) reset(view byte, ts uint64) { +func (c *Context[H, Tx]) reset(view byte, ts uint64) { c.MyIndex = -1 c.prepareSentTime = time.Time{} c.lastBlockTimestamp = ts @@ -308,7 +308,7 @@ func emptyReusableSlice[E any](s []E, n int) []E { // Fill initializes consensus when node is a speaker. It doesn't perform any // context modifications if MaxTimePerBlock extension is enabled and there are // no transactions in the memory pool and force is not set. -func (c *Context[H]) Fill(force bool) bool { +func (c *Context[H, Tx]) Fill(force bool) bool { txx := c.Config.GetVerified() if c.Config.MaxTimePerBlock != nil && !force && len(txx) == 0 { return false @@ -329,12 +329,12 @@ func (c *Context[H]) Fill(force bool) bool { // getTimestamp returns nanoseconds-precision timestamp using // current context config. -func (c *Context[H]) getTimestamp() uint64 { +func (c *Context[H, Tx]) getTimestamp() uint64 { return uint64(c.Config.Timer.Now().UnixNano()) / c.Config.TimestampIncrement * c.Config.TimestampIncrement } // CreateBlock returns resulting block for the current epoch. -func (c *Context[H]) CreateBlock() Block[H] { +func (c *Context[H, Tx]) CreateBlock() Block[H, Tx] { if c.block == nil { if c.block = c.MakeHeader(); c.block == nil { return nil @@ -352,7 +352,7 @@ func (c *Context[H]) CreateBlock() Block[H] { } // CreatePreBlock returns PreBlock for the current epoch. -func (c *Context[H]) CreatePreBlock() PreBlock[H] { +func (c *Context[H, Tx]) CreatePreBlock() PreBlock[H, Tx] { if c.preBlock == nil { if c.preBlock = c.MakePreHeader(); c.preBlock == nil { return nil @@ -366,13 +366,13 @@ func (c *Context[H]) CreatePreBlock() PreBlock[H] { // isAntiMEVExtensionEnabled returns whether Anti-MEV dBFT extension is enabled // at the currently processing block height. -func (c *Context[H]) isAntiMEVExtensionEnabled() bool { +func (c *Context[H, Tx]) isAntiMEVExtensionEnabled() bool { return c.Config.AntiMEVExtensionEnablingHeight >= 0 && uint32(c.Config.AntiMEVExtensionEnablingHeight) <= c.BlockIndex } // MakeHeader returns half-filled block for the current epoch. // All hashable fields will be filled. -func (c *Context[H]) MakeHeader() Block[H] { +func (c *Context[H, Tx]) MakeHeader() Block[H, Tx] { if c.header == nil { if !c.RequestSentOrReceived() { return nil @@ -393,7 +393,7 @@ func (c *Context[H]) MakeHeader() Block[H] { // MakePreHeader returns half-filled block for the current epoch. // All hashable fields will be filled. -func (c *Context[H]) MakePreHeader() PreBlock[H] { +func (c *Context[H, Tx]) MakePreHeader() PreBlock[H, Tx] { if c.preHeader == nil { if !c.RequestSentOrReceived() { return nil @@ -406,15 +406,15 @@ func (c *Context[H]) MakePreHeader() PreBlock[H] { // hasAllTransactions returns true iff all transactions were received // for the proposed block. -func (c *Context[H]) hasAllTransactions() bool { +func (c *Context[H, Tx]) hasAllTransactions() bool { return len(c.MissingTransactions) == 0 } -func (c *Context[H]) subscribeForTransactions() { +func (c *Context[H, Tx]) subscribeForTransactions() { c.txSubscriptionOn = true c.Config.SubscribeForTxs() } -func (c *Context[H]) unsubscribeFromTransactions() { +func (c *Context[H, Tx]) unsubscribeFromTransactions() { c.txSubscriptionOn = false } diff --git a/dbft.go b/dbft.go index 632621d18..5346512af 100644 --- a/dbft.go +++ b/dbft.go @@ -14,12 +14,12 @@ type ( // and [Config] (service configuration). Data exposed from these fields // is supposed to be read-only, state is changed via methods of this // structure. - DBFT[H Hash] struct { - Context[H] - Config[H] + DBFT[H Hash, Tx Transaction[H]] struct { + Context[H, Tx] + Config[H, Tx] *sync.Mutex - cache cache[H] + cache cache[H, Tx] recovering bool } ) @@ -28,8 +28,8 @@ type ( // using provided options or nil and error if some of the options are missing or invalid. // H and A generic parameters are used as hash and address representation for // dBFT consensus messages, blocks and transactions. -func New[H Hash](options ...func(config *Config[H])) (*DBFT[H], error) { - cfg := defaultConfig[H]() +func New[H Hash, Tx Transaction[H]](options ...func(config *Config[H, Tx])) (*DBFT[H, Tx], error) { + cfg := defaultConfig[H, Tx]() for _, option := range options { option(cfg) @@ -39,10 +39,10 @@ func New[H Hash](options ...func(config *Config[H])) (*DBFT[H], error) { return nil, fmt.Errorf("invalid config: %w", err) } - d := &DBFT[H]{ + d := &DBFT[H, Tx]{ Mutex: new(sync.Mutex), Config: *cfg, - Context: Context[H]{ + Context: Context[H, Tx]{ Config: cfg, }, } @@ -50,7 +50,7 @@ func New[H Hash](options ...func(config *Config[H])) (*DBFT[H], error) { return d, nil } -func (d *DBFT[H]) addTransaction(tx Transaction[H]) { +func (d *DBFT[H, Tx]) addTransaction(tx Tx) { i, ok := d.MissingTransactions[tx.Hash()] if !ok { return @@ -77,8 +77,8 @@ func (d *DBFT[H]) addTransaction(tx Transaction[H]) { // Start initializes dBFT instance and starts the protocol if node is primary. // It accepts the timestamp of the previous block. It should be called once // per DBFT lifetime. -func (d *DBFT[H]) Start(ts uint64) { - d.cache = newCache[H]() +func (d *DBFT[H, Tx]) Start(ts uint64) { + d.cache = newCache[H, Tx]() d.initializeConsensus(0, ts) if d.IsPrimary() { d.sendPrepareRequest(true) @@ -90,11 +90,11 @@ func (d *DBFT[H]) Start(ts uint64) { // after new block is processed by ledger (the block can come from dBFT or be // received by other means). The height is to be derived from the configured // CurrentHeight callback and view will be set to 0. -func (d *DBFT[H]) Reset(ts uint64) { +func (d *DBFT[H, Tx]) Reset(ts uint64) { d.initializeConsensus(0, ts) } -func (d *DBFT[H]) initializeConsensus(view byte, ts uint64) { +func (d *DBFT[H, Tx]) initializeConsensus(view byte, ts uint64) { d.reset(view, ts) var role string @@ -166,7 +166,7 @@ func (d *DBFT[H]) initializeConsensus(view byte, ts uint64) { // OnTransaction notifies service about receiving new transaction from the // proposed list of transactions. -func (d *DBFT[H]) OnTransaction(tx Transaction[H]) { +func (d *DBFT[H, Tx]) OnTransaction(tx Tx) { // d.Logger.Debug("OnTransaction", // zap.Bool("backup", d.IsBackup()), // zap.Bool("not_accepting", d.NotAcceptingPayloadsDueToViewChanging()), @@ -183,20 +183,20 @@ func (d *DBFT[H]) OnTransaction(tx Transaction[H]) { } // OnTimeout advances state machine as if timeout was fired. -func (d *DBFT[H]) OnTimeout(height uint32, view byte) { +func (d *DBFT[H, Tx]) OnTimeout(height uint32, view byte) { d.onTimeout(height, view, false) } // OnNewTransaction advances state machine if transactions subscription is active // and there's a new transaction added to the node pool. -func (d *DBFT[H]) OnNewTransaction() { +func (d *DBFT[H, Tx]) OnNewTransaction() { if !d.txSubscriptionOn { return } d.onTimeout(d.Timer.Height(), d.Timer.View(), true) } -func (d *DBFT[H]) onTimeout(height uint32, view byte, force bool) { +func (d *DBFT[H, Tx]) onTimeout(height uint32, view byte, force bool) { if d.Context.WatchOnly() || d.BlockSent() { return } @@ -241,7 +241,7 @@ func (d *DBFT[H]) onTimeout(height uint32, view byte, force bool) { } // OnReceive advances state machine in accordance with msg. -func (d *DBFT[H]) OnReceive(msg ConsensusPayload[H]) { +func (d *DBFT[H, Tx]) OnReceive(msg ConsensusPayload[H, Tx]) { if int(msg.ValidatorIndex()) >= len(d.Validators) { d.Logger.Error("too big validator index", zap.Uint16("from", msg.ValidatorIndex())) return @@ -311,7 +311,7 @@ func (d *DBFT[H]) OnReceive(msg ConsensusPayload[H]) { } } -func (d *DBFT[H]) onPrepareRequest(msg ConsensusPayload[H]) { +func (d *DBFT[H, Tx]) onPrepareRequest(msg ConsensusPayload[H, Tx]) { // ignore prepareRequest if we had already received it or // are in process of changing view if d.RequestSentOrReceived() { // || (d.ViewChanging() && !d.MoreThanFNodesCommittedOrLost()) { @@ -360,7 +360,7 @@ func (d *DBFT[H]) onPrepareRequest(msg ConsensusPayload[H]) { } // processMissingTx fills in the map of missing transactions and requests them. -func (d *DBFT[H]) processMissingTx() { +func (d *DBFT[H, Tx]) processMissingTx() { if len(d.MissingTransactions) != 0 { d.Logger.Info("missing tx", zap.Int("count", len(d.MissingTransactions))) @@ -372,7 +372,7 @@ func (d *DBFT[H]) processMissingTx() { // the new proposed block, if it's fine it returns true, if something is wrong // with it, it sends a changeView request and returns false. It's only valid to // call it when all transactions for this block are already collected. -func (d *DBFT[H]) createAndCheckBlock() bool { +func (d *DBFT[H, Tx]) createAndCheckBlock() bool { var blockOK bool if d.isAntiMEVExtensionEnabled() { b := d.CreatePreBlock() @@ -396,7 +396,7 @@ func (d *DBFT[H]) createAndCheckBlock() bool { // updateExistingPayloads is called _only_ from onPrepareRequest, it validates // payloads we may have received before PrepareRequest. -func (d *DBFT[H]) updateExistingPayloads(msg ConsensusPayload[H]) { +func (d *DBFT[H, Tx]) updateExistingPayloads(msg ConsensusPayload[H, Tx]) { for i, m := range d.PreparationPayloads { if m != nil && m.Type() == PrepareResponseType { resp := m.GetPrepareResponse() @@ -416,7 +416,7 @@ func (d *DBFT[H]) updateExistingPayloads(msg ConsensusPayload[H]) { // verifyPreCommitPayloadsAgainstPreBlock performs verification of PreCommit payloads // against generated PreBlock. -func (d *DBFT[H]) verifyPreCommitPayloadsAgainstPreBlock() { +func (d *DBFT[H, Tx]) verifyPreCommitPayloadsAgainstPreBlock() { if !d.hasAllTransactions() { return } @@ -437,7 +437,7 @@ func (d *DBFT[H]) verifyPreCommitPayloadsAgainstPreBlock() { // verifyCommitPayloadsAgainstHeader performs verification of commit payloads // against generated header. -func (d *DBFT[H]) verifyCommitPayloadsAgainstHeader() { +func (d *DBFT[H, Tx]) verifyCommitPayloadsAgainstHeader() { for i, m := range d.CommitPayloads { if m != nil && m.ViewNumber() == d.ViewNumber { if header := d.MakeHeader(); header != nil { @@ -451,7 +451,7 @@ func (d *DBFT[H]) verifyCommitPayloadsAgainstHeader() { } } -func (d *DBFT[H]) onPrepareResponse(msg ConsensusPayload[H]) { +func (d *DBFT[H, Tx]) onPrepareResponse(msg ConsensusPayload[H, Tx]) { if d.ViewNumber != msg.ViewNumber() { d.Logger.Debug("ignoring wrong view number", zap.Uint("view", uint(msg.ViewNumber()))) return @@ -507,7 +507,7 @@ func (d *DBFT[H]) onPrepareResponse(msg ConsensusPayload[H]) { } } -func (d *DBFT[H]) onChangeView(msg ConsensusPayload[H]) { +func (d *DBFT[H, Tx]) onChangeView(msg ConsensusPayload[H, Tx]) { p := msg.GetChangeView() if p.NewViewNumber() <= d.ViewNumber { @@ -538,7 +538,7 @@ func (d *DBFT[H]) onChangeView(msg ConsensusPayload[H]) { d.checkChangeView(p.NewViewNumber()) } -func (d *DBFT[H]) onPreCommit(msg ConsensusPayload[H]) { +func (d *DBFT[H, Tx]) onPreCommit(msg ConsensusPayload[H, Tx]) { existing := d.PreCommitPayloads[msg.ValidatorIndex()] if existing != nil { if existing.Hash() != msg.Hash() { @@ -588,7 +588,7 @@ func (d *DBFT[H]) onPreCommit(msg ConsensusPayload[H]) { ) } -func (d *DBFT[H]) onCommit(msg ConsensusPayload[H]) { +func (d *DBFT[H, Tx]) onCommit(msg ConsensusPayload[H, Tx]) { existing := d.CommitPayloads[msg.ValidatorIndex()] if existing != nil { if existing.Hash() != msg.Hash() { @@ -635,7 +635,7 @@ func (d *DBFT[H]) onCommit(msg ConsensusPayload[H]) { ) } -func (d *DBFT[H]) onRecoveryRequest(msg ConsensusPayload[H]) { +func (d *DBFT[H, Tx]) onRecoveryRequest(msg ConsensusPayload[H, Tx]) { // Only validators are allowed to send consensus messages. if d.Context.WatchOnly() { return @@ -655,7 +655,7 @@ func (d *DBFT[H]) onRecoveryRequest(msg ConsensusPayload[H]) { d.sendRecoveryMessage() } -func (d *DBFT[H]) onRecoveryMessage(msg ConsensusPayload[H]) { +func (d *DBFT[H, Tx]) onRecoveryMessage(msg ConsensusPayload[H, Tx]) { d.Logger.Debug("recovery message received", zap.Any("dump", msg)) var ( @@ -721,7 +721,7 @@ func (d *DBFT[H]) onRecoveryMessage(msg ConsensusPayload[H]) { } } -func (d *DBFT[H]) changeTimer(delay time.Duration) { +func (d *DBFT[H, Tx]) changeTimer(delay time.Duration) { d.Logger.Debug("reset timer", zap.Uint32("h", d.BlockIndex), zap.Int("v", int(d.ViewNumber)), @@ -729,7 +729,7 @@ func (d *DBFT[H]) changeTimer(delay time.Duration) { d.Timer.Reset(d.BlockIndex, d.ViewNumber, delay) } -func (d *DBFT[H]) extendTimer(count int) { +func (d *DBFT[H, Tx]) extendTimer(count int) { if !d.CommitSent() && (!d.isAntiMEVExtensionEnabled() || !d.PreCommitSent()) && !d.ViewChanging() { d.Timer.Extend(time.Duration(count) * d.timePerBlock / time.Duration(d.M())) } diff --git a/dbft_test.go b/dbft_test.go index 983486ee5..c96fc1998 100644 --- a/dbft_test.go +++ b/dbft_test.go @@ -14,7 +14,7 @@ import ( "go.uber.org/zap" ) -type Payload = dbft.ConsensusPayload[crypto.Uint256] +type Payload = dbft.ConsensusPayload[crypto.Uint256, *consensus.Tx64] type testState struct { myIndex int @@ -24,9 +24,9 @@ type testState struct { ch []Payload currHeight uint32 currHash crypto.Uint256 - preBlocks []dbft.PreBlock[crypto.Uint256] - blocks []dbft.Block[crypto.Uint256] - verify func(b dbft.Block[crypto.Uint256]) bool + preBlocks []dbft.PreBlock[crypto.Uint256, *consensus.Tx64] + blocks []dbft.Block[crypto.Uint256, *consensus.Tx64] + verify func(b dbft.Block[crypto.Uint256, *consensus.Tx64]) bool } const debugTests = false @@ -36,7 +36,7 @@ func TestDBFT_OnStartPrimarySendPrepareRequest(t *testing.T) { t.Run("backup sends nothing on start", func(t *testing.T) { s.currHeight = 0 - service, err := dbft.New[crypto.Uint256](s.getOptions()...) + service, err := dbft.New[crypto.Uint256, *consensus.Tx64](s.getOptions()...) require.NoError(t, err) service.Start(0) @@ -45,7 +45,7 @@ func TestDBFT_OnStartPrimarySendPrepareRequest(t *testing.T) { t.Run("primary send PrepareRequest on start", func(t *testing.T) { s.currHeight = 1 - service, _ := dbft.New[crypto.Uint256](s.getOptions()...) + service, _ := dbft.New[crypto.Uint256, *consensus.Tx64](s.getOptions()...) service.Start(0) p := s.tryRecv() @@ -90,7 +90,7 @@ func TestDBFT_SingleNode(t *testing.T) { if amev { opts = s.getAMEVOptions() } - service, _ := dbft.New[crypto.Uint256](opts...) + service, _ := dbft.New[crypto.Uint256, *consensus.Tx64](opts...) service.Start(0) p := s.tryRecv() @@ -127,9 +127,9 @@ func TestDBFT_SingleNode(t *testing.T) { func TestDBFT_OnReceiveRequestSendResponse(t *testing.T) { s := newTestState(2, 7) - s.verify = func(b dbft.Block[crypto.Uint256]) bool { + s.verify = func(b dbft.Block[crypto.Uint256, *consensus.Tx64]) bool { for _, tx := range b.Transactions() { - if *tx.(*consensus.Tx64)%10 == 0 { + if *tx%10 == 0 { return false } } @@ -139,7 +139,7 @@ func TestDBFT_OnReceiveRequestSendResponse(t *testing.T) { t.Run("receive request from primary", func(t *testing.T) { s.currHeight = 4 - service, _ := dbft.New[crypto.Uint256](s.getOptions()...) + service, _ := dbft.New[crypto.Uint256, *consensus.Tx64](s.getOptions()...) txs := []*consensus.Tx64{new(consensus.Tx64(1))} p := s.getPrepareRequest(5, txs[0]) @@ -170,7 +170,7 @@ func TestDBFT_OnReceiveRequestSendResponse(t *testing.T) { t.Run("change view on invalid tx", func(t *testing.T) { s.currHeight = 4 - service, _ := dbft.New[crypto.Uint256](s.getOptions()...) + service, _ := dbft.New[crypto.Uint256, *consensus.Tx64](s.getOptions()...) txs := []*consensus.Tx64{new(consensus.Tx64(10))} service.Start(0) @@ -179,7 +179,7 @@ func TestDBFT_OnReceiveRequestSendResponse(t *testing.T) { service.LastSeenMessage[i] = &dbft.HeightView{s.currHeight + 1, 0} } - p := s.getPrepareRequestWithMissing(5, []dbft.Transaction[crypto.Uint256]{txs[0]}, 0) + p := s.getPrepareRequestWithMissing(5, []*consensus.Tx64{txs[0]}, 0) service.OnReceive(p) require.Nil(t, s.tryRecv()) @@ -198,7 +198,7 @@ func TestDBFT_OnReceiveRequestSendResponse(t *testing.T) { t.Run("receive invalid prepare request", func(t *testing.T) { s.currHeight = 4 - service, _ := dbft.New[crypto.Uint256](s.getOptions()...) + service, _ := dbft.New[crypto.Uint256, *consensus.Tx64](s.getOptions()...) txs := []*consensus.Tx64{new(consensus.Tx64(1)), new(consensus.Tx64(2))} service.Start(0) @@ -216,7 +216,7 @@ func TestDBFT_OnReceiveRequestSendResponse(t *testing.T) { }) t.Run("does not have all transactions", func(t *testing.T) { - p := s.getPrepareRequestWithMissing(5, []dbft.Transaction[crypto.Uint256]{txs[0], txs[1]}, 1) + p := s.getPrepareRequestWithMissing(5, []*consensus.Tx64{txs[0], txs[1]}, 1) service.OnReceive(p) require.Nil(t, s.tryRecv()) @@ -245,12 +245,12 @@ func TestDBFT_CommitOnTransaction(t *testing.T) { s := newTestState(0, 4) s.currHeight = 1 - srv, _ := dbft.New[crypto.Uint256](s.getOptions()...) + srv, _ := dbft.New[crypto.Uint256, *consensus.Tx64](s.getOptions()...) srv.Start(0) require.Nil(t, s.tryRecv()) tx := new(consensus.Tx64(42)) - req := s.getPrepareRequestWithMissing(2, []dbft.Transaction[crypto.Uint256]{tx}, 0) + req := s.getPrepareRequestWithMissing(2, []*consensus.Tx64{tx}, 0) srv.OnReceive(req) srv.OnReceive(s.getPrepareResponse(1, req.Hash(), 0)) srv.OnReceive(s.getPrepareResponse(3, req.Hash(), 0)) @@ -264,7 +264,7 @@ func TestDBFT_CommitOnTransaction(t *testing.T) { privs: s.privs, } reqFull := s.getPrepareRequest(2, tx) - srv1, _ := dbft.New[crypto.Uint256](s1.getOptions()...) + srv1, _ := dbft.New[crypto.Uint256, *consensus.Tx64](s1.getOptions()...) srv1.Start(0) srv1.OnReceive(reqFull) srv1.OnReceive(s1.getPrepareResponse(1, reqFull.Hash(), 0)) @@ -286,7 +286,7 @@ func TestDBFT_OnReceiveCommit(t *testing.T) { s := newTestState(2, 4) t.Run("send commit after enough responses", func(t *testing.T) { s.currHeight = 1 - service, _ := dbft.New[crypto.Uint256](s.getOptions()...) + service, _ := dbft.New[crypto.Uint256, *consensus.Tx64](s.getOptions()...) service.Start(0) req := s.tryRecv() @@ -346,7 +346,7 @@ func TestDBFT_OnReceiveRecoveryRequest(t *testing.T) { s := newTestState(2, 4) t.Run("send recovery message", func(t *testing.T) { s.currHeight = 1 - service, _ := dbft.New[crypto.Uint256](s.getOptions()...) + service, _ := dbft.New[crypto.Uint256, *consensus.Tx64](s.getOptions()...) service.Start(0) req := s.tryRecv() @@ -368,7 +368,7 @@ func TestDBFT_OnReceiveRecoveryRequest(t *testing.T) { require.Equal(t, dbft.RecoveryMessageType, rm.Type()) other := s.copyWithIndex(3) - srv2, _ := dbft.New[crypto.Uint256](other.getOptions()...) + srv2, _ := dbft.New[crypto.Uint256, *consensus.Tx64](other.getOptions()...) srv2.Start(0) srv2.OnReceive(rm) @@ -416,7 +416,7 @@ func TestDBFT_OnReceiveRecoveryRequestResponds(t *testing.T) { t.Run(fmt.Sprintf("%d nodes, %d sender, %d receiver", param.nodes, param.sender, param.receiver), func(t *testing.T) { s := newTestState(param.receiver, param.nodes) s.currHeight = 1 - service, _ := dbft.New[crypto.Uint256](s.getOptions()...) + service, _ := dbft.New[crypto.Uint256, *consensus.Tx64](s.getOptions()...) service.Start(uint64(param.receiver)) _ = s.tryRecv() // Flush the queue if primary. @@ -438,7 +438,7 @@ func TestDBFT_OnReceiveChangeView(t *testing.T) { s := newTestState(2, 4) t.Run("change view correctly", func(t *testing.T) { s.currHeight = 6 - service, _ := dbft.New[crypto.Uint256](s.getOptions()...) + service, _ := dbft.New[crypto.Uint256, *consensus.Tx64](s.getOptions()...) service.Start(0) resp := s.getChangeView(1, 1) @@ -465,7 +465,7 @@ func TestDBFT_OnReceiveChangeView(t *testing.T) { func TestDBFT_Invalid(t *testing.T) { t.Run("without keys", func(t *testing.T) { - _, err := dbft.New[crypto.Uint256]() + _, err := dbft.New[crypto.Uint256, *consensus.Tx64]() require.Error(t, err) }) @@ -473,7 +473,7 @@ func TestDBFT_Invalid(t *testing.T) { require.NotNil(t, priv) require.NotNil(t, pub) - opts := []func(*dbft.Config[crypto.Uint256]){dbft.WithGetKeyPair[crypto.Uint256](func(_ []dbft.PublicKey) (int, dbft.PrivateKey, dbft.PublicKey) { + opts := []func(*dbft.Config[crypto.Uint256, *consensus.Tx64]){dbft.WithGetKeyPair[crypto.Uint256, *consensus.Tx64](func(_ []dbft.PublicKey) (int, dbft.PrivateKey, dbft.PublicKey) { return -1, nil, nil })} t.Run("without Timer", func(t *testing.T) { @@ -481,25 +481,25 @@ func TestDBFT_Invalid(t *testing.T) { require.Error(t, err) }) - opts = append(opts, dbft.WithTimer[crypto.Uint256](timer.New())) + opts = append(opts, dbft.WithTimer[crypto.Uint256, *consensus.Tx64](timer.New())) t.Run("without CurrentHeight", func(t *testing.T) { _, err := dbft.New(opts...) require.Error(t, err) }) - opts = append(opts, dbft.WithCurrentHeight[crypto.Uint256](func() uint32 { return 0 })) + opts = append(opts, dbft.WithCurrentHeight[crypto.Uint256, *consensus.Tx64](func() uint32 { return 0 })) t.Run("without CurrentBlockHash", func(t *testing.T) { _, err := dbft.New(opts...) require.Error(t, err) }) - opts = append(opts, dbft.WithCurrentBlockHash[crypto.Uint256](func() crypto.Uint256 { return crypto.Uint256{} })) + opts = append(opts, dbft.WithCurrentBlockHash[crypto.Uint256, *consensus.Tx64](func() crypto.Uint256 { return crypto.Uint256{} })) t.Run("without GetValidators", func(t *testing.T) { _, err := dbft.New(opts...) require.Error(t, err) }) - opts = append(opts, dbft.WithGetValidators[crypto.Uint256](func(...dbft.Transaction[crypto.Uint256]) []dbft.PublicKey { + opts = append(opts, dbft.WithGetValidators[crypto.Uint256, *consensus.Tx64](func(...*consensus.Tx64) []dbft.PublicKey { return []dbft.PublicKey{pub} })) t.Run("without NewBlockFromContext", func(t *testing.T) { @@ -507,7 +507,7 @@ func TestDBFT_Invalid(t *testing.T) { require.Error(t, err) }) - opts = append(opts, dbft.WithNewBlockFromContext[crypto.Uint256](func(_ *dbft.Context[crypto.Uint256]) dbft.Block[crypto.Uint256] { + opts = append(opts, dbft.WithNewBlockFromContext[crypto.Uint256, *consensus.Tx64](func(_ *dbft.Context[crypto.Uint256, *consensus.Tx64]) dbft.Block[crypto.Uint256, *consensus.Tx64] { return nil })) t.Run("without NewConsensusPayload", func(t *testing.T) { @@ -515,7 +515,7 @@ func TestDBFT_Invalid(t *testing.T) { require.Error(t, err) }) - opts = append(opts, dbft.WithNewConsensusPayload[crypto.Uint256](func(_ *dbft.Context[crypto.Uint256], _ dbft.MessageType, _ any) dbft.ConsensusPayload[crypto.Uint256] { + opts = append(opts, dbft.WithNewConsensusPayload[crypto.Uint256, *consensus.Tx64](func(_ *dbft.Context[crypto.Uint256, *consensus.Tx64], _ dbft.MessageType, _ any) dbft.ConsensusPayload[crypto.Uint256, *consensus.Tx64] { return nil })) t.Run("without NewPrepareRequest", func(t *testing.T) { @@ -523,7 +523,7 @@ func TestDBFT_Invalid(t *testing.T) { require.Error(t, err) }) - opts = append(opts, dbft.WithNewPrepareRequest[crypto.Uint256](func(uint64, uint64, []dbft.Transaction[crypto.Uint256]) dbft.PrepareRequest[crypto.Uint256] { + opts = append(opts, dbft.WithNewPrepareRequest[crypto.Uint256, *consensus.Tx64](func(uint64, uint64, []*consensus.Tx64) dbft.PrepareRequest[crypto.Uint256, *consensus.Tx64] { return nil })) t.Run("without NewPrepareResponse", func(t *testing.T) { @@ -531,7 +531,7 @@ func TestDBFT_Invalid(t *testing.T) { require.Error(t, err) }) - opts = append(opts, dbft.WithNewPrepareResponse[crypto.Uint256](func(crypto.Uint256) dbft.PrepareResponse[crypto.Uint256] { + opts = append(opts, dbft.WithNewPrepareResponse[crypto.Uint256, *consensus.Tx64](func(crypto.Uint256) dbft.PrepareResponse[crypto.Uint256] { return nil })) t.Run("without NewChangeView", func(t *testing.T) { @@ -539,7 +539,7 @@ func TestDBFT_Invalid(t *testing.T) { require.Error(t, err) }) - opts = append(opts, dbft.WithNewChangeView[crypto.Uint256](func(byte, dbft.ChangeViewReason, uint64) dbft.ChangeView { + opts = append(opts, dbft.WithNewChangeView[crypto.Uint256, *consensus.Tx64](func(byte, dbft.ChangeViewReason, uint64) dbft.ChangeView { return nil })) t.Run("without NewCommit", func(t *testing.T) { @@ -547,7 +547,7 @@ func TestDBFT_Invalid(t *testing.T) { require.Error(t, err) }) - opts = append(opts, dbft.WithNewCommit[crypto.Uint256](func([]byte) dbft.Commit { + opts = append(opts, dbft.WithNewCommit[crypto.Uint256, *consensus.Tx64](func([]byte) dbft.Commit { return nil })) t.Run("without NewRecoveryRequest", func(t *testing.T) { @@ -555,7 +555,7 @@ func TestDBFT_Invalid(t *testing.T) { require.Error(t, err) }) - opts = append(opts, dbft.WithNewRecoveryRequest[crypto.Uint256](func(uint64) dbft.RecoveryRequest { + opts = append(opts, dbft.WithNewRecoveryRequest[crypto.Uint256, *consensus.Tx64](func(uint64) dbft.RecoveryRequest { return nil })) t.Run("without NewRecoveryMessage", func(t *testing.T) { @@ -563,9 +563,9 @@ func TestDBFT_Invalid(t *testing.T) { require.Error(t, err) }) - opts = append(opts, dbft.WithNewRecoveryMessage[crypto.Uint256](func() dbft.RecoveryMessage[crypto.Uint256] { + opts = append(opts, dbft.WithNewRecoveryMessage[crypto.Uint256, *consensus.Tx64](func() dbft.RecoveryMessage[crypto.Uint256, *consensus.Tx64] { return nil - }), dbft.WithMaxTimePerBlock[crypto.Uint256](func() time.Duration { + }), dbft.WithMaxTimePerBlock[crypto.Uint256, *consensus.Tx64](func() time.Duration { return 0 })) t.Run("MaxTimePerBlock without SubscribeForTxs", func(t *testing.T) { @@ -573,7 +573,7 @@ func TestDBFT_Invalid(t *testing.T) { require.ErrorContains(t, err, "MaxTimePerBlock and SubscribeForTxs should be specified/not specified at the same time") }) - opts = append(opts, dbft.WithSubscribeForTxs[crypto.Uint256](func() {})) + opts = append(opts, dbft.WithSubscribeForTxs[crypto.Uint256, *consensus.Tx64](func() {})) t.Run("with all defaults", func(t *testing.T) { d, err := dbft.New(opts...) require.NoError(t, err) @@ -604,19 +604,19 @@ func TestDBFT_Invalid(t *testing.T) { func TestDBFT_FourGoodNodesDeadlock(t *testing.T) { r0 := newTestState(0, 4) r0.currHeight = 4 - s0, _ := dbft.New[crypto.Uint256](r0.getOptions()...) + s0, _ := dbft.New[crypto.Uint256, *consensus.Tx64](r0.getOptions()...) s0.Start(0) r1 := r0.copyWithIndex(1) - s1, _ := dbft.New[crypto.Uint256](r1.getOptions()...) + s1, _ := dbft.New[crypto.Uint256, *consensus.Tx64](r1.getOptions()...) s1.Start(0) r2 := r0.copyWithIndex(2) - s2, _ := dbft.New[crypto.Uint256](r2.getOptions()...) + s2, _ := dbft.New[crypto.Uint256, *consensus.Tx64](r2.getOptions()...) s2.Start(0) r3 := r0.copyWithIndex(3) - s3, _ := dbft.New[crypto.Uint256](r3.getOptions()...) + s3, _ := dbft.New[crypto.Uint256, *consensus.Tx64](r3.getOptions()...) s3.Start(0) // Step 1. The primary (at view 0) replica 1 sends the PrepareRequest message. @@ -809,7 +809,7 @@ func TestDBFT_OnReceiveCommitAMEV(t *testing.T) { s := newTestState(2, 4) t.Run("send preCommit after enough responses", func(t *testing.T) { s.currHeight = 1 - service, _ := dbft.New[crypto.Uint256](s.getAMEVOptions()...) + service, _ := dbft.New[crypto.Uint256, *consensus.Tx64](s.getAMEVOptions()...) service.Start(0) req := s.tryRecv() @@ -896,14 +896,14 @@ func TestDBFT_CachedMessages(t *testing.T) { if amev { opts = s2.getAMEVOptions() } - service2, _ := dbft.New[crypto.Uint256](opts...) + service2, _ := dbft.New[crypto.Uint256, *consensus.Tx64](opts...) service2.Start(0) opts = s1.getOptions() if amev { opts = s1.getAMEVOptions() } - service1, _ := dbft.New[crypto.Uint256](opts...) + service1, _ := dbft.New[crypto.Uint256, *consensus.Tx64](opts...) service1.Start(0) req := s2.tryRecv() @@ -1047,18 +1047,18 @@ func (s testState) getPrepareResponse(from uint16, phash crypto.Uint256, view by return p } -func (s testState) getPrepareRequest(from uint16, txs ...dbft.Transaction[crypto.Uint256]) Payload { +func (s testState) getPrepareRequest(from uint16, txs ...*consensus.Tx64) Payload { return s.getPrepareRequestWithHeight(from, s.currHeight+1, txs...) } -func (s testState) getPrepareRequestWithMissing(from uint16, txs []dbft.Transaction[crypto.Uint256], missing ...int) Payload { +func (s testState) getPrepareRequestWithMissing(from uint16, txs []*consensus.Tx64, missing ...int) Payload { req := consensus.NewPrepareRequestWithMissing(0, 0, txs, missing...) p := consensus.NewConsensusPayload(dbft.PrepareRequestType, s.currHeight+1, from, 0, req) return p } -func (s testState) getPrepareRequestWithHeight(from uint16, height uint32, txs ...dbft.Transaction[crypto.Uint256]) Payload { +func (s testState) getPrepareRequestWithHeight(from uint16, height uint32, txs ...*consensus.Tx64) Payload { req := consensus.NewPrepareRequest(0, 0, txs) p := consensus.NewConsensusPayload(dbft.PrepareRequestType, height, from, 0, req) @@ -1087,7 +1087,7 @@ func (s *testState) tryRecv() Payload { return p } -func (s *testState) nextBlock() dbft.Block[crypto.Uint256] { +func (s *testState) nextBlock() dbft.Block[crypto.Uint256, *consensus.Tx64] { if len(s.blocks) == 0 { return nil } @@ -1098,7 +1098,7 @@ func (s *testState) nextBlock() dbft.Block[crypto.Uint256] { return b } -func (s *testState) nextPreBlock() dbft.PreBlock[crypto.Uint256] { +func (s *testState) nextPreBlock() dbft.PreBlock[crypto.Uint256, *consensus.Tx64] { if len(s.preBlocks) == 0 { return nil } @@ -1120,43 +1120,43 @@ func (s testState) copyWithIndex(myIndex int) *testState { } } -func (s *testState) getOptions() []func(*dbft.Config[crypto.Uint256]) { - opts := []func(*dbft.Config[crypto.Uint256]){ - dbft.WithTimer[crypto.Uint256](timer.New()), - dbft.WithCurrentHeight[crypto.Uint256](func() uint32 { return s.currHeight }), - dbft.WithCurrentBlockHash[crypto.Uint256](func() crypto.Uint256 { return s.currHash }), - dbft.WithGetValidators[crypto.Uint256](func(...dbft.Transaction[crypto.Uint256]) []dbft.PublicKey { return s.pubs }), - dbft.WithGetKeyPair[crypto.Uint256](func(_ []dbft.PublicKey) (int, dbft.PrivateKey, dbft.PublicKey) { +func (s *testState) getOptions() []func(*dbft.Config[crypto.Uint256, *consensus.Tx64]) { + opts := []func(*dbft.Config[crypto.Uint256, *consensus.Tx64]){ + dbft.WithTimer[crypto.Uint256, *consensus.Tx64](timer.New()), + dbft.WithCurrentHeight[crypto.Uint256, *consensus.Tx64](func() uint32 { return s.currHeight }), + dbft.WithCurrentBlockHash[crypto.Uint256, *consensus.Tx64](func() crypto.Uint256 { return s.currHash }), + dbft.WithGetValidators[crypto.Uint256, *consensus.Tx64](func(...*consensus.Tx64) []dbft.PublicKey { return s.pubs }), + dbft.WithGetKeyPair[crypto.Uint256, *consensus.Tx64](func(_ []dbft.PublicKey) (int, dbft.PrivateKey, dbft.PublicKey) { return s.myIndex, s.privs[s.myIndex], s.pubs[s.myIndex] }), - dbft.WithBroadcast[crypto.Uint256](func(p Payload) { s.ch = append(s.ch, p) }), - dbft.WithProcessBlock[crypto.Uint256](func(b dbft.Block[crypto.Uint256]) error { s.blocks = append(s.blocks, b); return nil }), - dbft.WithWatchOnly[crypto.Uint256](func() bool { return false }), - dbft.WithGetBlock[crypto.Uint256](func(crypto.Uint256) dbft.Block[crypto.Uint256] { return nil }), - dbft.WithTimer[crypto.Uint256](timer.New()), - dbft.WithLogger[crypto.Uint256](zap.NewNop()), - dbft.WithNewBlockFromContext[crypto.Uint256](newBlockFromContext), - dbft.WithTimePerBlock[crypto.Uint256](func() time.Duration { + dbft.WithBroadcast[crypto.Uint256, *consensus.Tx64](func(p Payload) { s.ch = append(s.ch, p) }), + dbft.WithProcessBlock[crypto.Uint256, *consensus.Tx64](func(b dbft.Block[crypto.Uint256, *consensus.Tx64]) error { s.blocks = append(s.blocks, b); return nil }), + dbft.WithWatchOnly[crypto.Uint256, *consensus.Tx64](func() bool { return false }), + dbft.WithGetBlock[crypto.Uint256, *consensus.Tx64](func(crypto.Uint256) dbft.Block[crypto.Uint256, *consensus.Tx64] { return nil }), + dbft.WithTimer[crypto.Uint256, *consensus.Tx64](timer.New()), + dbft.WithLogger[crypto.Uint256, *consensus.Tx64](zap.NewNop()), + dbft.WithNewBlockFromContext[crypto.Uint256, *consensus.Tx64](newBlockFromContext), + dbft.WithTimePerBlock[crypto.Uint256, *consensus.Tx64](func() time.Duration { return time.Second * 10 }), - dbft.WithRequestTx[crypto.Uint256](func(seq iter.Seq[crypto.Uint256]) {}), - dbft.WithGetVerified[crypto.Uint256](func() []dbft.Transaction[crypto.Uint256] { return []dbft.Transaction[crypto.Uint256]{} }), - - dbft.WithNewConsensusPayload[crypto.Uint256](newConsensusPayload), - dbft.WithNewPrepareRequest[crypto.Uint256](consensus.NewPrepareRequest), - dbft.WithNewPrepareResponse[crypto.Uint256](consensus.NewPrepareResponse), - dbft.WithNewChangeView[crypto.Uint256](consensus.NewChangeView), - dbft.WithNewCommit[crypto.Uint256](consensus.NewCommit), - dbft.WithNewRecoveryRequest[crypto.Uint256](consensus.NewRecoveryRequest), - dbft.WithNewRecoveryMessage[crypto.Uint256](func() dbft.RecoveryMessage[crypto.Uint256] { + dbft.WithRequestTx[crypto.Uint256, *consensus.Tx64](func(seq iter.Seq[crypto.Uint256]) {}), + dbft.WithGetVerified[crypto.Uint256, *consensus.Tx64](func() []*consensus.Tx64 { return []*consensus.Tx64{} }), + + dbft.WithNewConsensusPayload[crypto.Uint256, *consensus.Tx64](newConsensusPayload), + dbft.WithNewPrepareRequest[crypto.Uint256, *consensus.Tx64](consensus.NewPrepareRequest), + dbft.WithNewPrepareResponse[crypto.Uint256, *consensus.Tx64](consensus.NewPrepareResponse), + dbft.WithNewChangeView[crypto.Uint256, *consensus.Tx64](consensus.NewChangeView), + dbft.WithNewCommit[crypto.Uint256, *consensus.Tx64](consensus.NewCommit), + dbft.WithNewRecoveryRequest[crypto.Uint256, *consensus.Tx64](consensus.NewRecoveryRequest), + dbft.WithNewRecoveryMessage[crypto.Uint256, *consensus.Tx64](func() dbft.RecoveryMessage[crypto.Uint256, *consensus.Tx64] { return consensus.NewRecoveryMessage(nil) }), - dbft.WithVerifyCommit[crypto.Uint256](func(p dbft.ConsensusPayload[crypto.Uint256]) error { return nil }), + dbft.WithVerifyCommit[crypto.Uint256, *consensus.Tx64](func(p dbft.ConsensusPayload[crypto.Uint256, *consensus.Tx64]) error { return nil }), } verify := s.verify if verify == nil { - verify = func(dbft.Block[crypto.Uint256]) bool { return true } + verify = func(dbft.Block[crypto.Uint256, *consensus.Tx64]) bool { return true } } opts = append(opts, dbft.WithVerifyBlock(verify)) @@ -1165,21 +1165,21 @@ func (s *testState) getOptions() []func(*dbft.Config[crypto.Uint256]) { cfg := zap.NewDevelopmentConfig() cfg.DisableStacktrace = true logger, _ := cfg.Build() - opts = append(opts, dbft.WithLogger[crypto.Uint256](logger)) + opts = append(opts, dbft.WithLogger[crypto.Uint256, *consensus.Tx64](logger)) } return opts } -func (s *testState) getAMEVOptions() []func(*dbft.Config[crypto.Uint256]) { +func (s *testState) getAMEVOptions() []func(*dbft.Config[crypto.Uint256, *consensus.Tx64]) { opts := s.getOptions() opts = append(opts, - dbft.WithAntiMEVExtensionEnablingHeight[crypto.Uint256](0), - dbft.WithNewPreCommit[crypto.Uint256](consensus.NewPreCommit), - dbft.WithNewCommit[crypto.Uint256](consensus.NewAMEVCommit), - dbft.WithNewPreBlockFromContext[crypto.Uint256](newPreBlockFromContext), - dbft.WithNewBlockFromContext[crypto.Uint256](newAMEVBlockFromContext), - dbft.WithProcessPreBlock(func(b dbft.PreBlock[crypto.Uint256]) error { + dbft.WithAntiMEVExtensionEnablingHeight[crypto.Uint256, *consensus.Tx64](0), + dbft.WithNewPreCommit[crypto.Uint256, *consensus.Tx64](consensus.NewPreCommit), + dbft.WithNewCommit[crypto.Uint256, *consensus.Tx64](consensus.NewAMEVCommit), + dbft.WithNewPreBlockFromContext[crypto.Uint256, *consensus.Tx64](newPreBlockFromContext), + dbft.WithNewBlockFromContext[crypto.Uint256, *consensus.Tx64](newAMEVBlockFromContext), + dbft.WithProcessPreBlock(func(b dbft.PreBlock[crypto.Uint256, *consensus.Tx64]) error { s.preBlocks = append(s.preBlocks, b) return nil }), @@ -1188,7 +1188,7 @@ func (s *testState) getAMEVOptions() []func(*dbft.Config[crypto.Uint256]) { return opts } -func newBlockFromContext(ctx *dbft.Context[crypto.Uint256]) dbft.Block[crypto.Uint256] { +func newBlockFromContext(ctx *dbft.Context[crypto.Uint256, *consensus.Tx64]) dbft.Block[crypto.Uint256, *consensus.Tx64] { if ctx.Transactions == nil { return nil } @@ -1196,7 +1196,7 @@ func newBlockFromContext(ctx *dbft.Context[crypto.Uint256]) dbft.Block[crypto.Ui return block } -func newPreBlockFromContext(ctx *dbft.Context[crypto.Uint256]) dbft.PreBlock[crypto.Uint256] { +func newPreBlockFromContext(ctx *dbft.Context[crypto.Uint256, *consensus.Tx64]) dbft.PreBlock[crypto.Uint256, *consensus.Tx64] { if ctx.Transactions == nil { return nil } @@ -1204,7 +1204,7 @@ func newPreBlockFromContext(ctx *dbft.Context[crypto.Uint256]) dbft.PreBlock[cry return pre } -func newAMEVBlockFromContext(ctx *dbft.Context[crypto.Uint256]) dbft.Block[crypto.Uint256] { +func newAMEVBlockFromContext(ctx *dbft.Context[crypto.Uint256, *consensus.Tx64]) dbft.Block[crypto.Uint256, *consensus.Tx64] { if ctx.Transactions == nil { return nil } @@ -1220,7 +1220,7 @@ func newAMEVBlockFromContext(ctx *dbft.Context[crypto.Uint256]) dbft.Block[crypt // newConsensusPayload is a function for creating consensus payload of specific // type. -func newConsensusPayload(c *dbft.Context[crypto.Uint256], t dbft.MessageType, msg any) dbft.ConsensusPayload[crypto.Uint256] { +func newConsensusPayload(c *dbft.Context[crypto.Uint256, *consensus.Tx64], t dbft.MessageType, msg any) dbft.ConsensusPayload[crypto.Uint256, *consensus.Tx64] { cp := consensus.NewConsensusPayload(t, c.BlockIndex, uint16(c.MyIndex), c.ViewNumber, msg) return cp } diff --git a/helpers.go b/helpers.go index 9f2ab9353..0336cd112 100644 --- a/helpers.go +++ b/helpers.go @@ -2,36 +2,36 @@ package dbft type ( // inbox is a structure storing messages from a single epoch. - inbox[H Hash] struct { - prepare map[uint16]ConsensusPayload[H] - chViews map[uint16]ConsensusPayload[H] - preCommit map[uint16]ConsensusPayload[H] - commit map[uint16]ConsensusPayload[H] + inbox[H Hash, Tx Transaction[H]] struct { + prepare map[uint16]ConsensusPayload[H, Tx] + chViews map[uint16]ConsensusPayload[H, Tx] + preCommit map[uint16]ConsensusPayload[H, Tx] + commit map[uint16]ConsensusPayload[H, Tx] } // cache is an auxiliary structure storing messages // from future epochs. - cache[H Hash] struct { - mail map[uint32]*inbox[H] + cache[H Hash, Tx Transaction[H]] struct { + mail map[uint32]*inbox[H, Tx] } ) -func newInbox[H Hash]() *inbox[H] { - return &inbox[H]{ - prepare: make(map[uint16]ConsensusPayload[H]), - chViews: make(map[uint16]ConsensusPayload[H]), - preCommit: make(map[uint16]ConsensusPayload[H]), - commit: make(map[uint16]ConsensusPayload[H]), +func newInbox[H Hash, Tx Transaction[H]]() *inbox[H, Tx] { + return &inbox[H, Tx]{ + prepare: make(map[uint16]ConsensusPayload[H, Tx]), + chViews: make(map[uint16]ConsensusPayload[H, Tx]), + preCommit: make(map[uint16]ConsensusPayload[H, Tx]), + commit: make(map[uint16]ConsensusPayload[H, Tx]), } } -func newCache[H Hash]() cache[H] { - return cache[H]{ - mail: make(map[uint32]*inbox[H]), +func newCache[H Hash, Tx Transaction[H]]() cache[H, Tx] { + return cache[H, Tx]{ + mail: make(map[uint32]*inbox[H, Tx]), } } -func (c *cache[H]) getHeight(h uint32) *inbox[H] { +func (c *cache[H, Tx]) getHeight(h uint32) *inbox[H, Tx] { if m, ok := c.mail[h]; ok { delete(c.mail, h) return m @@ -40,10 +40,10 @@ func (c *cache[H]) getHeight(h uint32) *inbox[H] { return nil } -func (c *cache[H]) addMessage(m ConsensusPayload[H]) { +func (c *cache[H, Tx]) addMessage(m ConsensusPayload[H, Tx]) { msgs, ok := c.mail[m.Height()] if !ok { - msgs = newInbox[H]() + msgs = newInbox[H, Tx]() c.mail[m.Height()] = msgs } diff --git a/helpers_test.go b/helpers_test.go index 0c62421cb..5d5820d9e 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -10,6 +10,7 @@ import ( // dependency. type ( hash struct{} + tx64 int payloadStub struct { height uint32 typ MessageType @@ -21,6 +22,10 @@ func (hash) String() string { return "" } +func (*tx64) Hash() hash { + return hash{} +} + func (p payloadStub) ViewNumber() byte { panic("TODO") } @@ -42,7 +47,7 @@ func (p payloadStub) SetPayload(any) { func (p payloadStub) GetChangeView() ChangeView { panic("TODO") } -func (p payloadStub) GetPrepareRequest() PrepareRequest[hash] { +func (p payloadStub) GetPrepareRequest() PrepareRequest[hash, *tx64] { panic("TODO") } func (p payloadStub) GetPrepareResponse() PrepareResponse[hash] { @@ -55,7 +60,7 @@ func (p payloadStub) GetPreCommit() PreCommit { panic("TODO") } func (p payloadStub) GetRecoveryRequest() RecoveryRequest { panic("TODO") } -func (p payloadStub) GetRecoveryMessage() RecoveryMessage[hash] { +func (p payloadStub) GetRecoveryMessage() RecoveryMessage[hash, *tx64] { panic("TODO") } func (p payloadStub) ValidatorIndex() uint16 { @@ -75,7 +80,7 @@ func (p payloadStub) Hash() hash { } func TestMessageCache(t *testing.T) { - c := newCache[hash]() + c := newCache[hash, *tx64]() p1 := payloadStub{ height: 3, diff --git a/internal/consensus/amev_block.go b/internal/consensus/amev_block.go index 5126da5e8..96d764752 100644 --- a/internal/consensus/amev_block.go +++ b/internal/consensus/amev_block.go @@ -14,16 +14,16 @@ import ( type amevBlock struct { base - transactions []dbft.Transaction[crypto.Uint256] + transactions []*Tx64 signature []byte hash *crypto.Uint256 } -var _ dbft.Block[crypto.Uint256] = new(amevBlock) +var _ dbft.Block[crypto.Uint256, *Tx64] = new(amevBlock) // NewAMEVBlock returns new block based on PreBlock and additional Commit-level data // collected from M consensus nodes. -func NewAMEVBlock(pre dbft.PreBlock[crypto.Uint256], cnData [][]byte, m int) dbft.Block[crypto.Uint256] { +func NewAMEVBlock(pre dbft.PreBlock[crypto.Uint256, *Tx64], cnData [][]byte, m int) dbft.Block[crypto.Uint256, *Tx64] { preB := pre.(*preBlock) res := new(amevBlock) res.base = preB.base @@ -64,14 +64,14 @@ func (b *amevBlock) MerkleRoot() crypto.Uint256 { } // Transactions implements Block interface. -func (b *amevBlock) Transactions() []dbft.Transaction[crypto.Uint256] { +func (b *amevBlock) Transactions() []*Tx64 { return b.transactions } // SetTransactions implements Block interface. This method is special since it's // left for dBFT 2.0 compatibility and transactions from this method must not be // reused to fill final Block's transactions. -func (b *amevBlock) SetTransactions(_ []dbft.Transaction[crypto.Uint256]) { +func (b *amevBlock) SetTransactions(_ []*Tx64) { } // Signature implements Block interface. diff --git a/internal/consensus/amev_preBlock.go b/internal/consensus/amev_preBlock.go index 2e8086414..5866ede0a 100644 --- a/internal/consensus/amev_preBlock.go +++ b/internal/consensus/amev_preBlock.go @@ -16,13 +16,13 @@ type preBlock struct { // and used to construct the final list of transactions for amevBlock. data uint32 - initialTransactions []dbft.Transaction[crypto.Uint256] + initialTransactions []*Tx64 } -var _ dbft.PreBlock[crypto.Uint256] = new(preBlock) +var _ dbft.PreBlock[crypto.Uint256, *Tx64] = new(preBlock) // NewPreBlock returns new preBlock. -func NewPreBlock(timestamp uint64, index uint32, prevHash crypto.Uint256, nonce uint64, txs []dbft.Transaction[crypto.Uint256]) dbft.PreBlock[crypto.Uint256] { +func NewPreBlock(timestamp uint64, index uint32, prevHash crypto.Uint256, nonce uint64, txs []*Tx64) dbft.PreBlock[crypto.Uint256, *Tx64] { pre := new(preBlock) pre.Timestamp = uint32(timestamp / 1000000000) pre.Index = index @@ -74,10 +74,10 @@ func (pre *preBlock) Verify(_ dbft.PublicKey, data []byte) error { return nil } -func (pre *preBlock) Transactions() []dbft.Transaction[crypto.Uint256] { +func (pre *preBlock) Transactions() []*Tx64 { return pre.initialTransactions } -func (pre *preBlock) SetTransactions(txs []dbft.Transaction[crypto.Uint256]) { +func (pre *preBlock) SetTransactions(txs []*Tx64) { pre.initialTransactions = txs } diff --git a/internal/consensus/block.go b/internal/consensus/block.go index e672eb632..021ec68a2 100644 --- a/internal/consensus/block.go +++ b/internal/consensus/block.go @@ -25,7 +25,7 @@ type ( neoBlock struct { base - transactions []dbft.Transaction[crypto.Uint256] + transactions []*Tx64 signature []byte hash *crypto.Uint256 } @@ -38,7 +38,7 @@ type ( } ) -var _ dbft.Block[crypto.Uint256] = new(neoBlock) +var _ dbft.Block[crypto.Uint256, *Tx64] = new(neoBlock) // PrevHash implements Block interface. func (b *neoBlock) PrevHash() crypto.Uint256 { @@ -56,17 +56,17 @@ func (b *neoBlock) MerkleRoot() crypto.Uint256 { } // Transactions implements Block interface. -func (b *neoBlock) Transactions() []dbft.Transaction[crypto.Uint256] { +func (b *neoBlock) Transactions() []*Tx64 { return b.transactions } // SetTransactions implements Block interface. -func (b *neoBlock) SetTransactions(txx []dbft.Transaction[crypto.Uint256]) { +func (b *neoBlock) SetTransactions(txx []*Tx64) { b.transactions = txx } // NewBlock returns new block. -func NewBlock(timestamp uint64, index uint32, prevHash crypto.Uint256, nonce uint64, txs []dbft.Transaction[crypto.Uint256]) dbft.Block[crypto.Uint256] { +func NewBlock(timestamp uint64, index uint32, prevHash crypto.Uint256, nonce uint64, txs []*Tx64) dbft.Block[crypto.Uint256, *Tx64] { block := new(neoBlock) block.Timestamp = uint32(timestamp / 1000000000) block.base.Index = index diff --git a/internal/consensus/block_test.go b/internal/consensus/block_test.go index 7b8067615..6f54cd5e3 100644 --- a/internal/consensus/block_test.go +++ b/internal/consensus/block_test.go @@ -6,7 +6,6 @@ import ( "errors" "testing" - "github.com/nspcc-dev/dbft" "github.com/nspcc-dev/dbft/internal/crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -17,7 +16,7 @@ func TestNeoBlock_Setters(t *testing.T) { require.Equal(t, crypto.Uint256{}, b.Hash()) - txs := []dbft.Transaction[crypto.Uint256]{new(Tx64(1)), new(Tx64(2))} + txs := []*Tx64{new(Tx64(1)), new(Tx64(2))} b.SetTransactions(txs) assert.Equal(t, txs, b.Transactions()) diff --git a/internal/consensus/consensus.go b/internal/consensus/consensus.go index 5b46caa6c..591212408 100644 --- a/internal/consensus/consensus.go +++ b/internal/consensus/consensus.go @@ -10,20 +10,20 @@ import ( ) func New(logger *zap.Logger, key dbft.PrivateKey, pub dbft.PublicKey, - getVerified func() []dbft.Transaction[crypto.Uint256], - broadcast func(dbft.ConsensusPayload[crypto.Uint256]), - processBlock func(dbft.Block[crypto.Uint256]) error, + getVerified func() []*Tx64, + broadcast func(dbft.ConsensusPayload[crypto.Uint256, *Tx64]), + processBlock func(dbft.Block[crypto.Uint256, *Tx64]) error, currentHeight func() uint32, currentBlockHash func() crypto.Uint256, - getValidators func(...dbft.Transaction[crypto.Uint256]) []dbft.PublicKey, - verifyPayload func(consensusPayload dbft.ConsensusPayload[crypto.Uint256]) error) (*dbft.DBFT[crypto.Uint256], error) { + getValidators func(...*Tx64) []dbft.PublicKey, + verifyPayload func(consensusPayload dbft.ConsensusPayload[crypto.Uint256, *Tx64]) error) (*dbft.DBFT[crypto.Uint256, *Tx64], error) { return dbft.New[crypto.Uint256]( - dbft.WithTimer[crypto.Uint256](timer.New()), - dbft.WithLogger[crypto.Uint256](logger), - dbft.WithTimePerBlock[crypto.Uint256](func() time.Duration { + dbft.WithTimer[crypto.Uint256, *Tx64](timer.New()), + dbft.WithLogger[crypto.Uint256, *Tx64](logger), + dbft.WithTimePerBlock[crypto.Uint256, *Tx64](func() time.Duration { return time.Second * 5 }), - dbft.WithGetKeyPair[crypto.Uint256](func(pubs []dbft.PublicKey) (int, dbft.PrivateKey, dbft.PublicKey) { + dbft.WithGetKeyPair[crypto.Uint256, *Tx64](func(pubs []dbft.PublicKey) (int, dbft.PrivateKey, dbft.PublicKey) { for i := range pubs { if pub.(*crypto.ECDSAPub).Equals(pubs[i]) { return i, key, pub @@ -32,30 +32,30 @@ func New(logger *zap.Logger, key dbft.PrivateKey, pub dbft.PublicKey, return -1, nil, nil }), - dbft.WithGetVerified[crypto.Uint256](getVerified), - dbft.WithBroadcast[crypto.Uint256](broadcast), - dbft.WithProcessBlock[crypto.Uint256](processBlock), - dbft.WithCurrentHeight[crypto.Uint256](currentHeight), - dbft.WithCurrentBlockHash[crypto.Uint256](currentBlockHash), - dbft.WithGetValidators[crypto.Uint256](getValidators), - dbft.WithVerifyPrepareRequest[crypto.Uint256](verifyPayload), - dbft.WithVerifyPrepareResponse[crypto.Uint256](verifyPayload), - dbft.WithVerifyCommit[crypto.Uint256](verifyPayload), + dbft.WithGetVerified[crypto.Uint256, *Tx64](getVerified), + dbft.WithBroadcast[crypto.Uint256, *Tx64](broadcast), + dbft.WithProcessBlock[crypto.Uint256, *Tx64](processBlock), + dbft.WithCurrentHeight[crypto.Uint256, *Tx64](currentHeight), + dbft.WithCurrentBlockHash[crypto.Uint256, *Tx64](currentBlockHash), + dbft.WithGetValidators[crypto.Uint256, *Tx64](getValidators), + dbft.WithVerifyPrepareRequest[crypto.Uint256, *Tx64](verifyPayload), + dbft.WithVerifyPrepareResponse[crypto.Uint256, *Tx64](verifyPayload), + dbft.WithVerifyCommit[crypto.Uint256, *Tx64](verifyPayload), - dbft.WithNewBlockFromContext[crypto.Uint256](newBlockFromContext), - dbft.WithNewConsensusPayload[crypto.Uint256](defaultNewConsensusPayload), - dbft.WithNewPrepareRequest[crypto.Uint256](NewPrepareRequest), - dbft.WithNewPrepareResponse[crypto.Uint256](NewPrepareResponse), - dbft.WithNewChangeView[crypto.Uint256](NewChangeView), - dbft.WithNewCommit[crypto.Uint256](NewCommit), - dbft.WithNewRecoveryMessage[crypto.Uint256](func() dbft.RecoveryMessage[crypto.Uint256] { + dbft.WithNewBlockFromContext[crypto.Uint256, *Tx64](newBlockFromContext), + dbft.WithNewConsensusPayload[crypto.Uint256, *Tx64](defaultNewConsensusPayload), + dbft.WithNewPrepareRequest[crypto.Uint256, *Tx64](NewPrepareRequest), + dbft.WithNewPrepareResponse[crypto.Uint256, *Tx64](NewPrepareResponse), + dbft.WithNewChangeView[crypto.Uint256, *Tx64](NewChangeView), + dbft.WithNewCommit[crypto.Uint256, *Tx64](NewCommit), + dbft.WithNewRecoveryMessage[crypto.Uint256, *Tx64](func() dbft.RecoveryMessage[crypto.Uint256, *Tx64] { return NewRecoveryMessage(nil) }), - dbft.WithNewRecoveryRequest[crypto.Uint256](NewRecoveryRequest), + dbft.WithNewRecoveryRequest[crypto.Uint256, *Tx64](NewRecoveryRequest), ) } -func newBlockFromContext(ctx *dbft.Context[crypto.Uint256]) dbft.Block[crypto.Uint256] { +func newBlockFromContext(ctx *dbft.Context[crypto.Uint256, *Tx64]) dbft.Block[crypto.Uint256, *Tx64] { if ctx.Transactions == nil { return nil } @@ -65,6 +65,6 @@ func newBlockFromContext(ctx *dbft.Context[crypto.Uint256]) dbft.Block[crypto.Ui // defaultNewConsensusPayload is default function for creating // consensus payload of specific type. -func defaultNewConsensusPayload(c *dbft.Context[crypto.Uint256], t dbft.MessageType, msg any) dbft.ConsensusPayload[crypto.Uint256] { +func defaultNewConsensusPayload(c *dbft.Context[crypto.Uint256, *Tx64], t dbft.MessageType, msg any) dbft.ConsensusPayload[crypto.Uint256, *Tx64] { return NewConsensusPayload(t, c.BlockIndex, uint16(c.MyIndex), c.ViewNumber, msg) } diff --git a/internal/consensus/consensus_message.go b/internal/consensus/consensus_message.go index ca0412c5e..7f5a10253 100644 --- a/internal/consensus/consensus_message.go +++ b/internal/consensus/consensus_message.go @@ -31,7 +31,7 @@ type ( } ) -var _ dbft.ConsensusMessage[crypto.Uint256] = (*message)(nil) +var _ dbft.ConsensusMessage[crypto.Uint256, *Tx64] = (*message)(nil) // EncodeBinary implements Serializable interface. func (m message) EncodeBinary(w *gob.Encoder) error { @@ -81,8 +81,8 @@ func (m *message) DecodeBinary(r *gob.Decoder) error { } func (m message) GetChangeView() dbft.ChangeView { return m.payload.(dbft.ChangeView) } -func (m message) GetPrepareRequest() dbft.PrepareRequest[crypto.Uint256] { - return m.payload.(dbft.PrepareRequest[crypto.Uint256]) +func (m message) GetPrepareRequest() dbft.PrepareRequest[crypto.Uint256, *Tx64] { + return m.payload.(dbft.PrepareRequest[crypto.Uint256, *Tx64]) } func (m message) GetPrepareResponse() dbft.PrepareResponse[crypto.Uint256] { return m.payload.(dbft.PrepareResponse[crypto.Uint256]) @@ -90,8 +90,8 @@ func (m message) GetPrepareResponse() dbft.PrepareResponse[crypto.Uint256] { func (m message) GetCommit() dbft.Commit { return m.payload.(dbft.Commit) } func (m message) GetPreCommit() dbft.PreCommit { return m.payload.(dbft.PreCommit) } func (m message) GetRecoveryRequest() dbft.RecoveryRequest { return m.payload.(dbft.RecoveryRequest) } -func (m message) GetRecoveryMessage() dbft.RecoveryMessage[crypto.Uint256] { - return m.payload.(dbft.RecoveryMessage[crypto.Uint256]) +func (m message) GetRecoveryMessage() dbft.RecoveryMessage[crypto.Uint256, *Tx64] { + return m.payload.(dbft.RecoveryMessage[crypto.Uint256, *Tx64]) } // ViewNumber implements ConsensusMessage interface. diff --git a/internal/consensus/constructors.go b/internal/consensus/constructors.go index b20d08e9a..75dc5d652 100644 --- a/internal/consensus/constructors.go +++ b/internal/consensus/constructors.go @@ -9,7 +9,7 @@ import ( ) // NewConsensusPayload returns minimal ConsensusPayload implementation. -func NewConsensusPayload(t dbft.MessageType, height uint32, validatorIndex uint16, viewNumber byte, consensusMessage any) dbft.ConsensusPayload[crypto.Uint256] { +func NewConsensusPayload(t dbft.MessageType, height uint32, validatorIndex uint16, viewNumber byte, consensusMessage any) dbft.ConsensusPayload[crypto.Uint256, *Tx64] { return &Payload{ message: message{ cmType: t, @@ -22,24 +22,21 @@ func NewConsensusPayload(t dbft.MessageType, height uint32, validatorIndex uint1 } // NewPrepareRequest returns minimal prepareRequest implementation. -func NewPrepareRequest(ts uint64, nonce uint64, txs []dbft.Transaction[crypto.Uint256]) dbft.PrepareRequest[crypto.Uint256] { +func NewPrepareRequest(ts uint64, nonce uint64, txs []*Tx64) dbft.PrepareRequest[crypto.Uint256, *Tx64] { return NewPrepareRequestWithMissing(ts, nonce, txs) } // NewPrepareRequestWithMissing returns prepareRequest implementation with the // specified transactions marked as missing. -func NewPrepareRequestWithMissing(ts uint64, nonce uint64, txs []dbft.Transaction[crypto.Uint256], missing ...int) dbft.PrepareRequest[crypto.Uint256] { - reqTxs := make([]*Tx64, len(txs)) +func NewPrepareRequestWithMissing(ts uint64, nonce uint64, txs []*Tx64, missing ...int) dbft.PrepareRequest[crypto.Uint256, *Tx64] { missingTxs := make(map[crypto.Uint256]int) for i, tx := range txs { - tx64 := tx.(*Tx64) - reqTxs[i] = tx64 if slices.Contains(missing, i) { - missingTxs[tx64.Hash()] = i + missingTxs[tx.Hash()] = i } } return &prepareRequest{ - txs: reqTxs, + txs: txs, nonce: nonce, timestamp: nanoSecToSec(ts), missing: missingTxs, @@ -90,7 +87,7 @@ func NewRecoveryRequest(ts uint64) dbft.RecoveryRequest { } // NewRecoveryMessage returns minimal RecoveryMessage implementation. -func NewRecoveryMessage(preparationHash *crypto.Uint256) dbft.RecoveryMessage[crypto.Uint256] { +func NewRecoveryMessage(preparationHash *crypto.Uint256) dbft.RecoveryMessage[crypto.Uint256, *Tx64] { return &recoveryMessage{ preparationHash: preparationHash, preparationPayloads: make([]preparationCompact, 0), diff --git a/internal/consensus/message.go b/internal/consensus/message.go index 5b5b01c07..598392a83 100644 --- a/internal/consensus/message.go +++ b/internal/consensus/message.go @@ -32,7 +32,7 @@ type ( } ) -var _ dbft.ConsensusPayload[crypto.Uint256] = (*Payload)(nil) +var _ dbft.ConsensusPayload[crypto.Uint256, *Tx64] = (*Payload)(nil) // EncodeBinary implements Serializable interface. func (p Payload) EncodeBinary(w *gob.Encoder) error { diff --git a/internal/consensus/message_test.go b/internal/consensus/message_test.go index 36d3109b0..20a271564 100644 --- a/internal/consensus/message_test.go +++ b/internal/consensus/message_test.go @@ -107,11 +107,11 @@ func TestRecoveryMessage_NoPayloads(t *testing.T) { rec := m.GetRecoveryMessage() require.NotNil(t, rec) - var p dbft.ConsensusPayload[crypto.Uint256] + var p dbft.ConsensusPayload[crypto.Uint256, *Tx64] require.NotPanics(t, func() { p = rec.GetPrepareRequest(p, validators, 0) }) require.Nil(t, p) - var ps []dbft.ConsensusPayload[crypto.Uint256] + var ps []dbft.ConsensusPayload[crypto.Uint256, *Tx64] require.NotPanics(t, func() { ps = rec.GetPrepareResponses(p, validators) }) require.Len(t, ps, 0) diff --git a/internal/consensus/prepare_request.go b/internal/consensus/prepare_request.go index d3570145a..c030b3af0 100644 --- a/internal/consensus/prepare_request.go +++ b/internal/consensus/prepare_request.go @@ -22,7 +22,7 @@ type ( } ) -var _ dbft.PrepareRequest[crypto.Uint256] = (*prepareRequest)(nil) +var _ dbft.PrepareRequest[crypto.Uint256, *Tx64] = (*prepareRequest)(nil) // EncodeBinary implements Serializable interface. func (p prepareRequest) EncodeBinary(w *gob.Encoder) error { @@ -57,10 +57,6 @@ func (p prepareRequest) Nonce() uint64 { } // Transactions implements PrepareRequest interface. -func (p prepareRequest) Transactions() ([]dbft.Transaction[crypto.Uint256], map[crypto.Uint256]int) { - txs := make([]dbft.Transaction[crypto.Uint256], len(p.txs)) - for i, tx := range p.txs { - txs[i] = tx - } - return txs, p.missing +func (p prepareRequest) Transactions() ([]*Tx64, map[crypto.Uint256]int) { + return p.txs, p.missing } diff --git a/internal/consensus/recovery_message.go b/internal/consensus/recovery_message.go index e538faa4d..fcb1a5897 100644 --- a/internal/consensus/recovery_message.go +++ b/internal/consensus/recovery_message.go @@ -16,7 +16,7 @@ type ( preCommitPayloads []preCommitCompact commitPayloads []commitCompact changeViewPayloads []changeViewCompact - prepareRequest dbft.PrepareRequest[crypto.Uint256] + prepareRequest dbft.PrepareRequest[crypto.Uint256, *Tx64] } // recoveryMessageAux is an auxiliary structure for recoveryMessage encoding. recoveryMessageAux struct { @@ -27,7 +27,7 @@ type ( } ) -var _ dbft.RecoveryMessage[crypto.Uint256] = (*recoveryMessage)(nil) +var _ dbft.RecoveryMessage[crypto.Uint256, *Tx64] = (*recoveryMessage)(nil) // PreparationHash implements RecoveryMessage interface. func (m *recoveryMessage) PreparationHash() *crypto.Uint256 { @@ -35,7 +35,7 @@ func (m *recoveryMessage) PreparationHash() *crypto.Uint256 { } // AddPayload implements RecoveryMessage interface. -func (m *recoveryMessage) AddPayload(p dbft.ConsensusPayload[crypto.Uint256]) { +func (m *recoveryMessage) AddPayload(p dbft.ConsensusPayload[crypto.Uint256, *Tx64]) { switch p.Type() { case dbft.PrepareRequestType: m.prepareRequest = p.GetPrepareRequest() @@ -69,7 +69,7 @@ func (m *recoveryMessage) AddPayload(p dbft.ConsensusPayload[crypto.Uint256]) { } } -func fromPayload(t dbft.MessageType, recovery dbft.ConsensusPayload[crypto.Uint256], p Serializable) *Payload { +func fromPayload(t dbft.MessageType, recovery dbft.ConsensusPayload[crypto.Uint256, *Tx64], p Serializable) *Payload { return &Payload{ message: message{ cmType: t, @@ -81,21 +81,17 @@ func fromPayload(t dbft.MessageType, recovery dbft.ConsensusPayload[crypto.Uint2 } // GetPrepareRequest implements RecoveryMessage interface. -func (m *recoveryMessage) GetPrepareRequest(p dbft.ConsensusPayload[crypto.Uint256], _ []dbft.PublicKey, ind uint16) dbft.ConsensusPayload[crypto.Uint256] { +func (m *recoveryMessage) GetPrepareRequest(p dbft.ConsensusPayload[crypto.Uint256, *Tx64], _ []dbft.PublicKey, ind uint16) dbft.ConsensusPayload[crypto.Uint256, *Tx64] { if m.prepareRequest == nil { return nil } txs, _ := m.prepareRequest.Transactions() - reqTxs := make([]*Tx64, len(txs)) - for i, tx := range txs { - reqTxs[i] = tx.(*Tx64) - } req := fromPayload(dbft.PrepareRequestType, p, &prepareRequest{ // prepareRequest.Timestamp() here returns nanoseconds-precision value, so convert it to seconds again timestamp: nanoSecToSec(m.prepareRequest.Timestamp()), nonce: m.prepareRequest.Nonce(), - txs: reqTxs, + txs: txs, }) req.SetValidatorIndex(ind) @@ -103,12 +99,12 @@ func (m *recoveryMessage) GetPrepareRequest(p dbft.ConsensusPayload[crypto.Uint2 } // GetPrepareResponses implements RecoveryMessage interface. -func (m *recoveryMessage) GetPrepareResponses(p dbft.ConsensusPayload[crypto.Uint256], _ []dbft.PublicKey) []dbft.ConsensusPayload[crypto.Uint256] { +func (m *recoveryMessage) GetPrepareResponses(p dbft.ConsensusPayload[crypto.Uint256, *Tx64], _ []dbft.PublicKey) []dbft.ConsensusPayload[crypto.Uint256, *Tx64] { if m.preparationHash == nil { return nil } - payloads := make([]dbft.ConsensusPayload[crypto.Uint256], len(m.preparationPayloads)) + payloads := make([]dbft.ConsensusPayload[crypto.Uint256, *Tx64], len(m.preparationPayloads)) for i, resp := range m.preparationPayloads { payloads[i] = fromPayload(dbft.PrepareResponseType, p, &prepareResponse{ @@ -121,8 +117,8 @@ func (m *recoveryMessage) GetPrepareResponses(p dbft.ConsensusPayload[crypto.Uin } // GetChangeViews implements RecoveryMessage interface. -func (m *recoveryMessage) GetChangeViews(p dbft.ConsensusPayload[crypto.Uint256], _ []dbft.PublicKey) []dbft.ConsensusPayload[crypto.Uint256] { - payloads := make([]dbft.ConsensusPayload[crypto.Uint256], len(m.changeViewPayloads)) +func (m *recoveryMessage) GetChangeViews(p dbft.ConsensusPayload[crypto.Uint256, *Tx64], _ []dbft.PublicKey) []dbft.ConsensusPayload[crypto.Uint256, *Tx64] { + payloads := make([]dbft.ConsensusPayload[crypto.Uint256, *Tx64], len(m.changeViewPayloads)) for i, cv := range m.changeViewPayloads { payloads[i] = fromPayload(dbft.ChangeViewType, p, &changeView{ @@ -136,8 +132,8 @@ func (m *recoveryMessage) GetChangeViews(p dbft.ConsensusPayload[crypto.Uint256] } // GetPreCommits implements RecoveryMessage interface. -func (m *recoveryMessage) GetPreCommits(p dbft.ConsensusPayload[crypto.Uint256], _ []dbft.PublicKey) []dbft.ConsensusPayload[crypto.Uint256] { - payloads := make([]dbft.ConsensusPayload[crypto.Uint256], len(m.preCommitPayloads)) +func (m *recoveryMessage) GetPreCommits(p dbft.ConsensusPayload[crypto.Uint256, *Tx64], _ []dbft.PublicKey) []dbft.ConsensusPayload[crypto.Uint256, *Tx64] { + payloads := make([]dbft.ConsensusPayload[crypto.Uint256, *Tx64], len(m.preCommitPayloads)) for i, c := range m.preCommitPayloads { payloads[i] = fromPayload(dbft.PreCommitType, p, &preCommit{magic: binary.BigEndian.Uint32(c.Data)}) @@ -148,8 +144,8 @@ func (m *recoveryMessage) GetPreCommits(p dbft.ConsensusPayload[crypto.Uint256], } // GetCommits implements RecoveryMessage interface. -func (m *recoveryMessage) GetCommits(p dbft.ConsensusPayload[crypto.Uint256], _ []dbft.PublicKey) []dbft.ConsensusPayload[crypto.Uint256] { - payloads := make([]dbft.ConsensusPayload[crypto.Uint256], len(m.commitPayloads)) +func (m *recoveryMessage) GetCommits(p dbft.ConsensusPayload[crypto.Uint256, *Tx64], _ []dbft.PublicKey) []dbft.ConsensusPayload[crypto.Uint256, *Tx64] { + payloads := make([]dbft.ConsensusPayload[crypto.Uint256, *Tx64], len(m.commitPayloads)) for i, c := range m.commitPayloads { payloads[i] = fromPayload(dbft.CommitType, p, &commit{signature: c.Signature}) diff --git a/internal/simulation/main.go b/internal/simulation/main.go index 5c48a02f5..8a707882e 100644 --- a/internal/simulation/main.go +++ b/internal/simulation/main.go @@ -22,8 +22,8 @@ import ( type ( simNode struct { id int - d *dbft.DBFT[crypto.Uint256] - messages chan dbft.ConsensusPayload[crypto.Uint256] + d *dbft.DBFT[crypto.Uint256, *consensus.Tx64] + messages chan dbft.ConsensusPayload[crypto.Uint256, *consensus.Tx64] key dbft.PrivateKey pub dbft.PublicKey pool *memPool @@ -105,7 +105,7 @@ func initSimNode(nodes []*simNode, i int, log *zap.Logger) error { key, pub := crypto.Generate() nodes[i] = &simNode{ id: i, - messages: make(chan dbft.ConsensusPayload[crypto.Uint256], defaultChanSize), + messages: make(chan dbft.ConsensusPayload[crypto.Uint256, *consensus.Tx64], defaultChanSize), key: key, pub: pub, pool: newMemoryPool(), @@ -153,7 +153,7 @@ func sortValidators(pubs []dbft.PublicKey) { }) } -func (n *simNode) Broadcast(m dbft.ConsensusPayload[crypto.Uint256]) { +func (n *simNode) Broadcast(m dbft.ConsensusPayload[crypto.Uint256, *consensus.Tx64]) { for i, node := range n.cluster { if i != n.id { select { @@ -169,11 +169,11 @@ func (n *simNode) CurrentHeight() uint32 { return n.height } func (n *simNode) CurrentBlockHash() crypto.Uint256 { return n.lastHash } // GetValidators always returns the same list of validators. -func (n *simNode) GetValidators(...dbft.Transaction[crypto.Uint256]) []dbft.PublicKey { +func (n *simNode) GetValidators(...*consensus.Tx64) []dbft.PublicKey { return n.validators } -func (n *simNode) ProcessBlock(b dbft.Block[crypto.Uint256]) error { +func (n *simNode) ProcessBlock(b dbft.Block[crypto.Uint256, *consensus.Tx64]) error { n.d.Logger.Debug("received block", zap.Uint32("height", b.Index())) for _, tx := range b.Transactions() { @@ -186,7 +186,7 @@ func (n *simNode) ProcessBlock(b dbft.Block[crypto.Uint256]) error { } // VerifyPayload verifies that payload was received from a good validator. -func (n *simNode) VerifyPayload(p dbft.ConsensusPayload[crypto.Uint256]) error { +func (n *simNode) VerifyPayload(p dbft.ConsensusPayload[crypto.Uint256, *consensus.Tx64]) error { if *blocked != -1 && p.ValidatorIndex() == uint16(*blocked) { return fmt.Errorf("message from blocked validator: %d", *blocked) } @@ -205,17 +205,17 @@ func (n *simNode) addTx(count int) { type memPool struct { mtx *sync.RWMutex - store map[crypto.Uint256]dbft.Transaction[crypto.Uint256] + store map[crypto.Uint256]*consensus.Tx64 } func newMemoryPool() *memPool { return &memPool{ mtx: new(sync.RWMutex), - store: make(map[crypto.Uint256]dbft.Transaction[crypto.Uint256]), + store: make(map[crypto.Uint256]*consensus.Tx64), } } -func (p *memPool) Add(tx dbft.Transaction[crypto.Uint256]) { +func (p *memPool) Add(tx *consensus.Tx64) { p.mtx.Lock() h := tx.Hash() @@ -226,7 +226,7 @@ func (p *memPool) Add(tx dbft.Transaction[crypto.Uint256]) { p.mtx.Unlock() } -func (p *memPool) Get(h crypto.Uint256) (tx dbft.Transaction[crypto.Uint256]) { +func (p *memPool) Get(h crypto.Uint256) (tx *consensus.Tx64) { p.mtx.RLock() tx = p.store[h] p.mtx.RUnlock() @@ -240,13 +240,13 @@ func (p *memPool) Delete(h crypto.Uint256) { p.mtx.Unlock() } -func (p *memPool) GetVerified() (txx []dbft.Transaction[crypto.Uint256]) { +func (p *memPool) GetVerified() (txx []*consensus.Tx64) { n := *txPerBlock if n == 0 { return } - txx = make([]dbft.Transaction[crypto.Uint256], 0, n) + txx = make([]*consensus.Tx64, 0, n) for _, tx := range p.store { txx = append(txx, tx) diff --git a/pre_block.go b/pre_block.go index a1097005a..ba5c0ec04 100644 --- a/pre_block.go +++ b/pre_block.go @@ -3,7 +3,7 @@ package dbft // PreBlock is a generic interface for a PreBlock used by anti-MEV dBFT extension. // It holds a "draft" of block that should be converted to a final block with the // help of additional data held by PreCommit messages. -type PreBlock[H Hash] interface { +type PreBlock[H Hash, Tx Transaction[H]] interface { // Data returns PreBlock's data CNs need to exchange during PreCommit phase. // Data represents additional information not related to a final block signature. Data() []byte @@ -18,8 +18,8 @@ type PreBlock[H Hash] interface { // Transactions returns PreBlock's transaction list. This list may be different // comparing to the final set of Block's transactions. - Transactions() []Transaction[H] + Transactions() []Tx // SetTransactions sets PreBlock's transaction list. This list may be different // comparing to the final set of Block's transactions. - SetTransactions([]Transaction[H]) + SetTransactions([]Tx) } diff --git a/prepare_request.go b/prepare_request.go index 1f5b53712..8870df4ac 100644 --- a/prepare_request.go +++ b/prepare_request.go @@ -1,7 +1,7 @@ package dbft // PrepareRequest represents dBFT PrepareRequest message. -type PrepareRequest[H Hash] interface { +type PrepareRequest[H Hash, Tx Transaction[H]] interface { // Timestamp returns this message's timestamp. Timestamp() uint64 // Nonce is a random nonce. @@ -9,5 +9,5 @@ type PrepareRequest[H Hash] interface { // Transactions returns the list of all transactions in a proposed block // with possible gaps in place of missing transactions and the map of // missing transaction hashes to their indexes in the proposal list. - Transactions() ([]Transaction[H], map[H]int) + Transactions() ([]Tx, map[H]int) } diff --git a/recovery_message.go b/recovery_message.go index 02e620146..35fcccd3f 100644 --- a/recovery_message.go +++ b/recovery_message.go @@ -1,21 +1,21 @@ package dbft // RecoveryMessage represents dBFT Recovery message. -type RecoveryMessage[H Hash] interface { +type RecoveryMessage[H Hash, Tx Transaction[H]] interface { // AddPayload adds payload from this epoch to be recovered. - AddPayload(p ConsensusPayload[H]) + AddPayload(p ConsensusPayload[H, Tx]) // GetPrepareRequest returns PrepareRequest to be processed. - GetPrepareRequest(p ConsensusPayload[H], validators []PublicKey, primary uint16) ConsensusPayload[H] + GetPrepareRequest(p ConsensusPayload[H, Tx], validators []PublicKey, primary uint16) ConsensusPayload[H, Tx] // GetPrepareResponses returns a slice of PrepareResponse in any order. - GetPrepareResponses(p ConsensusPayload[H], validators []PublicKey) []ConsensusPayload[H] + GetPrepareResponses(p ConsensusPayload[H, Tx], validators []PublicKey) []ConsensusPayload[H, Tx] // GetChangeViews returns a slice of ChangeView in any order. - GetChangeViews(p ConsensusPayload[H], validators []PublicKey) []ConsensusPayload[H] + GetChangeViews(p ConsensusPayload[H, Tx], validators []PublicKey) []ConsensusPayload[H, Tx] // GetPreCommits returns a slice of PreCommit messages in any order. // If implemented on networks with no AntiMEV extension it can just // always return nil. - GetPreCommits(p ConsensusPayload[H], validators []PublicKey) []ConsensusPayload[H] + GetPreCommits(p ConsensusPayload[H, Tx], validators []PublicKey) []ConsensusPayload[H, Tx] // GetCommits returns a slice of Commit in any order. - GetCommits(p ConsensusPayload[H], validators []PublicKey) []ConsensusPayload[H] + GetCommits(p ConsensusPayload[H, Tx], validators []PublicKey) []ConsensusPayload[H, Tx] // PreparationHash returns has of PrepareRequest payload for this epoch. // It can be useful in case only PrepareResponse payloads were received. diff --git a/send.go b/send.go index 189029723..d60e56115 100644 --- a/send.go +++ b/send.go @@ -6,7 +6,7 @@ import ( "go.uber.org/zap" ) -func (d *DBFT[H]) broadcast(msg ConsensusPayload[H]) { +func (d *DBFT[H, Tx]) broadcast(msg ConsensusPayload[H, Tx]) { d.Logger.Debug("broadcasting message", zap.Stringer("type", msg.Type()), zap.Uint32("height", d.BlockIndex), @@ -16,7 +16,7 @@ func (d *DBFT[H]) broadcast(msg ConsensusPayload[H]) { d.Broadcast(msg) } -func (c *Context[H]) makePrepareRequest(force bool) ConsensusPayload[H] { +func (c *Context[H, Tx]) makePrepareRequest(force bool) ConsensusPayload[H, Tx] { if !c.Fill(force) { return nil } @@ -26,15 +26,15 @@ func (c *Context[H]) makePrepareRequest(force bool) ConsensusPayload[H] { return c.Config.NewConsensusPayload(c, PrepareRequestType, req) } -func (d *DBFT[H]) sendPrepareRequest(force bool) { +func (d *DBFT[H, Tx]) sendPrepareRequest(force bool) { msg := d.makePrepareRequest(force) - if msg == ConsensusPayload[H](nil) { + if msg == ConsensusPayload[H, Tx](nil) { d.subscribeForTransactions() // Try one more time since there's a tiny race between an attempt to // construct prepare request and transactions subscription. msg = d.makePrepareRequest(force) - if msg == ConsensusPayload[H](nil) { + if msg == ConsensusPayload[H, Tx](nil) { delay := d.maxTimePerBlock - d.timePerBlock d.changeTimer(delay) return @@ -57,7 +57,7 @@ func (d *DBFT[H]) sendPrepareRequest(force bool) { d.checkPrepare() } -func (c *Context[H]) makeChangeView(ts uint64, reason ChangeViewReason) ConsensusPayload[H] { +func (c *Context[H, Tx]) makeChangeView(ts uint64, reason ChangeViewReason) ConsensusPayload[H, Tx] { cv := c.Config.NewChangeView(c.ViewNumber+1, reason, ts) msg := c.Config.NewConsensusPayload(c, ChangeViewType, cv) @@ -66,7 +66,7 @@ func (c *Context[H]) makeChangeView(ts uint64, reason ChangeViewReason) Consensu return msg } -func (d *DBFT[H]) sendChangeView(reason ChangeViewReason) { +func (d *DBFT[H, Tx]) sendChangeView(reason ChangeViewReason) { if d.Context.WatchOnly() { return } @@ -103,7 +103,7 @@ func (d *DBFT[H]) sendChangeView(reason ChangeViewReason) { d.checkChangeView(newView) } -func (c *Context[H]) makePrepareResponse() ConsensusPayload[H] { +func (c *Context[H, Tx]) makePrepareResponse() ConsensusPayload[H, Tx] { resp := c.Config.NewPrepareResponse(c.PreparationPayloads[c.PrimaryIndex].Hash()) msg := c.Config.NewConsensusPayload(c, PrepareResponseType, resp) @@ -112,14 +112,14 @@ func (c *Context[H]) makePrepareResponse() ConsensusPayload[H] { return msg } -func (d *DBFT[H]) sendPrepareResponse() { +func (d *DBFT[H, Tx]) sendPrepareResponse() { msg := d.makePrepareResponse() d.Logger.Info("sending PrepareResponse", zap.Uint32("height", d.BlockIndex), zap.Uint("view", uint(d.ViewNumber))) d.StopTxFlow() d.broadcast(msg) } -func (c *Context[H]) makePreCommit() (ConsensusPayload[H], error) { +func (c *Context[H, Tx]) makePreCommit() (ConsensusPayload[H, Tx], error) { if msg := c.PreCommitPayloads[c.MyIndex]; msg != nil { return msg, nil } @@ -140,7 +140,7 @@ func (c *Context[H]) makePreCommit() (ConsensusPayload[H], error) { return nil, fmt.Errorf("failed to construct PreBlock") } -func (c *Context[H]) makeCommit() (ConsensusPayload[H], error) { +func (c *Context[H, Tx]) makeCommit() (ConsensusPayload[H, Tx], error) { if msg := c.CommitPayloads[c.MyIndex]; msg != nil { return msg, nil } @@ -161,7 +161,7 @@ func (c *Context[H]) makeCommit() (ConsensusPayload[H], error) { return nil, fmt.Errorf("failed to construct Header") } -func (d *DBFT[H]) sendPreCommit() { +func (d *DBFT[H, Tx]) sendPreCommit() { msg, err := d.makePreCommit() if err != nil { d.Logger.Error("failed to construct PreCommit", zap.Error(err)) @@ -172,7 +172,7 @@ func (d *DBFT[H]) sendPreCommit() { d.broadcast(msg) } -func (d *DBFT[H]) sendCommit() { +func (d *DBFT[H, Tx]) sendCommit() { msg, err := d.makeCommit() if err != nil { d.Logger.Error("failed to construct Commit", zap.Error(err)) @@ -183,7 +183,7 @@ func (d *DBFT[H]) sendCommit() { d.broadcast(msg) } -func (d *DBFT[H]) sendRecoveryRequest() { +func (d *DBFT[H, Tx]) sendRecoveryRequest() { // If we're here, something is wrong, we either missing some messages or // transactions or both, so re-request missing transactions here too. if d.RequestSentOrReceived() && !d.hasAllTransactions() { @@ -193,7 +193,7 @@ func (d *DBFT[H]) sendRecoveryRequest() { d.broadcast(d.NewConsensusPayload(&d.Context, RecoveryRequestType, req)) } -func (c *Context[H]) makeRecoveryMessage() ConsensusPayload[H] { +func (c *Context[H, Tx]) makeRecoveryMessage() ConsensusPayload[H, Tx] { recovery := c.Config.NewRecoveryMessage() for _, p := range c.PreparationPayloads { @@ -231,6 +231,6 @@ func (c *Context[H]) makeRecoveryMessage() ConsensusPayload[H] { return c.Config.NewConsensusPayload(c, RecoveryMessageType, recovery) } -func (d *DBFT[H]) sendRecoveryMessage() { +func (d *DBFT[H, Tx]) sendRecoveryMessage() { d.broadcast(d.makeRecoveryMessage()) }