diff --git a/README.md b/README.md index a0e7c65c..03177de9 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,17 @@ written in [TLA⁺](https://lamport.azurewebsites.net/tla/tla.html) language. ## Design and structure 1. All control flow is done in main package. Most of the code which communicates with external -world (event time events) is hidden behind interfaces and callbacks. As a consequence it is -highly flexible and extendable. Description of config options can be found in `config.go`. +world (event time events) is hidden behind interfaces, callbacks and generic parameters. As a +consequence it is highly flexible and extendable. Description of config options can be found +in `config.go`. 2. `crypto` package contains `PrivateKey`/`PublicKey` interfaces which permits usage of one's own cryptography for signing blocks on `Commit` stage. Default implementation with ECDSA signatures is provided, BLS multisignatures could be added in the nearest future. +3. `crypto` package contains `Hash`/`Address` interfaces which permits usage of one's own +hash/address implementation without additional overhead on conversions. Instantiate dBFT with +custom hash/address implementation that matches requirements specified in the corresponding +documentation. 3. `block` package contains `Block` and `Transaction` abstractions. Every block must be able to be signed and verified as well as implement setters and getters for main fields. Minimal default implementation is provided. diff --git a/block/block.go b/block/block.go index 3235c567..f041b12a 100644 --- a/block/block.go +++ b/block/block.go @@ -6,7 +6,6 @@ import ( "github.com/nspcc-dev/dbft/crypto" "github.com/nspcc-dev/dbft/merkle" - "github.com/nspcc-dev/neo-go/pkg/util" ) type ( @@ -17,21 +16,21 @@ type ( Index uint32 Timestamp uint32 Version uint32 - MerkleRoot util.Uint256 - PrevHash util.Uint256 - NextConsensus util.Uint160 + MerkleRoot crypto.Uint256 + PrevHash crypto.Uint256 + NextConsensus crypto.Uint160 } // Block is a generic interface for a block used by dbft. - Block interface { + Block[H crypto.Hash, A crypto.Address] interface { // Hash returns block hash. - Hash() util.Uint256 + Hash() H Version() uint32 // PrevHash returns previous block hash. - PrevHash() util.Uint256 + PrevHash() H // MerkleRoot returns a merkle root of the transaction hashes. - MerkleRoot() util.Uint256 + MerkleRoot() H // Timestamp returns block's proposal timestamp. Timestamp() uint64 // Index returns block index. @@ -39,7 +38,7 @@ type ( // ConsensusData is a random nonce. ConsensusData() uint64 // NextConsensus returns hash of the validators of the next block. - NextConsensus() util.Uint160 + NextConsensus() A // Signature returns block's signature. Signature() []byte @@ -49,18 +48,18 @@ type ( Verify(key crypto.PublicKey, sign []byte) error // Transactions returns block's transaction list. - Transactions() []Transaction + Transactions() []Transaction[H] // SetTransactions sets block's transaction list. - SetTransactions([]Transaction) + SetTransactions([]Transaction[H]) } neoBlock struct { base consensusData uint64 - transactions []Transaction + transactions []Transaction[crypto.Uint256] signature []byte - hash *util.Uint256 + hash *crypto.Uint256 } ) @@ -70,7 +69,7 @@ func (b neoBlock) Version() uint32 { } // PrevHash implements Block interface. -func (b *neoBlock) PrevHash() util.Uint256 { +func (b *neoBlock) PrevHash() crypto.Uint256 { return b.base.PrevHash } @@ -85,12 +84,12 @@ func (b *neoBlock) Index() uint32 { } // NextConsensus implements Block interface. -func (b *neoBlock) NextConsensus() util.Uint160 { +func (b *neoBlock) NextConsensus() crypto.Uint160 { return b.base.NextConsensus } // MerkleRoot implements Block interface. -func (b *neoBlock) MerkleRoot() util.Uint256 { +func (b *neoBlock) MerkleRoot() crypto.Uint256 { return b.base.MerkleRoot } @@ -100,17 +99,17 @@ func (b *neoBlock) ConsensusData() uint64 { } // Transactions implements Block interface. -func (b *neoBlock) Transactions() []Transaction { +func (b *neoBlock) Transactions() []Transaction[crypto.Uint256] { return b.transactions } // SetTransactions implements Block interface. -func (b *neoBlock) SetTransactions(txx []Transaction) { +func (b *neoBlock) SetTransactions(txx []Transaction[crypto.Uint256]) { b.transactions = txx } // NewBlock returns new block. -func NewBlock(timestamp uint64, index uint32, nextConsensus util.Uint160, prevHash util.Uint256, version uint32, nonce uint64, txHashes []util.Uint256) Block { +func NewBlock(timestamp uint64, index uint32, nextConsensus crypto.Uint160, prevHash crypto.Uint256, version uint32, nonce uint64, txHashes []crypto.Uint256) Block[crypto.Uint256, crypto.Uint160] { block := new(neoBlock) block.base.Timestamp = uint32(timestamp / 1000000000) block.base.Index = index @@ -165,7 +164,7 @@ func (b *neoBlock) Verify(pub crypto.PublicKey, sign []byte) error { } // Hash implements Block interface. -func (b *neoBlock) Hash() (h util.Uint256) { +func (b *neoBlock) Hash() (h crypto.Uint256) { if b.hash != nil { return *b.hash } else if b.transactions == nil { diff --git a/block/block_test.go b/block/block_test.go index 96e14098..35cb5461 100644 --- a/block/block_test.go +++ b/block/block_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/nspcc-dev/dbft/crypto" - "github.com/nspcc-dev/neo-go/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -17,9 +16,9 @@ import ( func TestNeoBlock_Setters(t *testing.T) { b := new(neoBlock) - require.Equal(t, util.Uint256{}, b.Hash()) + require.Equal(t, crypto.Uint256{}, b.Hash()) - txs := []Transaction{testTx(1), testTx(2)} + txs := []Transaction[crypto.Uint256]{testTx(1), testTx(2)} b.SetTransactions(txs) assert.Equal(t, txs, b.Transactions()) @@ -29,14 +28,14 @@ func TestNeoBlock_Setters(t *testing.T) { b.base.Version = 42 assert.EqualValues(t, 42, b.Version()) - b.base.NextConsensus = util.Uint160{1} - assert.Equal(t, util.Uint160{1}, b.NextConsensus()) + b.base.NextConsensus = crypto.Uint160{1} + assert.Equal(t, crypto.Uint160{1}, b.NextConsensus()) - b.base.PrevHash = util.Uint256{3, 7} - assert.Equal(t, util.Uint256{3, 7}, b.PrevHash()) + b.base.PrevHash = crypto.Uint256{3, 7} + assert.Equal(t, crypto.Uint256{3, 7}, b.PrevHash()) - b.base.MerkleRoot = util.Uint256{13} - assert.Equal(t, util.Uint256{13}, b.MerkleRoot()) + b.base.MerkleRoot = crypto.Uint256{13} + assert.Equal(t, crypto.Uint256{13}, b.MerkleRoot()) b.base.Timestamp = 1234 // 1234s -> 1234000000000ns @@ -85,7 +84,7 @@ func (t testKey) Sign([]byte) ([]byte, error) { type testTx uint64 -func (tx testTx) Hash() (h util.Uint256) { +func (tx testTx) Hash() (h crypto.Uint256) { binary.LittleEndian.PutUint64(h[:], uint64(tx)) return } diff --git a/block/transaction.go b/block/transaction.go index 39d26ad2..cb15ceeb 100644 --- a/block/transaction.go +++ b/block/transaction.go @@ -1,12 +1,12 @@ package block import ( - "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/dbft/crypto" ) // Transaction is a generic transaction interface. -type Transaction interface { +type Transaction[H crypto.Hash] interface { // Hash must return cryptographic hash of the transaction. // Transactions which have equal hashes are considered equal. - Hash() util.Uint256 + Hash() H } diff --git a/check.go b/check.go index c71b0106..14b2bb46 100644 --- a/check.go +++ b/check.go @@ -5,7 +5,7 @@ import ( "go.uber.org/zap" ) -func (d *DBFT) checkPrepare() { +func (d *DBFT[H, A]) checkPrepare() { if !d.hasAllTransactions() { d.Logger.Debug("check prepare: some transactions are missing", zap.Any("hashes", d.MissingTransactions)) return @@ -37,7 +37,7 @@ func (d *DBFT) checkPrepare() { } } -func (d *DBFT) checkCommit() { +func (d *DBFT[H, A]) checkCommit() { if !d.hasAllTransactions() { d.Logger.Debug("check commit: some transactions are missing", zap.Any("hashes", d.MissingTransactions)) return @@ -78,7 +78,7 @@ func (d *DBFT) checkCommit() { // new height. } -func (d *DBFT) checkChangeView(view byte) { +func (d *DBFT[H, A]) checkChangeView(view byte) { if d.ViewNumber >= view { return } diff --git a/config.go b/config.go index bdd803b2..13399da2 100644 --- a/config.go +++ b/config.go @@ -9,12 +9,11 @@ import ( "github.com/nspcc-dev/dbft/crypto" "github.com/nspcc-dev/dbft/payload" "github.com/nspcc-dev/dbft/timer" - "github.com/nspcc-dev/neo-go/pkg/util" "go.uber.org/zap" ) // Config contains initialization and working parameters for dBFT. -type Config struct { +type Config[H crypto.Hash, A crypto.Address] struct { // Logger Logger *zap.Logger // Timer @@ -30,45 +29,45 @@ type Config struct { // together with it's key pair. GetKeyPair func([]crypto.PublicKey) (int, crypto.PrivateKey, crypto.PublicKey) // NewBlockFromContext should allocate, fill from Context and return new block.Block. - NewBlockFromContext func(ctx *Context) block.Block + NewBlockFromContext func(ctx *Context[H, A]) block.Block[H, A] // RequestTx is a callback which is called when transaction contained // in current block can't be found in memory pool. - RequestTx func(h ...util.Uint256) + RequestTx func(h ...H) // StopTxFlow is a callback which is called when the process no longer needs // any transactions. StopTxFlow func() // GetTx returns a transaction from memory pool. - GetTx func(h util.Uint256) block.Transaction + GetTx func(h H) block.Transaction[H] // GetVerified returns a slice of verified transactions // to be proposed in a new block. - GetVerified func() []block.Transaction + GetVerified func() []block.Transaction[H] // VerifyBlock verifies if block is valid. - VerifyBlock func(b block.Block) bool + VerifyBlock func(b block.Block[H, A]) bool // Broadcast should broadcast payload m to the consensus nodes. - Broadcast func(m payload.ConsensusPayload) + Broadcast func(m payload.ConsensusPayload[H, A]) // ProcessBlock is called every time new block is accepted. - ProcessBlock func(b block.Block) + ProcessBlock func(b block.Block[H, A]) // GetBlock should return block with hash. - GetBlock func(h util.Uint256) block.Block + GetBlock func(h H) block.Block[H, A] // WatchOnly tells if a node should only watch. WatchOnly func() bool // CurrentHeight returns index of the last accepted block. CurrentHeight func() uint32 // CurrentBlockHash returns hash of the last accepted block. - CurrentBlockHash func() util.Uint256 + CurrentBlockHash func() H // GetValidators returns list of the validators. // 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(...block.Transaction) []crypto.PublicKey + GetValidators func(...block.Transaction[H]) []crypto.PublicKey // GetConsensusAddress returns hash of the validator list. - GetConsensusAddress func(...crypto.PublicKey) util.Uint160 + GetConsensusAddress func(...crypto.PublicKey) A // NewConsensusPayload is a constructor for payload.ConsensusPayload. - NewConsensusPayload func(*Context, payload.MessageType, any) payload.ConsensusPayload + NewConsensusPayload func(*Context[H, A], payload.MessageType, any) payload.ConsensusPayload[H, A] // NewPrepareRequest is a constructor for payload.PrepareRequest. - NewPrepareRequest func() payload.PrepareRequest + NewPrepareRequest func() payload.PrepareRequest[H, A] // NewPrepareResponse is a constructor for payload.PrepareResponse. - NewPrepareResponse func() payload.PrepareResponse + NewPrepareResponse func() payload.PrepareResponse[H] // NewChangeView is a constructor for payload.ChangeView. NewChangeView func() payload.ChangeView // NewCommit is a constructor for payload.Commit. @@ -76,56 +75,44 @@ type Config struct { // NewRecoveryRequest is a constructor for payload.RecoveryRequest. NewRecoveryRequest func() payload.RecoveryRequest // NewRecoveryMessage is a constructor for payload.RecoveryMessage. - NewRecoveryMessage func() payload.RecoveryMessage + NewRecoveryMessage func() payload.RecoveryMessage[H, A] // VerifyPrepareRequest can perform external payload verification and returns true iff it was successful. - VerifyPrepareRequest func(p payload.ConsensusPayload) error + VerifyPrepareRequest func(p payload.ConsensusPayload[H, A]) error // VerifyPrepareResponse performs external PrepareResponse verification and returns nil if it's successful. - VerifyPrepareResponse func(p payload.ConsensusPayload) error + VerifyPrepareResponse func(p payload.ConsensusPayload[H, A]) error } const defaultSecondsPerBlock = time.Second * 15 const defaultTimestampIncrement = uint64(time.Millisecond / time.Nanosecond) -// Option is a generic options type. It can modify config in any way it wants. -type Option = func(*Config) - -func defaultConfig() *Config { +func defaultConfig[H crypto.Hash, A crypto.Address]() *Config[H, A] { // fields which are set to nil must be provided from client - return &Config{ - Logger: zap.NewNop(), - Timer: timer.New(), - SecondsPerBlock: defaultSecondsPerBlock, - TimestampIncrement: defaultTimestampIncrement, - GetKeyPair: nil, - NewBlockFromContext: NewBlockFromContext, - RequestTx: func(...util.Uint256) {}, - StopTxFlow: func() {}, - GetTx: func(util.Uint256) block.Transaction { return nil }, - GetVerified: func() []block.Transaction { return make([]block.Transaction, 0) }, - VerifyBlock: func(block.Block) bool { return true }, - Broadcast: func(payload.ConsensusPayload) {}, - ProcessBlock: func(block.Block) {}, - GetBlock: func(util.Uint256) block.Block { return nil }, - WatchOnly: func() bool { return false }, - CurrentHeight: nil, - CurrentBlockHash: nil, - GetValidators: nil, - GetConsensusAddress: func(...crypto.PublicKey) util.Uint160 { return util.Uint160{} }, - NewConsensusPayload: defaultNewConsensusPayload, - NewPrepareRequest: payload.NewPrepareRequest, - NewPrepareResponse: payload.NewPrepareResponse, - NewChangeView: payload.NewChangeView, - NewCommit: payload.NewCommit, - NewRecoveryRequest: payload.NewRecoveryRequest, - NewRecoveryMessage: payload.NewRecoveryMessage, - - VerifyPrepareRequest: func(payload.ConsensusPayload) error { return nil }, - VerifyPrepareResponse: func(payload.ConsensusPayload) error { return nil }, - } -} - -func checkConfig(cfg *Config) error { + return &Config[H, A]{ + Logger: zap.NewNop(), + Timer: timer.New(), + SecondsPerBlock: defaultSecondsPerBlock, + TimestampIncrement: defaultTimestampIncrement, + GetKeyPair: nil, + RequestTx: func(...H) {}, + StopTxFlow: func() {}, + GetTx: func(H) block.Transaction[H] { return nil }, + GetVerified: func() []block.Transaction[H] { return make([]block.Transaction[H], 0) }, + VerifyBlock: func(block.Block[H, A]) bool { return true }, + Broadcast: func(payload.ConsensusPayload[H, A]) {}, + ProcessBlock: func(block.Block[H, A]) {}, + GetBlock: func(H) block.Block[H, A] { return nil }, + WatchOnly: func() bool { return false }, + CurrentHeight: nil, + CurrentBlockHash: nil, + GetValidators: nil, + + VerifyPrepareRequest: func(payload.ConsensusPayload[H, A]) error { return nil }, + VerifyPrepareResponse: func(payload.ConsensusPayload[H, A]) error { return nil }, + } +} + +func checkConfig[H crypto.Hash, A crypto.Address](cfg *Config[H, A]) error { if cfg.GetKeyPair == nil { return errors.New("private key is nil") } else if cfg.CurrentHeight == nil { @@ -134,6 +121,24 @@ func checkConfig(cfg *Config) error { return errors.New("CurrentBlockHash is nil") } else if cfg.GetValidators == nil { return errors.New("GetValidators is nil") + } else if cfg.NewBlockFromContext == nil { + return errors.New("NewBlockFromContext is nil") + } else if cfg.GetConsensusAddress == nil { + return errors.New("GetConsensusAddress is nil") + } else if cfg.NewConsensusPayload == nil { + return errors.New("NewConsensusPayload is nil") + } else if cfg.NewPrepareRequest == nil { + return errors.New("NewPrepareRequest is nil") + } else if cfg.NewPrepareResponse == nil { + return errors.New("NewPrepareResponse is nil") + } else if cfg.NewChangeView == nil { + return errors.New("NewChangeView is nil") + } else if cfg.NewCommit == nil { + return errors.New("NewCommit is nil") + } else if cfg.NewRecoveryRequest == nil { + return errors.New("NewRecoveryRequest is nil") + } else if cfg.NewRecoveryMessage == nil { + return errors.New("NewRecoveryMessage is nil") } return nil @@ -141,13 +146,13 @@ func checkConfig(cfg *Config) error { // WithKeyPair sets GetKeyPair to a function returning default key pair // if it is present in a list of validators. -func WithKeyPair(priv crypto.PrivateKey, pub crypto.PublicKey) Option { +func WithKeyPair[H crypto.Hash, A crypto.Address](priv crypto.PrivateKey, pub crypto.PublicKey) func(config *Config[H, A]) { myPub, err := pub.MarshalBinary() if err != nil { return nil } - return func(cfg *Config) { + return func(cfg *Config[H, A]) { cfg.GetKeyPair = func(ps []crypto.PublicKey) (int, crypto.PrivateKey, crypto.PublicKey) { for i := range ps { pi, err := ps[i].MarshalBinary() @@ -164,197 +169,197 @@ func WithKeyPair(priv crypto.PrivateKey, pub crypto.PublicKey) Option { } // WithGetKeyPair sets GetKeyPair. -func WithGetKeyPair(f func([]crypto.PublicKey) (int, crypto.PrivateKey, crypto.PublicKey)) Option { - return func(cfg *Config) { +func WithGetKeyPair[H crypto.Hash, A crypto.Address](f func([]crypto.PublicKey) (int, crypto.PrivateKey, crypto.PublicKey)) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.GetKeyPair = f } } // WithLogger sets Logger. -func WithLogger(log *zap.Logger) Option { - return func(cfg *Config) { +func WithLogger[H crypto.Hash, A crypto.Address](log *zap.Logger) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.Logger = log } } // WithTimer sets Timer. -func WithTimer(t timer.Timer) Option { - return func(cfg *Config) { +func WithTimer[H crypto.Hash, A crypto.Address](t timer.Timer) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.Timer = t } } // WithSecondsPerBlock sets SecondsPerBlock. -func WithSecondsPerBlock(d time.Duration) Option { - return func(cfg *Config) { +func WithSecondsPerBlock[H crypto.Hash, A crypto.Address](d time.Duration) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.SecondsPerBlock = d } } // WithTimestampIncrement sets TimestampIncrement. -func WithTimestampIncrement(u uint64) Option { - return func(cfg *Config) { +func WithTimestampIncrement[H crypto.Hash, A crypto.Address](u uint64) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.TimestampIncrement = u } } // WithNewBlockFromContext sets NewBlockFromContext. -func WithNewBlockFromContext(f func(ctx *Context) block.Block) Option { - return func(cfg *Config) { +func WithNewBlockFromContext[H crypto.Hash, A crypto.Address](f func(ctx *Context[H, A]) block.Block[H, A]) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.NewBlockFromContext = f } } // WithRequestTx sets RequestTx. -func WithRequestTx(f func(h ...util.Uint256)) Option { - return func(cfg *Config) { +func WithRequestTx[H crypto.Hash, A crypto.Address](f func(h ...H)) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.RequestTx = f } } // WithStopTxFlow sets StopTxFlow. -func WithStopTxFlow(f func()) Option { - return func(cfg *Config) { +func WithStopTxFlow[H crypto.Hash, A crypto.Address](f func()) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.StopTxFlow = f } } // WithGetTx sets GetTx. -func WithGetTx(f func(h util.Uint256) block.Transaction) Option { - return func(cfg *Config) { +func WithGetTx[H crypto.Hash, A crypto.Address](f func(h H) block.Transaction[H]) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.GetTx = f } } // WithGetVerified sets GetVerified. -func WithGetVerified(f func() []block.Transaction) Option { - return func(cfg *Config) { +func WithGetVerified[H crypto.Hash, A crypto.Address](f func() []block.Transaction[H]) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.GetVerified = f } } // WithVerifyBlock sets VerifyBlock. -func WithVerifyBlock(f func(b block.Block) bool) Option { - return func(cfg *Config) { +func WithVerifyBlock[H crypto.Hash, A crypto.Address](f func(b block.Block[H, A]) bool) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.VerifyBlock = f } } // WithBroadcast sets Broadcast. -func WithBroadcast(f func(m payload.ConsensusPayload)) Option { - return func(cfg *Config) { +func WithBroadcast[H crypto.Hash, A crypto.Address](f func(m payload.ConsensusPayload[H, A])) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.Broadcast = f } } // WithProcessBlock sets ProcessBlock. -func WithProcessBlock(f func(b block.Block)) Option { - return func(cfg *Config) { +func WithProcessBlock[H crypto.Hash, A crypto.Address](f func(b block.Block[H, A])) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.ProcessBlock = f } } // WithGetBlock sets GetBlock. -func WithGetBlock(f func(h util.Uint256) block.Block) Option { - return func(cfg *Config) { +func WithGetBlock[H crypto.Hash, A crypto.Address](f func(h H) block.Block[H, A]) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.GetBlock = f } } // WithWatchOnly sets WatchOnly. -func WithWatchOnly(f func() bool) Option { - return func(cfg *Config) { +func WithWatchOnly[H crypto.Hash, A crypto.Address](f func() bool) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.WatchOnly = f } } // WithCurrentHeight sets CurrentHeight. -func WithCurrentHeight(f func() uint32) Option { - return func(cfg *Config) { +func WithCurrentHeight[H crypto.Hash, A crypto.Address](f func() uint32) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.CurrentHeight = f } } // WithCurrentBlockHash sets CurrentBlockHash. -func WithCurrentBlockHash(f func() util.Uint256) Option { - return func(cfg *Config) { +func WithCurrentBlockHash[H crypto.Hash, A crypto.Address](f func() H) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.CurrentBlockHash = f } } // WithGetValidators sets GetValidators. -func WithGetValidators(f func(...block.Transaction) []crypto.PublicKey) Option { - return func(cfg *Config) { +func WithGetValidators[H crypto.Hash, A crypto.Address](f func(...block.Transaction[H]) []crypto.PublicKey) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.GetValidators = f } } // WithGetConsensusAddress sets GetConsensusAddress. -func WithGetConsensusAddress(f func(keys ...crypto.PublicKey) util.Uint160) Option { - return func(cfg *Config) { +func WithGetConsensusAddress[H crypto.Hash, A crypto.Address](f func(keys ...crypto.PublicKey) A) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.GetConsensusAddress = f } } // WithNewConsensusPayload sets NewConsensusPayload. -func WithNewConsensusPayload(f func(*Context, payload.MessageType, any) payload.ConsensusPayload) Option { - return func(cfg *Config) { +func WithNewConsensusPayload[H crypto.Hash, A crypto.Address](f func(*Context[H, A], payload.MessageType, any) payload.ConsensusPayload[H, A]) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.NewConsensusPayload = f } } // WithNewPrepareRequest sets NewPrepareRequest. -func WithNewPrepareRequest(f func() payload.PrepareRequest) Option { - return func(cfg *Config) { +func WithNewPrepareRequest[H crypto.Hash, A crypto.Address](f func() payload.PrepareRequest[H, A]) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.NewPrepareRequest = f } } // WithNewPrepareResponse sets NewPrepareResponse. -func WithNewPrepareResponse(f func() payload.PrepareResponse) Option { - return func(cfg *Config) { +func WithNewPrepareResponse[H crypto.Hash, A crypto.Address](f func() payload.PrepareResponse[H]) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.NewPrepareResponse = f } } // WithNewChangeView sets NewChangeView. -func WithNewChangeView(f func() payload.ChangeView) Option { - return func(cfg *Config) { +func WithNewChangeView[H crypto.Hash, A crypto.Address](f func() payload.ChangeView) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.NewChangeView = f } } // WithNewCommit sets NewCommit. -func WithNewCommit(f func() payload.Commit) Option { - return func(cfg *Config) { +func WithNewCommit[H crypto.Hash, A crypto.Address](f func() payload.Commit) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.NewCommit = f } } // WithNewRecoveryRequest sets NewRecoveryRequest. -func WithNewRecoveryRequest(f func() payload.RecoveryRequest) Option { - return func(cfg *Config) { +func WithNewRecoveryRequest[H crypto.Hash, A crypto.Address](f func() payload.RecoveryRequest) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.NewRecoveryRequest = f } } // WithNewRecoveryMessage sets NewRecoveryMessage. -func WithNewRecoveryMessage(f func() payload.RecoveryMessage) Option { - return func(cfg *Config) { +func WithNewRecoveryMessage[H crypto.Hash, A crypto.Address](f func() payload.RecoveryMessage[H, A]) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.NewRecoveryMessage = f } } // WithVerifyPrepareRequest sets VerifyPrepareRequest. -func WithVerifyPrepareRequest(f func(payload.ConsensusPayload) error) Option { - return func(cfg *Config) { +func WithVerifyPrepareRequest[H crypto.Hash, A crypto.Address](f func(payload.ConsensusPayload[H, A]) error) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.VerifyPrepareRequest = f } } // WithVerifyPrepareResponse sets VerifyPrepareResponse. -func WithVerifyPrepareResponse(f func(payload.ConsensusPayload) error) Option { - return func(cfg *Config) { +func WithVerifyPrepareResponse[H crypto.Hash, A crypto.Address](f func(payload.ConsensusPayload[H, A]) error) func(config *Config[H, A]) { + return func(cfg *Config[H, A]) { cfg.VerifyPrepareResponse = f } } diff --git a/context.go b/context.go index 70a84d94..83e0f99f 100644 --- a/context.go +++ b/context.go @@ -9,22 +9,21 @@ import ( "github.com/nspcc-dev/dbft/crypto" "github.com/nspcc-dev/dbft/payload" "github.com/nspcc-dev/dbft/timer" - "github.com/nspcc-dev/neo-go/pkg/util" ) // Context is a main dBFT structure which // contains all information needed for performing transitions. -type Context struct { +type Context[H crypto.Hash, A crypto.Address] struct { // Config is dBFT's Config instance. - Config *Config + Config *Config[H, A] // Priv is node's private key. Priv crypto.PrivateKey // Pub is node's public key. Pub crypto.PublicKey - block block.Block - header block.Block + block block.Block[H, A] + header block.Block[H, A] // 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 @@ -45,33 +44,33 @@ type Context struct { Version uint32 // NextConsensus is a hash of the validators which will be accepting the next block. - NextConsensus util.Uint160 + NextConsensus A // PrevHash is a hash of the previous block. - PrevHash util.Uint256 + PrevHash H // Timestamp is a nanosecond-precision timestamp Timestamp uint64 Nonce uint64 // TransactionHashes is a slice of hashes of proposed transactions in the current block. - TransactionHashes []util.Uint256 + TransactionHashes []H // MissingTransactions is a slice of hashes containing missing transactions for the current block. - MissingTransactions []util.Uint256 + MissingTransactions []H // Transactions is a map containing actual transactions for the current block. - Transactions map[util.Uint256]block.Transaction + Transactions map[H]block.Transaction[H] // PreparationPayloads stores consensus Prepare* payloads for the current epoch. - PreparationPayloads []payload.ConsensusPayload + PreparationPayloads []payload.ConsensusPayload[H, A] // 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 []payload.ConsensusPayload + CommitPayloads []payload.ConsensusPayload[H, A] // ChangeViewPayloads stores consensus ChangeView payloads for the current epoch. - ChangeViewPayloads []payload.ConsensusPayload + ChangeViewPayloads []payload.ConsensusPayload[H, A] // LastChangeViewPayloads stores consensus ChangeView payloads for the last epoch. - LastChangeViewPayloads []payload.ConsensusPayload + LastChangeViewPayloads []payload.ConsensusPayload[H, A] // LastSeenMessage array stores the height of the last seen message, for each validator. // if this node never heard from validator i, LastSeenMessage[i] will be -1. LastSeenMessage []*timer.HV @@ -82,16 +81,16 @@ type Context struct { } // N returns total number of validators. -func (c *Context) N() int { return len(c.Validators) } +func (c *Context[H, A]) N() int { return len(c.Validators) } // F returns number of validators which can be faulty. -func (c *Context) F() int { return (len(c.Validators) - 1) / 3 } +func (c *Context[H, A]) F() int { return (len(c.Validators) - 1) / 3 } // M returns number of validators which must function correctly. -func (c *Context) M() int { return len(c.Validators) - c.F() } +func (c *Context[H, A]) M() int { return len(c.Validators) - c.F() } // GetPrimaryIndex returns index of a primary node for the specified view. -func (c *Context) GetPrimaryIndex(viewNumber byte) uint { +func (c *Context[H, A]) GetPrimaryIndex(viewNumber byte) uint { p := (int(c.BlockIndex) - int(viewNumber)) % len(c.Validators) if p >= 0 { return uint(p) @@ -101,19 +100,19 @@ func (c *Context) GetPrimaryIndex(viewNumber byte) uint { } // IsPrimary returns true iff node is primary for current height and view. -func (c *Context) IsPrimary() bool { return c.MyIndex == int(c.PrimaryIndex) } +func (c *Context[H, A]) IsPrimary() bool { return c.MyIndex == int(c.PrimaryIndex) } // IsBackup returns true iff node is backup for current height and view. -func (c *Context) IsBackup() bool { +func (c *Context[H, A]) IsBackup() bool { return c.MyIndex >= 0 && !c.IsPrimary() } // WatchOnly returns true iff node takes no active part in consensus. -func (c *Context) WatchOnly() bool { return c.MyIndex < 0 || c.Config.WatchOnly() } +func (c *Context[H, A]) WatchOnly() bool { return c.MyIndex < 0 || c.Config.WatchOnly() } // CountCommitted returns number of received Commit messages not only for the current // epoch but also for any other epoch. -func (c *Context) CountCommitted() (count int) { +func (c *Context[H, A]) CountCommitted() (count int) { for i := range c.CommitPayloads { if c.CommitPayloads[i] != nil { count++ @@ -125,7 +124,7 @@ func (c *Context) 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) CountFailed() (count int) { +func (c *Context[H, A]) CountFailed() (count int) { for i, hv := range c.LastSeenMessage { if c.CommitPayloads[i] == nil && (hv == nil || hv.Height < c.BlockIndex || hv.View < c.ViewNumber) { count++ @@ -137,18 +136,18 @@ func (c *Context) CountFailed() (count int) { // RequestSentOrReceived returns true iff PrepareRequest // was sent or received for the current epoch. -func (c *Context) RequestSentOrReceived() bool { +func (c *Context[H, A]) RequestSentOrReceived() bool { return c.PreparationPayloads[c.PrimaryIndex] != nil } // ResponseSent returns true iff Prepare* message was sent for the current epoch. -func (c *Context) ResponseSent() bool { +func (c *Context[H, A]) ResponseSent() bool { return !c.WatchOnly() && c.PreparationPayloads[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) CommitSent() bool { +func (c *Context[H, A]) CommitSent() bool { return !c.WatchOnly() && c.CommitPayloads[c.MyIndex] != nil } @@ -165,10 +164,10 @@ func (c *Context) 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) BlockSent() bool { return c.blockProcessed } +func (c *Context[H, A]) BlockSent() bool { return c.blockProcessed } // ViewChanging returns true iff node is in a process of changing view. -func (c *Context) ViewChanging() bool { +func (c *Context[H, A]) ViewChanging() bool { if c.WatchOnly() { return false } @@ -179,7 +178,7 @@ func (c *Context) ViewChanging() bool { } // NotAcceptingPayloadsDueToViewChanging returns true if node should not accept new payloads. -func (c *Context) NotAcceptingPayloadsDueToViewChanging() bool { +func (c *Context[H, A]) NotAcceptingPayloadsDueToViewChanging() bool { return c.ViewChanging() && !c.MoreThanFNodesCommittedOrLost() } @@ -190,11 +189,11 @@ func (c *Context) 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) MoreThanFNodesCommittedOrLost() bool { +func (c *Context[H, A]) MoreThanFNodesCommittedOrLost() bool { return c.CountCommitted()+c.CountFailed() > c.F() } -func (c *Context) reset(view byte, ts uint64) { +func (c *Context[H, A]) reset(view byte, ts uint64) { c.MyIndex = -1 c.lastBlockTimestamp = ts @@ -204,7 +203,7 @@ func (c *Context) reset(view byte, ts uint64) { c.Validators = c.Config.GetValidators() n := len(c.Validators) - c.LastChangeViewPayloads = make([]payload.ConsensusPayload, n) + c.LastChangeViewPayloads = make([]payload.ConsensusPayload[H, A], n) if c.LastSeenMessage == nil { c.LastSeenMessage = make([]*timer.HV, n) @@ -227,13 +226,13 @@ func (c *Context) reset(view byte, ts uint64) { c.header = nil n := len(c.Validators) - c.ChangeViewPayloads = make([]payload.ConsensusPayload, n) + c.ChangeViewPayloads = make([]payload.ConsensusPayload[H, A], n) if view == 0 { - c.CommitPayloads = make([]payload.ConsensusPayload, n) + c.CommitPayloads = make([]payload.ConsensusPayload[H, A], n) } - c.PreparationPayloads = make([]payload.ConsensusPayload, n) + c.PreparationPayloads = make([]payload.ConsensusPayload[H, A], n) - c.Transactions = make(map[util.Uint256]block.Transaction) + c.Transactions = make(map[H]block.Transaction[H]) c.TransactionHashes = nil c.MissingTransactions = nil c.PrimaryIndex = c.GetPrimaryIndex(view) @@ -248,7 +247,7 @@ func (c *Context) reset(view byte, ts uint64) { } // Fill initializes consensus when node is a speaker. -func (c *Context) Fill() { +func (c *Context[H, A]) Fill() { b := make([]byte, 8) _, err := rand.Read(b) if err != nil { @@ -257,7 +256,7 @@ func (c *Context) Fill() { txx := c.Config.GetVerified() c.Nonce = binary.LittleEndian.Uint64(b) - c.TransactionHashes = make([]util.Uint256, len(txx)) + c.TransactionHashes = make([]H, len(txx)) for i := range txx { h := txx[i].Hash() @@ -276,18 +275,18 @@ func (c *Context) Fill() { // getTimestamp returns nanoseconds-precision timestamp using // current context config. -func (c *Context) getTimestamp() uint64 { +func (c *Context[H, A]) 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) CreateBlock() block.Block { +func (c *Context[H, A]) CreateBlock() block.Block[H, A] { if c.block == nil { if c.block = c.MakeHeader(); c.block == nil { return nil } - txx := make([]block.Transaction, len(c.TransactionHashes)) + txx := make([]block.Transaction[H], len(c.TransactionHashes)) for i, h := range c.TransactionHashes { txx[i] = c.Transactions[h] @@ -301,7 +300,7 @@ func (c *Context) CreateBlock() block.Block { // MakeHeader returns half-filled block for the current epoch. // All hashable fields will be filled. -func (c *Context) MakeHeader() block.Block { +func (c *Context[H, A]) MakeHeader() block.Block[H, A] { if c.header == nil { if !c.RequestSentOrReceived() { return nil @@ -313,7 +312,7 @@ func (c *Context) MakeHeader() block.Block { } // NewBlockFromContext returns new block filled with given contexet. -func NewBlockFromContext(ctx *Context) block.Block { +func NewBlockFromContext(ctx *Context[crypto.Uint256, crypto.Uint160]) block.Block[crypto.Uint256, crypto.Uint160] { if ctx.TransactionHashes == nil { return nil } @@ -323,6 +322,6 @@ func NewBlockFromContext(ctx *Context) block.Block { // hasAllTransactions returns true iff all transactions were received // for the proposed block. -func (c *Context) hasAllTransactions() bool { +func (c *Context[H, A]) hasAllTransactions() bool { return len(c.TransactionHashes) == len(c.Transactions) } diff --git a/crypto/crypto.go b/crypto/crypto.go index 839813e8..3c1d5021 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -2,6 +2,7 @@ package crypto import ( "encoding" + "fmt" "io" ) @@ -20,6 +21,27 @@ type ( // Sign returns msg's signature and error on failure. Sign(msg []byte) (sig []byte, err error) } + + // Hash is a generic hash interface used by dbft for payloads, blocks and + // transactions identification. It is recommended to implement this interface + // using hash functions with low hash collision probability. The following + // requirements must be met: + // 1. Hashes of two equal payloads/blocks/transactions are equal. + // 2. Hashes of two different payloads/blocks/transactions are different. + Hash interface { + comparable + fmt.Stringer + } + + // Address is a generic address interface used by dbft for operations related + // to consensus address. It is recommended to implement this interface + // using hash functions with low hash collision probability. The following + // requirements must be met: + // 1. Addresses of two equal sets of consensus members are equal. + // 2. Addresses of two different sets of consensus members are different. + Address interface { + comparable + } ) type suiteType byte diff --git a/crypto/hash.go b/crypto/hash.go index 1ea9bf6b..e99a29a6 100644 --- a/crypto/hash.go +++ b/crypto/hash.go @@ -2,13 +2,33 @@ package crypto import ( "crypto/sha256" + "encoding/hex" - "github.com/nspcc-dev/neo-go/pkg/util" "golang.org/x/crypto/ripemd160" //nolint:staticcheck // SA1019: package golang.org/x/crypto/ripemd160 is deprecated ) +const ( + Uint256Size = 32 + Uint160Size = 20 +) + +type ( + Uint256 [Uint256Size]byte + Uint160 [Uint160Size]byte +) + +// String implements fmt.Stringer interface. +func (h Uint256) String() string { + return hex.EncodeToString(h[:]) +} + +// String implements fmt.Stringer interface. +func (h Uint160) String() string { + return hex.EncodeToString(h[:]) +} + // Hash256 returns double sha-256 of data. -func Hash256(data []byte) util.Uint256 { +func Hash256(data []byte) Uint256 { h1 := sha256.Sum256(data) h2 := sha256.Sum256(h1[:]) @@ -16,11 +36,13 @@ func Hash256(data []byte) util.Uint256 { } // Hash160 returns ripemd160 from sha256 of data. -func Hash160(data []byte) (h util.Uint160) { +func Hash160(data []byte) Uint160 { h1 := sha256.Sum256(data) rp := ripemd160.New() _, _ = rp.Write(h1[:]) + + var h Uint160 copy(h[:], rp.Sum(nil)) - return + return h } diff --git a/crypto/hash_test.go b/crypto/hash_test.go index 1a96a9d4..33b96d74 100644 --- a/crypto/hash_test.go +++ b/crypto/hash_test.go @@ -4,13 +4,12 @@ import ( "encoding/hex" "testing" - "github.com/nspcc-dev/neo-go/pkg/util" "github.com/stretchr/testify/require" ) var hash256tc = []struct { data []byte - hash util.Uint256 + hash Uint256 }{ {[]byte{}, parse256("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456")}, {[]byte{0, 1, 2, 3}, parse256("f7a355c00c89a08c80636bed35556a210b51786f6803a494f28fc5ba05959fc2")}, @@ -18,7 +17,7 @@ var hash256tc = []struct { var hash160tc = []struct { data []byte - hash util.Uint160 + hash Uint160 }{ {[]byte{}, parse160("b472a266d0bd89c13706a4132ccfb16f7c3b9fcb")}, {[]byte{0, 1, 2, 3}, parse160("3c3fa3d4adcaf8f52d5b1843975e122548269937")}, @@ -36,12 +35,12 @@ func TestHash160(t *testing.T) { } } -func parse256(s string) (h util.Uint256) { +func parse256(s string) (h Uint256) { parseHex(h[:], s) return } -func parse160(s string) (h util.Uint160) { +func parse160(s string) (h Uint160) { parseHex(h[:], s) return } diff --git a/dbft.go b/dbft.go index 7f061335..11f366d7 100644 --- a/dbft.go +++ b/dbft.go @@ -5,39 +5,38 @@ import ( "time" "github.com/nspcc-dev/dbft/block" + "github.com/nspcc-dev/dbft/crypto" "github.com/nspcc-dev/dbft/payload" "github.com/nspcc-dev/dbft/timer" - "github.com/nspcc-dev/neo-go/pkg/util" "go.uber.org/zap" ) type ( // DBFT is a wrapper over Context containing service configuration and // some other parameters not directly related to dBFT's state machine. - DBFT struct { - Context - Config + DBFT[H crypto.Hash, A crypto.Address] struct { + Context[H, A] + Config[H, A] *sync.Mutex - cache cache + cache cache[H, A] recovering bool } - // Service is an interface for dBFT consensus. - Service interface { + Service[H crypto.Hash, A crypto.Address] interface { Start(uint64) - OnTransaction(block.Transaction) - OnReceive(payload.ConsensusPayload) + OnTransaction(block.Transaction[H]) + OnReceive(payload.ConsensusPayload[H, A]) OnTimeout(timer.HV) } ) -var _ Service = (*DBFT)(nil) - -// New returns new DBFT instance with provided options -// and nil if some of the options are missing or invalid. -func New(options ...Option) *DBFT { - cfg := defaultConfig() +// New returns new DBFT instance with specified H and A generic parameters +// using provided options or nil 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 crypto.Hash, A crypto.Address](options ...func(config *Config[H, A])) *DBFT[H, A] { + cfg := defaultConfig[H, A]() for _, option := range options { option(cfg) @@ -47,10 +46,10 @@ func New(options ...Option) *DBFT { return nil } - d := &DBFT{ + d := &DBFT[H, A]{ Mutex: new(sync.Mutex), Config: *cfg, - Context: Context{ + Context: Context[H, A]{ Config: cfg, }, } @@ -58,7 +57,7 @@ func New(options ...Option) *DBFT { return d } -func (d *DBFT) addTransaction(tx block.Transaction) { +func (d *DBFT[H, A]) addTransaction(tx block.Transaction[H]) { d.Transactions[tx.Hash()] = tx if d.hasAllTransactions() { if d.IsPrimary() || d.Context.WatchOnly() { @@ -77,14 +76,14 @@ func (d *DBFT) addTransaction(tx block.Transaction) { // Start initializes dBFT instance and starts protocol if node is primary. It // accepts a timestamp of the previous block. -func (d *DBFT) Start(ts uint64) { - d.cache = newCache() +func (d *DBFT[H, A]) Start(ts uint64) { + d.cache = newCache[H, A]() d.InitializeConsensus(0, ts) d.start() } // InitializeConsensus initializes dBFT instance. -func (d *DBFT) InitializeConsensus(view byte, ts uint64) { +func (d *DBFT[H, A]) InitializeConsensus(view byte, ts uint64) { d.reset(view, ts) var role string @@ -137,7 +136,7 @@ func (d *DBFT) InitializeConsensus(view byte, ts uint64) { } // OnTransaction notifies service about receiving new transaction. -func (d *DBFT) OnTransaction(tx block.Transaction) { +func (d *DBFT[H, A]) OnTransaction(tx block.Transaction[H]) { // d.Logger.Debug("OnTransaction", // zap.Bool("backup", d.IsBackup()), // zap.Bool("not_accepting", d.NotAcceptingPayloadsDueToViewChanging()), @@ -169,7 +168,7 @@ func (d *DBFT) OnTransaction(tx block.Transaction) { } // OnTimeout advances state machine as if timeout was fired. -func (d *DBFT) OnTimeout(hv timer.HV) { +func (d *DBFT[H, A]) OnTimeout(hv timer.HV) { if d.Context.WatchOnly() || d.BlockSent() { return } @@ -200,7 +199,7 @@ func (d *DBFT) OnTimeout(hv timer.HV) { } // OnReceive advances state machine in accordance with msg. -func (d *DBFT) OnReceive(msg payload.ConsensusPayload) { +func (d *DBFT[H, A]) OnReceive(msg payload.ConsensusPayload[H, A]) { if int(msg.ValidatorIndex()) >= len(d.Validators) { d.Logger.Error("too big validator index", zap.Uint16("from", msg.ValidatorIndex())) return @@ -269,7 +268,7 @@ func (d *DBFT) OnReceive(msg payload.ConsensusPayload) { // start performs initial operations and returns messages to be sent. // It must be called after every height or view increment. -func (d *DBFT) start() { +func (d *DBFT[H, A]) start() { if !d.IsPrimary() { if msgs := d.cache.getHeight(d.BlockIndex); msgs != nil { for _, m := range msgs.prepare { @@ -291,7 +290,7 @@ func (d *DBFT) start() { d.sendPrepareRequest() } -func (d *DBFT) onPrepareRequest(msg payload.ConsensusPayload) { +func (d *DBFT[H, A]) onPrepareRequest(msg payload.ConsensusPayload[H, A]) { // ignore prepareRequest if we had already received it or // are in process of changing view if d.RequestSentOrReceived() { //|| (d.ViewChanging() && !d.MoreThanFNodesCommittedOrLost()) { @@ -343,8 +342,8 @@ func (d *DBFT) onPrepareRequest(msg payload.ConsensusPayload) { d.checkPrepare() } -func (d *DBFT) processMissingTx() { - missing := make([]util.Uint256, 0, len(d.TransactionHashes)/2) +func (d *DBFT[H, A]) processMissingTx() { + missing := make([]H, 0, len(d.TransactionHashes)/2) for _, h := range d.TransactionHashes { if _, ok := d.Transactions[h]; ok { @@ -369,8 +368,8 @@ func (d *DBFT) 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) createAndCheckBlock() bool { - txx := make([]block.Transaction, 0, len(d.TransactionHashes)) +func (d *DBFT[H, A]) createAndCheckBlock() bool { + txx := make([]block.Transaction[H], 0, len(d.TransactionHashes)) for _, h := range d.TransactionHashes { txx = append(txx, d.Transactions[h]) } @@ -387,7 +386,7 @@ func (d *DBFT) createAndCheckBlock() bool { return true } -func (d *DBFT) updateExistingPayloads(msg payload.ConsensusPayload) { +func (d *DBFT[H, A]) updateExistingPayloads(msg payload.ConsensusPayload[H, A]) { for i, m := range d.PreparationPayloads { if m != nil && m.Type() == payload.PrepareResponseType { resp := m.GetPrepareResponse() @@ -410,7 +409,7 @@ func (d *DBFT) updateExistingPayloads(msg payload.ConsensusPayload) { } } -func (d *DBFT) onPrepareResponse(msg payload.ConsensusPayload) { +func (d *DBFT[H, A]) onPrepareResponse(msg payload.ConsensusPayload[H, A]) { if d.ViewNumber != msg.ViewNumber() { d.Logger.Debug("ignoring wrong view number", zap.Uint("view", uint(msg.ViewNumber()))) return @@ -462,7 +461,7 @@ func (d *DBFT) onPrepareResponse(msg payload.ConsensusPayload) { } } -func (d *DBFT) onChangeView(msg payload.ConsensusPayload) { +func (d *DBFT[H, A]) onChangeView(msg payload.ConsensusPayload[H, A]) { p := msg.GetChangeView() if p.NewViewNumber() <= d.ViewNumber { @@ -493,16 +492,16 @@ func (d *DBFT) onChangeView(msg payload.ConsensusPayload) { d.checkChangeView(p.NewViewNumber()) } -func (d *DBFT) onCommit(msg payload.ConsensusPayload) { +func (d *DBFT[H, A]) onCommit(msg payload.ConsensusPayload[H, A]) { existing := d.CommitPayloads[msg.ValidatorIndex()] if existing != nil { - if !existing.Hash().Equals(msg.Hash()) { + if existing.Hash() != msg.Hash() { d.Logger.Warn("rejecting commit due to existing", zap.Uint("validator", uint(msg.ValidatorIndex())), zap.Uint("existing view", uint(existing.ViewNumber())), zap.Uint("view", uint(msg.ViewNumber())), - zap.String("existing hash", existing.Hash().StringLE()), - zap.String("hash", msg.Hash().StringLE()), + zap.Stringer("existing hash", existing.Hash()), + zap.Stringer("hash", msg.Hash()), ) } return @@ -535,7 +534,7 @@ func (d *DBFT) onCommit(msg payload.ConsensusPayload) { d.CommitPayloads[msg.ValidatorIndex()] = msg } -func (d *DBFT) onRecoveryRequest(msg payload.ConsensusPayload) { +func (d *DBFT[H, A]) onRecoveryRequest(msg payload.ConsensusPayload[H, A]) { if !d.CommitSent() { // Limit recoveries to be sent from no more than F nodes // TODO replace loop with a single if @@ -557,7 +556,7 @@ func (d *DBFT) onRecoveryRequest(msg payload.ConsensusPayload) { d.sendRecoveryMessage() } -func (d *DBFT) onRecoveryMessage(msg payload.ConsensusPayload) { +func (d *DBFT[H, A]) onRecoveryMessage(msg payload.ConsensusPayload[H, A]) { d.Logger.Debug("recovery message received", zap.Any("dump", msg)) var ( @@ -617,7 +616,7 @@ func (d *DBFT) onRecoveryMessage(msg payload.ConsensusPayload) { } } -func (d *DBFT) changeTimer(delay time.Duration) { +func (d *DBFT[H, A]) changeTimer(delay time.Duration) { d.Logger.Debug("reset timer", zap.Uint32("h", d.BlockIndex), zap.Int("v", int(d.ViewNumber)), @@ -625,7 +624,7 @@ func (d *DBFT) changeTimer(delay time.Duration) { d.Timer.Reset(timer.HV{Height: d.BlockIndex, View: d.ViewNumber}, delay) } -func (d *DBFT) extendTimer(count time.Duration) { +func (d *DBFT[H, A]) extendTimer(count time.Duration) { if !d.CommitSent() && !d.ViewChanging() { d.Timer.Extend(count * d.SecondsPerBlock / time.Duration(d.M())) } diff --git a/dbft_test.go b/dbft_test.go index 9ec9eddf..e2c1b42a 100644 --- a/dbft_test.go +++ b/dbft_test.go @@ -10,12 +10,11 @@ import ( "github.com/nspcc-dev/dbft/crypto" "github.com/nspcc-dev/dbft/payload" "github.com/nspcc-dev/dbft/timer" - "github.com/nspcc-dev/neo-go/pkg/util" "github.com/stretchr/testify/require" "go.uber.org/zap" ) -type Payload = payload.ConsensusPayload +type Payload = payload.ConsensusPayload[crypto.Uint256, crypto.Uint160] type testState struct { myIndex int @@ -24,16 +23,16 @@ type testState struct { pubs []crypto.PublicKey ch []Payload currHeight uint32 - currHash util.Uint256 + currHash crypto.Uint256 pool *testPool - blocks []block.Block - verify func(b block.Block) bool + blocks []block.Block[crypto.Uint256, crypto.Uint160] + verify func(b block.Block[crypto.Uint256, crypto.Uint160]) bool } type ( testTx uint64 testPool struct { - storage map[util.Uint256]testTx + storage map[crypto.Uint256]testTx } ) @@ -44,7 +43,7 @@ func TestDBFT_OnStartPrimarySendPrepareRequest(t *testing.T) { t.Run("backup sends nothing on start", func(t *testing.T) { s.currHeight = 0 - service := New(s.getOptions()...) + service := New[crypto.Uint256, crypto.Uint160](s.getOptions()...) service.Start(0) require.Nil(t, s.tryRecv()) @@ -52,7 +51,7 @@ func TestDBFT_OnStartPrimarySendPrepareRequest(t *testing.T) { t.Run("primary send PrepareRequest on start", func(t *testing.T) { s.currHeight = 1 - service := New(s.getOptions()...) + service := New[crypto.Uint256, crypto.Uint160](s.getOptions()...) service.Start(0) p := s.tryRecv() @@ -91,7 +90,7 @@ func TestDBFT_SingleNode(t *testing.T) { s := newTestState(0, 1) s.currHeight = 2 - service := New(s.getOptions()...) + service := New[crypto.Uint256, crypto.Uint160](s.getOptions()...) service.Start(0) p := s.tryRecv() @@ -117,7 +116,7 @@ func TestDBFT_SingleNode(t *testing.T) { func TestDBFT_OnReceiveRequestSendResponse(t *testing.T) { s := newTestState(2, 7) - s.verify = func(b block.Block) bool { + s.verify = func(b block.Block[crypto.Uint256, crypto.Uint160]) bool { for _, tx := range b.Transactions() { if tx.(testTx)%10 == 0 { return false @@ -129,7 +128,7 @@ func TestDBFT_OnReceiveRequestSendResponse(t *testing.T) { t.Run("receive request from primary", func(t *testing.T) { s.currHeight = 4 - service := New(s.getOptions()...) + service := New[crypto.Uint256, crypto.Uint160](s.getOptions()...) txs := []testTx{1} s.pool.Add(txs[0]) @@ -161,7 +160,7 @@ func TestDBFT_OnReceiveRequestSendResponse(t *testing.T) { t.Run("change view on invalid tx", func(t *testing.T) { s.currHeight = 4 - service := New(s.getOptions()...) + service := New[crypto.Uint256, crypto.Uint160](s.getOptions()...) txs := []testTx{10} service.Start(0) @@ -189,7 +188,7 @@ func TestDBFT_OnReceiveRequestSendResponse(t *testing.T) { t.Run("receive invalid prepare request", func(t *testing.T) { s.currHeight = 4 - service := New(s.getOptions()...) + service := New[crypto.Uint256, crypto.Uint160](s.getOptions()...) txs := []testTx{1, 2} s.pool.Add(txs[0]) @@ -238,7 +237,7 @@ func TestDBFT_CommitOnTransaction(t *testing.T) { s := newTestState(0, 4) s.currHeight = 1 - srv := New(s.getOptions()...) + srv := New[crypto.Uint256, crypto.Uint160](s.getOptions()...) srv.Start(0) require.Nil(t, s.tryRecv()) @@ -258,7 +257,7 @@ func TestDBFT_CommitOnTransaction(t *testing.T) { privs: s.privs, } s1.pool.Add(tx) - srv1 := New(s1.getOptions()...) + srv1 := New[crypto.Uint256, crypto.Uint160](s1.getOptions()...) srv1.Start(0) srv1.OnReceive(req) srv1.OnReceive(s1.getPrepareResponse(1, req.Hash())) @@ -280,7 +279,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 := New(s.getOptions()...) + service := New[crypto.Uint256, crypto.Uint160](s.getOptions()...) service.Start(0) req := s.tryRecv() @@ -340,7 +339,7 @@ func TestDBFT_OnReceiveRecoveryRequest(t *testing.T) { s := newTestState(2, 4) t.Run("send recovery message", func(t *testing.T) { s.currHeight = 1 - service := New(s.getOptions()...) + service := New[crypto.Uint256, crypto.Uint160](s.getOptions()...) service.Start(0) req := s.tryRecv() @@ -362,7 +361,7 @@ func TestDBFT_OnReceiveRecoveryRequest(t *testing.T) { require.Equal(t, payload.RecoveryMessageType, rm.Type()) other := s.copyWithIndex(3) - srv2 := New(other.getOptions()...) + srv2 := New[crypto.Uint256, crypto.Uint160](other.getOptions()...) srv2.Start(0) srv2.OnReceive(rm) @@ -385,7 +384,7 @@ func TestDBFT_OnReceiveChangeView(t *testing.T) { s := newTestState(2, 4) t.Run("change view correctly", func(t *testing.T) { s.currHeight = 6 - service := New(s.getOptions()...) + service := New[crypto.Uint256, crypto.Uint160](s.getOptions()...) service.Start(0) resp := s.getChangeView(1, 1) @@ -412,31 +411,94 @@ func TestDBFT_OnReceiveChangeView(t *testing.T) { func TestDBFT_Invalid(t *testing.T) { t.Run("without keys", func(t *testing.T) { - require.Nil(t, New()) + require.Nil(t, New[crypto.Uint256, crypto.Uint160]()) }) priv, pub := crypto.Generate(rand.Reader) require.NotNil(t, priv) require.NotNil(t, pub) - opts := []Option{WithKeyPair(priv, pub)} + opts := []func(*Config[crypto.Uint256, crypto.Uint160]){WithKeyPair[crypto.Uint256, crypto.Uint160](priv, pub)} t.Run("without CurrentHeight", func(t *testing.T) { require.Nil(t, New(opts...)) }) - opts = append(opts, WithCurrentHeight(func() uint32 { return 0 })) + opts = append(opts, WithCurrentHeight[crypto.Uint256, crypto.Uint160](func() uint32 { return 0 })) t.Run("without CurrentBlockHash", func(t *testing.T) { require.Nil(t, New(opts...)) }) - opts = append(opts, WithCurrentBlockHash(func() util.Uint256 { return util.Uint256{} })) + opts = append(opts, WithCurrentBlockHash[crypto.Uint256, crypto.Uint160](func() crypto.Uint256 { return crypto.Uint256{} })) t.Run("without GetValidators", func(t *testing.T) { require.Nil(t, New(opts...)) }) - opts = append(opts, WithGetValidators(func(...block.Transaction) []crypto.PublicKey { + opts = append(opts, WithGetValidators[crypto.Uint256, crypto.Uint160](func(...block.Transaction[crypto.Uint256]) []crypto.PublicKey { return []crypto.PublicKey{pub} })) + t.Run("without NewBlockFromContext", func(t *testing.T) { + require.Nil(t, New(opts...)) + }) + + opts = append(opts, WithNewBlockFromContext[crypto.Uint256, crypto.Uint160](func(_ *Context[crypto.Uint256, crypto.Uint160]) block.Block[crypto.Uint256, crypto.Uint160] { + return nil + })) + t.Run("without GetConsensusAddress", func(t *testing.T) { + require.Nil(t, New(opts...)) + }) + + opts = append(opts, WithGetConsensusAddress[crypto.Uint256, crypto.Uint160](func(_ ...crypto.PublicKey) crypto.Uint160 { + return crypto.Uint160{} + })) + t.Run("without NewConsensusPayload", func(t *testing.T) { + require.Nil(t, New(opts...)) + }) + + opts = append(opts, WithNewConsensusPayload[crypto.Uint256, crypto.Uint160](func(_ *Context[crypto.Uint256, crypto.Uint160], _ payload.MessageType, _ any) payload.ConsensusPayload[crypto.Uint256, crypto.Uint160] { + return nil + })) + t.Run("without NewPrepareRequest", func(t *testing.T) { + require.Nil(t, New(opts...)) + }) + + opts = append(opts, WithNewPrepareRequest[crypto.Uint256, crypto.Uint160](func() payload.PrepareRequest[crypto.Uint256, crypto.Uint160] { + return nil + })) + t.Run("without NewPrepareResponse", func(t *testing.T) { + require.Nil(t, New(opts...)) + }) + + opts = append(opts, WithNewPrepareResponse[crypto.Uint256, crypto.Uint160](func() payload.PrepareResponse[crypto.Uint256] { + return nil + })) + t.Run("without NewChangeView", func(t *testing.T) { + require.Nil(t, New(opts...)) + }) + + opts = append(opts, WithNewChangeView[crypto.Uint256, crypto.Uint160](func() payload.ChangeView { + return nil + })) + t.Run("without NewCommit", func(t *testing.T) { + require.Nil(t, New(opts...)) + }) + + opts = append(opts, WithNewCommit[crypto.Uint256, crypto.Uint160](func() payload.Commit { + return nil + })) + t.Run("without NewRecoveryRequest", func(t *testing.T) { + require.Nil(t, New(opts...)) + }) + + opts = append(opts, WithNewRecoveryRequest[crypto.Uint256, crypto.Uint160](func() payload.RecoveryRequest { + return nil + })) + t.Run("without NewRecoveryMessage", func(t *testing.T) { + require.Nil(t, New(opts...)) + }) + + opts = append(opts, WithNewRecoveryMessage[crypto.Uint256, crypto.Uint160](func() payload.RecoveryMessage[crypto.Uint256, crypto.Uint160] { + return nil + })) t.Run("with all defaults", func(t *testing.T) { d := New(opts...) require.NotNil(t, d) @@ -467,19 +529,19 @@ func TestDBFT_Invalid(t *testing.T) { func TestDBFT_FourGoodNodesDeadlock(t *testing.T) { r0 := newTestState(0, 4) r0.currHeight = 4 - s0 := New(r0.getOptions()...) + s0 := New[crypto.Uint256, crypto.Uint160](r0.getOptions()...) s0.Start(0) r1 := r0.copyWithIndex(1) - s1 := New(r1.getOptions()...) + s1 := New[crypto.Uint256, crypto.Uint160](r1.getOptions()...) s1.Start(0) r2 := r0.copyWithIndex(2) - s2 := New(r2.getOptions()...) + s2 := New[crypto.Uint256, crypto.Uint160](r2.getOptions()...) s2.Start(0) r3 := r0.copyWithIndex(3) - s3 := New(r3.getOptions()...) + s3 := New[crypto.Uint256, crypto.Uint160](r3.getOptions()...) s3.Start(0) // Step 1. The primary (at view 0) replica 1 sends the PrepareRequest message. @@ -698,7 +760,7 @@ func (s testState) getCommit(from uint16, sign []byte) Payload { return p } -func (s testState) getPrepareResponse(from uint16, phash util.Uint256) Payload { +func (s testState) getPrepareResponse(from uint16, phash crypto.Uint256) Payload { resp := payload.NewPrepareResponse() resp.SetPreparationHash(phash) @@ -709,7 +771,7 @@ func (s testState) getPrepareResponse(from uint16, phash util.Uint256) Payload { return p } -func (s testState) getPrepareRequest(from uint16, hashes ...util.Uint256) Payload { +func (s testState) getPrepareRequest(from uint16, hashes ...crypto.Uint256) Payload { req := payload.NewPrepareRequest() req.SetTransactionHashes(hashes) req.SetNextConsensus(s.nextConsensus()) @@ -752,7 +814,7 @@ func (s *testState) tryRecv() Payload { return p } -func (s *testState) nextBlock() block.Block { +func (s *testState) nextBlock() block.Block[crypto.Uint256, crypto.Uint160] { if len(s.blocks) == 0 { return nil } @@ -775,41 +837,41 @@ func (s testState) copyWithIndex(myIndex int) *testState { } } -func (s testState) nextConsensus(...crypto.PublicKey) util.Uint160 { - return util.Uint160{1} +func (s testState) nextConsensus(...crypto.PublicKey) crypto.Uint160 { + return crypto.Uint160{1} } -func (s *testState) getOptions() []Option { - opts := []Option{ - WithCurrentHeight(func() uint32 { return s.currHeight }), - WithCurrentBlockHash(func() util.Uint256 { return s.currHash }), - WithGetValidators(func(...block.Transaction) []crypto.PublicKey { return s.pubs }), - WithKeyPair(s.privs[s.myIndex], s.pubs[s.myIndex]), - WithBroadcast(func(p Payload) { s.ch = append(s.ch, p) }), - WithGetTx(s.pool.Get), - WithProcessBlock(func(b block.Block) { s.blocks = append(s.blocks, b) }), - WithGetConsensusAddress(s.nextConsensus), - WithWatchOnly(func() bool { return false }), - WithGetBlock(func(util.Uint256) block.Block { return nil }), - WithTimer(timer.New()), - WithLogger(zap.NewNop()), - WithNewBlockFromContext(NewBlockFromContext), - WithSecondsPerBlock(time.Second * 10), - WithRequestTx(func(...util.Uint256) {}), - WithGetVerified(func() []block.Transaction { return []block.Transaction{} }), - - WithNewConsensusPayload(defaultNewConsensusPayload), - WithNewPrepareRequest(payload.NewPrepareRequest), - WithNewPrepareResponse(payload.NewPrepareResponse), - WithNewChangeView(payload.NewChangeView), - WithNewCommit(payload.NewCommit), - WithNewRecoveryRequest(payload.NewRecoveryRequest), - WithNewRecoveryMessage(payload.NewRecoveryMessage), +func (s *testState) getOptions() []func(*Config[crypto.Uint256, crypto.Uint160]) { + opts := []func(*Config[crypto.Uint256, crypto.Uint160]){ + WithCurrentHeight[crypto.Uint256, crypto.Uint160](func() uint32 { return s.currHeight }), + WithCurrentBlockHash[crypto.Uint256, crypto.Uint160](func() crypto.Uint256 { return s.currHash }), + WithGetValidators[crypto.Uint256, crypto.Uint160](func(...block.Transaction[crypto.Uint256]) []crypto.PublicKey { return s.pubs }), + WithKeyPair[crypto.Uint256, crypto.Uint160](s.privs[s.myIndex], s.pubs[s.myIndex]), + WithBroadcast[crypto.Uint256, crypto.Uint160](func(p Payload) { s.ch = append(s.ch, p) }), + WithGetTx[crypto.Uint256, crypto.Uint160](s.pool.Get), + WithProcessBlock[crypto.Uint256, crypto.Uint160](func(b block.Block[crypto.Uint256, crypto.Uint160]) { s.blocks = append(s.blocks, b) }), + WithGetConsensusAddress[crypto.Uint256, crypto.Uint160](s.nextConsensus), + WithWatchOnly[crypto.Uint256, crypto.Uint160](func() bool { return false }), + WithGetBlock[crypto.Uint256, crypto.Uint160](func(crypto.Uint256) block.Block[crypto.Uint256, crypto.Uint160] { return nil }), + WithTimer[crypto.Uint256, crypto.Uint160](timer.New()), + WithLogger[crypto.Uint256, crypto.Uint160](zap.NewNop()), + WithNewBlockFromContext[crypto.Uint256, crypto.Uint160](NewBlockFromContext), + WithSecondsPerBlock[crypto.Uint256, crypto.Uint160](time.Second * 10), + WithRequestTx[crypto.Uint256, crypto.Uint160](func(...crypto.Uint256) {}), + WithGetVerified[crypto.Uint256, crypto.Uint160](func() []block.Transaction[crypto.Uint256] { return []block.Transaction[crypto.Uint256]{} }), + + WithNewConsensusPayload[crypto.Uint256, crypto.Uint160](newConsensusPayload), + WithNewPrepareRequest[crypto.Uint256, crypto.Uint160](payload.NewPrepareRequest), + WithNewPrepareResponse[crypto.Uint256, crypto.Uint160](payload.NewPrepareResponse), + WithNewChangeView[crypto.Uint256, crypto.Uint160](payload.NewChangeView), + WithNewCommit[crypto.Uint256, crypto.Uint160](payload.NewCommit), + WithNewRecoveryRequest[crypto.Uint256, crypto.Uint160](payload.NewRecoveryRequest), + WithNewRecoveryMessage[crypto.Uint256, crypto.Uint160](payload.NewRecoveryMessage), } verify := s.verify if verify == nil { - verify = func(block.Block) bool { return true } + verify = func(block.Block[crypto.Uint256, crypto.Uint160]) bool { return true } } opts = append(opts, WithVerifyBlock(verify)) @@ -818,12 +880,25 @@ func (s *testState) getOptions() []Option { cfg := zap.NewDevelopmentConfig() cfg.DisableStacktrace = true logger, _ := cfg.Build() - opts = append(opts, WithLogger(logger)) + opts = append(opts, WithLogger[crypto.Uint256, crypto.Uint160](logger)) } return opts } +// newConsensusPayload is a function for creating consensus payload of specific +// type. +func newConsensusPayload(c *Context[crypto.Uint256, crypto.Uint160], t payload.MessageType, msg any) payload.ConsensusPayload[crypto.Uint256, crypto.Uint160] { + cp := payload.NewConsensusPayload() + cp.SetHeight(c.BlockIndex) + cp.SetValidatorIndex(uint16(c.MyIndex)) + cp.SetViewNumber(c.ViewNumber) + cp.SetType(t) + cp.SetPayload(msg) + + return cp +} + func getTestValidators(n int) (privs []crypto.PrivateKey, pubs []crypto.PublicKey) { for i := 0; i < n; i++ { priv, pub := crypto.Generate(rand.Reader) @@ -834,14 +909,14 @@ func getTestValidators(n int) (privs []crypto.PrivateKey, pubs []crypto.PublicKe return } -func (tx testTx) Hash() (h util.Uint256) { +func (tx testTx) Hash() (h crypto.Uint256) { binary.LittleEndian.PutUint64(h[:], uint64(tx)) return } func newTestPool() *testPool { return &testPool{ - storage: make(map[util.Uint256]testTx), + storage: make(map[crypto.Uint256]testTx), } } @@ -849,7 +924,7 @@ func (p *testPool) Add(tx testTx) { p.storage[tx.Hash()] = tx } -func (p *testPool) Get(h util.Uint256) block.Transaction { +func (p *testPool) Get(h crypto.Uint256) block.Transaction[crypto.Uint256] { if tx, ok := p.storage[h]; ok { return tx } diff --git a/go.mod b/go.mod index 7c58cc85..4aa10a7a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/nspcc-dev/dbft go 1.19 require ( - github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09 github.com/nspcc-dev/neofs-crypto v0.4.0 github.com/pkg/errors v0.8.1 github.com/spaolacci/murmur3 v1.1.0 diff --git a/go.sum b/go.sum index 21ea6212..4d3d70af 100644 --- a/go.sum +++ b/go.sum @@ -1,156 +1,31 @@ -github.com/CityOfZion/neo-go v0.62.1-pre.0.20191114145240-e740fbe708f8/go.mod h1:MJCkWUBhi9pn/CrYO1Q3P687y2KeahrOPS9BD9LDGb0= -github.com/CityOfZion/neo-go v0.70.1-pre.0.20191209120015-fccb0085941e/go.mod h1:0enZl0az8xA6PVkwzEOwPWVJGqlt/GO4hA4kmQ5Xzig= -github.com/CityOfZion/neo-go v0.70.1-pre.0.20191212173117-32ac01130d4c/go.mod h1:JtlHfeqLywZLswKIKFnAp+yzezY4Dji9qlfQKB2OD/I= -github.com/CityOfZion/neo-go v0.71.1-pre.0.20200129171427-f773ec69fb84/go.mod h1:FLI526IrRWHmcsO+mHsCbj64pJZhwQFTLJZu+A4PGOA= -github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= -github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg= -github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-redis/redis v6.10.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= -github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+dBlPhLnuZhBV+DfPEdS2CHWWLp5JTScY3bw= -github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA51EGfb5GS/HOv7VdmngNRTssSeQ729dvGY= -github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk= -github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ= -github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09 h1:XPW8PIcnZzp9wFX99b0GyWhC8scBDkqYtcjCEl40HQ4= -github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg= -github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA= -github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4= github.com/nspcc-dev/neofs-crypto v0.4.0/go.mod h1:6XJ8kbXgOfevbI2WMruOtI+qUJXNwSGM/E9eClXxPHs= -github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE= github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= -github.com/yuin/gopher-lua v0.0.0-20191128022950-c6266f4fe8d7/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20180318012157-96caea41033d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/abiosoft/ishell.v2 v2.0.0/go.mod h1:sFp+cGtH6o4s1FtpVPTMcHq2yue+c4DGOVohJCPUzwY= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/helpers.go b/helpers.go index 510fa132..c0b0fcc3 100644 --- a/helpers.go +++ b/helpers.go @@ -1,39 +1,40 @@ package dbft import ( + "github.com/nspcc-dev/dbft/crypto" "github.com/nspcc-dev/dbft/payload" ) type ( // inbox is a structure storing messages from a single epoch. - inbox struct { - prepare map[uint16]payload.ConsensusPayload - chViews map[uint16]payload.ConsensusPayload - commit map[uint16]payload.ConsensusPayload + inbox[H crypto.Hash, A crypto.Address] struct { + prepare map[uint16]payload.ConsensusPayload[H, A] + chViews map[uint16]payload.ConsensusPayload[H, A] + commit map[uint16]payload.ConsensusPayload[H, A] } // cache is an auxiliary structure storing messages // from future epochs. - cache struct { - mail map[uint32]*inbox + cache[H crypto.Hash, A crypto.Address] struct { + mail map[uint32]*inbox[H, A] } ) -func newInbox() *inbox { - return &inbox{ - prepare: make(map[uint16]payload.ConsensusPayload), - chViews: make(map[uint16]payload.ConsensusPayload), - commit: make(map[uint16]payload.ConsensusPayload), +func newInbox[H crypto.Hash, A crypto.Address]() *inbox[H, A] { + return &inbox[H, A]{ + prepare: make(map[uint16]payload.ConsensusPayload[H, A]), + chViews: make(map[uint16]payload.ConsensusPayload[H, A]), + commit: make(map[uint16]payload.ConsensusPayload[H, A]), } } -func newCache() cache { - return cache{ - mail: make(map[uint32]*inbox), +func newCache[H crypto.Hash, A crypto.Address]() cache[H, A] { + return cache[H, A]{ + mail: make(map[uint32]*inbox[H, A]), } } -func (c *cache) getHeight(h uint32) *inbox { +func (c *cache[H, A]) getHeight(h uint32) *inbox[H, A] { if m, ok := c.mail[h]; ok { delete(c.mail, h) return m @@ -42,10 +43,10 @@ func (c *cache) getHeight(h uint32) *inbox { return nil } -func (c *cache) addMessage(m payload.ConsensusPayload) { +func (c *cache[H, A]) addMessage(m payload.ConsensusPayload[H, A]) { msgs, ok := c.mail[m.Height()] if !ok { - msgs = newInbox() + msgs = newInbox[H, A]() c.mail[m.Height()] = msgs } diff --git a/helpers_test.go b/helpers_test.go index add09d8f..10831ca2 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -3,13 +3,14 @@ package dbft import ( "testing" + "github.com/nspcc-dev/dbft/crypto" "github.com/stretchr/testify/require" "github.com/nspcc-dev/dbft/payload" ) func TestMessageCache(t *testing.T) { - c := newCache() + c := newCache[crypto.Uint256, crypto.Uint160]() p1 := payload.NewConsensusPayload() p1.SetHeight(3) diff --git a/merkle/merkle_tree.go b/merkle/merkle_tree.go index 0dbccef2..88bc8939 100644 --- a/merkle/merkle_tree.go +++ b/merkle/merkle_tree.go @@ -2,7 +2,6 @@ package merkle import ( "github.com/nspcc-dev/dbft/crypto" - "github.com/nspcc-dev/neo-go/pkg/util" ) type ( @@ -15,7 +14,7 @@ type ( // TreeNode represents inner node of a merkle tree. TreeNode struct { - Hash util.Uint256 + Hash crypto.Uint256 Parent *TreeNode Left *TreeNode Right *TreeNode @@ -23,7 +22,7 @@ type ( ) // NewMerkleTree returns new merkle tree built on hashes. -func NewMerkleTree(hashes ...util.Uint256) *Tree { +func NewMerkleTree(hashes ...crypto.Uint256) *Tree { if len(hashes) == 0 { return nil } diff --git a/merkle/merkle_tree_test.go b/merkle/merkle_tree_test.go index 1a3ca870..1fc1240c 100644 --- a/merkle/merkle_tree_test.go +++ b/merkle/merkle_tree_test.go @@ -5,7 +5,7 @@ import ( "encoding/hex" "testing" - "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/dbft/crypto" "github.com/stretchr/testify/require" ) @@ -15,7 +15,7 @@ func TestNewMerkleTree(t *testing.T) { }) t.Run("merkle tree on 1 leave", func(t *testing.T) { - h := util.Uint256{1, 2, 3, 4} + h := crypto.Uint256{1, 2, 3, 4} mt := NewMerkleTree(h) require.NotNil(t, mt) require.Equal(t, 1, mt.Depth) @@ -24,7 +24,7 @@ func TestNewMerkleTree(t *testing.T) { }) t.Run("predefined tree on 4 leaves", func(t *testing.T) { - hashes := make([]util.Uint256, 5) + hashes := make([]crypto.Uint256, 5) for i := 0; i < 5; i++ { hashes[i] = sha256.Sum256([]byte{byte(i)}) } @@ -40,7 +40,7 @@ func TestNewMerkleTree(t *testing.T) { } func TestTreeNode_IsLeaf(t *testing.T) { - hashes := []util.Uint256{{1}, {2}, {3}} + hashes := []crypto.Uint256{{1}, {2}, {3}} mt := NewMerkleTree(hashes...) require.NotNil(t, mt) diff --git a/payload/consensus_message.go b/payload/consensus_message.go index 0ebe8de7..65d1d192 100644 --- a/payload/consensus_message.go +++ b/payload/consensus_message.go @@ -5,6 +5,7 @@ import ( "encoding/gob" "fmt" + "github.com/nspcc-dev/dbft/crypto" "github.com/pkg/errors" ) @@ -18,7 +19,7 @@ type ( DecodeBinary(decoder *gob.Decoder) error } - consensusMessage interface { + consensusMessage[H crypto.Hash, A crypto.Address] interface { // ViewNumber returns view number when this message was originated. ViewNumber() byte // SetViewNumber sets view number. @@ -37,15 +38,15 @@ type ( // GetChangeView returns payload as if it was ChangeView. GetChangeView() ChangeView // GetPrepareRequest returns payload as if it was PrepareRequest. - GetPrepareRequest() PrepareRequest + GetPrepareRequest() PrepareRequest[H, A] // GetPrepareResponse returns payload as if it was PrepareResponse. - GetPrepareResponse() PrepareResponse + GetPrepareResponse() PrepareResponse[H] // GetCommit returns payload as if it was Commit. GetCommit() Commit // GetRecoveryRequest returns payload as if it was RecoveryRequest. GetRecoveryRequest() RecoveryRequest // GetRecoveryMessage returns payload as if it was RecoveryMessage. - GetRecoveryMessage() RecoveryMessage + GetRecoveryMessage() RecoveryMessage[H, A] } message struct { @@ -73,7 +74,7 @@ const ( RecoveryMessageType MessageType = 0x41 ) -var _ consensusMessage = (*message)(nil) +var _ consensusMessage[crypto.Uint256, crypto.Uint160] = (*message)(nil) // String implements fmt.Stringer interface. func (m MessageType) String() string { @@ -142,12 +143,18 @@ func (m *message) DecodeBinary(r *gob.Decoder) error { return m.payload.(Serializable).DecodeBinary(dec) } -func (m message) GetChangeView() ChangeView { return m.payload.(ChangeView) } -func (m message) GetPrepareRequest() PrepareRequest { return m.payload.(PrepareRequest) } -func (m message) GetPrepareResponse() PrepareResponse { return m.payload.(PrepareResponse) } +func (m message) GetChangeView() ChangeView { return m.payload.(ChangeView) } +func (m message) GetPrepareRequest() PrepareRequest[crypto.Uint256, crypto.Uint160] { + return m.payload.(PrepareRequest[crypto.Uint256, crypto.Uint160]) +} +func (m message) GetPrepareResponse() PrepareResponse[crypto.Uint256] { + return m.payload.(PrepareResponse[crypto.Uint256]) +} func (m message) GetCommit() Commit { return m.payload.(Commit) } func (m message) GetRecoveryRequest() RecoveryRequest { return m.payload.(RecoveryRequest) } -func (m message) GetRecoveryMessage() RecoveryMessage { return m.payload.(RecoveryMessage) } +func (m message) GetRecoveryMessage() RecoveryMessage[crypto.Uint256, crypto.Uint160] { + return m.payload.(RecoveryMessage[crypto.Uint256, crypto.Uint160]) +} // ViewNumber implements ConsensusMessage interface. func (m message) ViewNumber() byte { diff --git a/payload/constructors.go b/payload/constructors.go index 7156c135..696c3a64 100644 --- a/payload/constructors.go +++ b/payload/constructors.go @@ -1,17 +1,19 @@ package payload +import "github.com/nspcc-dev/dbft/crypto" + // NewConsensusPayload returns minimal ConsensusPayload implementation. -func NewConsensusPayload() ConsensusPayload { +func NewConsensusPayload() ConsensusPayload[crypto.Uint256, crypto.Uint160] { return &Payload{} } // NewPrepareRequest returns minimal prepareRequest implementation. -func NewPrepareRequest() PrepareRequest { +func NewPrepareRequest() PrepareRequest[crypto.Uint256, crypto.Uint160] { return new(prepareRequest) } // NewPrepareResponse returns minimal PrepareResponse implementation. -func NewPrepareResponse() PrepareResponse { +func NewPrepareResponse() PrepareResponse[crypto.Uint256] { return new(prepareResponse) } @@ -31,7 +33,7 @@ func NewRecoveryRequest() RecoveryRequest { } // NewRecoveryMessage returns minimal RecoveryMessage implementation. -func NewRecoveryMessage() RecoveryMessage { +func NewRecoveryMessage() RecoveryMessage[crypto.Uint256, crypto.Uint160] { return &recoveryMessage{ preparationPayloads: make([]preparationCompact, 0), commitPayloads: make([]commitCompact, 0), diff --git a/payload/message.go b/payload/message.go index d8ed52db..17e3a5ce 100644 --- a/payload/message.go +++ b/payload/message.go @@ -5,14 +5,13 @@ import ( "encoding/gob" "github.com/nspcc-dev/dbft/crypto" - "github.com/nspcc-dev/neo-go/pkg/util" ) type ( // ConsensusPayload is a generic payload type which is exchanged // between the nodes. - ConsensusPayload interface { - consensusMessage + ConsensusPayload[H crypto.Hash, A crypto.Address] interface { + consensusMessage[H, A] // ValidatorIndex returns index of validator from which // payload was originated from. @@ -25,7 +24,7 @@ type ( SetHeight(h uint32) // Hash returns 32-byte checksum of the payload. - Hash() util.Uint256 + Hash() H } // Payload represents minimal payload containing all necessary fields. @@ -34,24 +33,24 @@ type ( version uint32 validatorIndex uint16 - prevHash util.Uint256 + prevHash crypto.Uint256 height uint32 - hash *util.Uint256 + hash *crypto.Uint256 } // payloadAux is an auxiliary structure for Payload encoding. payloadAux struct { Version uint32 ValidatorIndex uint16 - PrevHash util.Uint256 + PrevHash crypto.Uint256 Height uint32 Data []byte } ) -var _ ConsensusPayload = (*Payload)(nil) +var _ ConsensusPayload[crypto.Uint256, crypto.Uint160] = (*Payload)(nil) // EncodeBinary implements Serializable interface. func (p Payload) EncodeBinary(w *gob.Encoder) error { @@ -104,7 +103,7 @@ func (p *Payload) UnmarshalUnsigned(data []byte) error { } // Hash implements ConsensusPayload interface. -func (p *Payload) Hash() util.Uint256 { +func (p *Payload) Hash() crypto.Uint256 { if p.hash != nil { return *p.hash } @@ -135,12 +134,12 @@ func (p *Payload) SetValidatorIndex(i uint16) { } // PrevHash implements ConsensusPayload interface. -func (p Payload) PrevHash() util.Uint256 { +func (p Payload) PrevHash() crypto.Uint256 { return p.prevHash } // SetPrevHash implements ConsensusPayload interface. -func (p *Payload) SetPrevHash(h util.Uint256) { +func (p *Payload) SetPrevHash(h crypto.Uint256) { p.prevHash = h } diff --git a/payload/message_test.go b/payload/message_test.go index 82d27746..24e1ef56 100644 --- a/payload/message_test.go +++ b/payload/message_test.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/nspcc-dev/dbft/crypto" - "github.com/nspcc-dev/neo-go/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -16,7 +15,7 @@ func TestPayload_EncodeDecode(t *testing.T) { m := NewConsensusPayload().(*Payload) m.SetValidatorIndex(10) m.SetHeight(77) - m.SetPrevHash(util.Uint256{1}) + m.SetPrevHash(crypto.Uint256{1}) m.SetVersion(8) m.SetViewNumber(3) @@ -25,7 +24,7 @@ func TestPayload_EncodeDecode(t *testing.T) { m.SetPayload(&prepareRequest{ nonce: 123, timestamp: 345, - transactionHashes: []util.Uint256{ + transactionHashes: []crypto.Uint256{ {1, 2, 3}, {5, 6, 7}, }, @@ -38,7 +37,7 @@ func TestPayload_EncodeDecode(t *testing.T) { t.Run("PrepareResponse", func(t *testing.T) { m.SetType(PrepareResponseType) m.SetPayload(&prepareResponse{ - preparationHash: util.Uint256{3}, + preparationHash: crypto.Uint256{3}, }) testEncodeDecode(t, m, new(Payload)) @@ -85,7 +84,7 @@ func TestPayload_EncodeDecode(t *testing.T) { prepareRequest: &prepareRequest{ nonce: 123, timestamp: 345, - transactionHashes: []util.Uint256{ + transactionHashes: []crypto.Uint256{ {1, 2, 3}, {5, 6, 7}, }, @@ -111,7 +110,7 @@ func TestRecoveryMessage_NoPayloads(t *testing.T) { m := NewConsensusPayload().(*Payload) m.SetValidatorIndex(0) m.SetHeight(77) - m.SetPrevHash(util.Uint256{1}) + m.SetPrevHash(crypto.Uint256{1}) m.SetVersion(8) m.SetViewNumber(3) m.SetPayload(&recoveryMessage{}) @@ -122,11 +121,11 @@ func TestRecoveryMessage_NoPayloads(t *testing.T) { rec := m.GetRecoveryMessage() require.NotNil(t, rec) - var p ConsensusPayload + var p ConsensusPayload[crypto.Uint256, crypto.Uint160] require.NotPanics(t, func() { p = rec.GetPrepareRequest(p, validators, 0) }) require.Nil(t, p) - var ps []ConsensusPayload + var ps []ConsensusPayload[crypto.Uint256, crypto.Uint160] require.NotPanics(t, func() { ps = rec.GetPrepareResponses(p, validators) }) require.Len(t, ps, 0) @@ -188,8 +187,8 @@ func TestPayload_Setters(t *testing.T) { t.Run("RecoveryMessage", func(t *testing.T) { r := NewRecoveryMessage() - r.SetPreparationHash(&util.Uint256{1, 2, 3}) - require.Equal(t, &util.Uint256{1, 2, 3}, r.PreparationHash()) + r.SetPreparationHash(&crypto.Uint256{1, 2, 3}) + require.Equal(t, &crypto.Uint256{1, 2, 3}, r.PreparationHash()) }) } diff --git a/payload/prepare_request.go b/payload/prepare_request.go index 8d6b31eb..e34e459f 100644 --- a/payload/prepare_request.go +++ b/payload/prepare_request.go @@ -3,11 +3,11 @@ package payload import ( "encoding/gob" - "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/dbft/crypto" ) // PrepareRequest represents dBFT PrepareRequest message. -type PrepareRequest interface { +type PrepareRequest[H crypto.Hash, A crypto.Address] interface { // Timestamp returns this message's timestamp. Timestamp() uint64 // SetTimestamp sets timestamp of this message. @@ -19,34 +19,34 @@ type PrepareRequest interface { SetNonce(nonce uint64) // TransactionHashes returns hashes of all transaction in a proposed block. - TransactionHashes() []util.Uint256 + TransactionHashes() []H // SetTransactionHashes sets transaction's hashes. - SetTransactionHashes(hs []util.Uint256) + SetTransactionHashes(hs []H) // NextConsensus returns hash which is based on which validators will // try to agree on a block in the current epoch. - NextConsensus() util.Uint160 + NextConsensus() A // SetNextConsensus sets next consensus field. - SetNextConsensus(nc util.Uint160) + SetNextConsensus(nc A) } type ( prepareRequest struct { - transactionHashes []util.Uint256 + transactionHashes []crypto.Uint256 nonce uint64 timestamp uint32 - nextConsensus util.Uint160 + nextConsensus crypto.Uint160 } // prepareRequestAux is an auxiliary structure for prepareRequest encoding. prepareRequestAux struct { - TransactionHashes []util.Uint256 + TransactionHashes []crypto.Uint256 Nonce uint64 Timestamp uint32 - NextConsensus util.Uint160 + NextConsensus crypto.Uint160 } ) -var _ PrepareRequest = (*prepareRequest)(nil) +var _ PrepareRequest[crypto.Uint256, crypto.Uint160] = (*prepareRequest)(nil) // EncodeBinary implements Serializable interface. func (p prepareRequest) EncodeBinary(w *gob.Encoder) error { @@ -93,21 +93,21 @@ func (p *prepareRequest) SetNonce(nonce uint64) { } // TransactionHashes implements PrepareRequest interface. -func (p prepareRequest) TransactionHashes() []util.Uint256 { +func (p prepareRequest) TransactionHashes() []crypto.Uint256 { return p.transactionHashes } // SetTransactionHashes implements PrepareRequest interface. -func (p *prepareRequest) SetTransactionHashes(hs []util.Uint256) { +func (p *prepareRequest) SetTransactionHashes(hs []crypto.Uint256) { p.transactionHashes = hs } // NextConsensus implements PrepareRequest interface. -func (p prepareRequest) NextConsensus() util.Uint160 { +func (p prepareRequest) NextConsensus() crypto.Uint160 { return p.nextConsensus } // SetNextConsensus implements PrepareRequest interface. -func (p *prepareRequest) SetNextConsensus(nc util.Uint160) { +func (p *prepareRequest) SetNextConsensus(nc crypto.Uint160) { p.nextConsensus = nc } diff --git a/payload/prepare_response.go b/payload/prepare_response.go index 0268d7df..42e32d44 100644 --- a/payload/prepare_response.go +++ b/payload/prepare_response.go @@ -3,29 +3,29 @@ package payload import ( "encoding/gob" - "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/dbft/crypto" ) // PrepareResponse represents dBFT PrepareResponse message. -type PrepareResponse interface { +type PrepareResponse[H crypto.Hash] interface { // PreparationHash returns the hash of PrepareRequest payload // for this epoch. - PreparationHash() util.Uint256 + PreparationHash() H // SetPreparationHash sets preparations hash. - SetPreparationHash(h util.Uint256) + SetPreparationHash(h H) } type ( prepareResponse struct { - preparationHash util.Uint256 + preparationHash crypto.Uint256 } // prepareResponseAux is an auxiliary structure for prepareResponse encoding. prepareResponseAux struct { - PreparationHash util.Uint256 + PreparationHash crypto.Uint256 } ) -var _ PrepareResponse = (*prepareResponse)(nil) +var _ PrepareResponse[crypto.Uint256] = (*prepareResponse)(nil) // EncodeBinary implements Serializable interface. func (p prepareResponse) EncodeBinary(w *gob.Encoder) error { @@ -46,11 +46,11 @@ func (p *prepareResponse) DecodeBinary(r *gob.Decoder) error { } // PreparationHash implements PrepareResponse interface. -func (p *prepareResponse) PreparationHash() util.Uint256 { +func (p *prepareResponse) PreparationHash() crypto.Uint256 { return p.preparationHash } // SetPreparationHash implements PrepareResponse interface. -func (p *prepareResponse) SetPreparationHash(h util.Uint256) { +func (p *prepareResponse) SetPreparationHash(h crypto.Uint256) { p.preparationHash = h } diff --git a/payload/recovery_message.go b/payload/recovery_message.go index 117473e8..4292d34a 100644 --- a/payload/recovery_message.go +++ b/payload/recovery_message.go @@ -5,36 +5,35 @@ import ( "errors" "github.com/nspcc-dev/dbft/crypto" - "github.com/nspcc-dev/neo-go/pkg/util" ) type ( // RecoveryMessage represents dBFT Recovery message. - RecoveryMessage interface { + RecoveryMessage[H crypto.Hash, A crypto.Address] interface { // AddPayload adds payload from this epoch to be recovered. - AddPayload(p ConsensusPayload) + AddPayload(p ConsensusPayload[H, A]) // GetPrepareRequest returns PrepareRequest to be processed. - GetPrepareRequest(p ConsensusPayload, validators []crypto.PublicKey, primary uint16) ConsensusPayload + GetPrepareRequest(p ConsensusPayload[H, A], validators []crypto.PublicKey, primary uint16) ConsensusPayload[H, A] // GetPrepareResponses returns a slice of PrepareResponse in any order. - GetPrepareResponses(p ConsensusPayload, validators []crypto.PublicKey) []ConsensusPayload + GetPrepareResponses(p ConsensusPayload[H, A], validators []crypto.PublicKey) []ConsensusPayload[H, A] // GetChangeViews returns a slice of ChangeView in any order. - GetChangeViews(p ConsensusPayload, validators []crypto.PublicKey) []ConsensusPayload + GetChangeViews(p ConsensusPayload[H, A], validators []crypto.PublicKey) []ConsensusPayload[H, A] // GetCommits returns a slice of Commit in any order. - GetCommits(p ConsensusPayload, validators []crypto.PublicKey) []ConsensusPayload + GetCommits(p ConsensusPayload[H, A], validators []crypto.PublicKey) []ConsensusPayload[H, A] // PreparationHash returns has of PrepareRequest payload for this epoch. // It can be useful in case only PrepareResponse payloads were received. - PreparationHash() *util.Uint256 + PreparationHash() *H // SetPreparationHash sets preparation hash. - SetPreparationHash(h *util.Uint256) + SetPreparationHash(h *H) } recoveryMessage struct { - preparationHash *util.Uint256 + preparationHash *crypto.Uint256 preparationPayloads []preparationCompact commitPayloads []commitCompact changeViewPayloads []changeViewCompact - prepareRequest PrepareRequest + prepareRequest PrepareRequest[crypto.Uint256, crypto.Uint160] } // recoveryMessageAux is an auxiliary structure for recoveryMessage encoding. recoveryMessageAux struct { @@ -44,20 +43,20 @@ type ( } ) -var _ RecoveryMessage = (*recoveryMessage)(nil) +var _ RecoveryMessage[crypto.Uint256, crypto.Uint160] = (*recoveryMessage)(nil) // PreparationHash implements RecoveryMessage interface. -func (m *recoveryMessage) PreparationHash() *util.Uint256 { +func (m *recoveryMessage) PreparationHash() *crypto.Uint256 { return m.preparationHash } // SetPreparationHash implements RecoveryMessage interface. -func (m *recoveryMessage) SetPreparationHash(h *util.Uint256) { +func (m *recoveryMessage) SetPreparationHash(h *crypto.Uint256) { m.preparationHash = h } // AddPayload implements RecoveryMessage interface. -func (m *recoveryMessage) AddPayload(p ConsensusPayload) { +func (m *recoveryMessage) AddPayload(p ConsensusPayload[crypto.Uint256, crypto.Uint160]) { switch p.Type() { case PrepareRequestType: m.prepareRequest = p.GetPrepareRequest() @@ -83,7 +82,7 @@ func (m *recoveryMessage) AddPayload(p ConsensusPayload) { } } -func fromPayload(t MessageType, recovery ConsensusPayload, p Serializable) *Payload { +func fromPayload(t MessageType, recovery ConsensusPayload[crypto.Uint256, crypto.Uint160], p Serializable) *Payload { return &Payload{ message: message{ cmType: t, @@ -95,7 +94,7 @@ func fromPayload(t MessageType, recovery ConsensusPayload, p Serializable) *Payl } // GetPrepareRequest implements RecoveryMessage interface. -func (m *recoveryMessage) GetPrepareRequest(p ConsensusPayload, _ []crypto.PublicKey, ind uint16) ConsensusPayload { +func (m *recoveryMessage) GetPrepareRequest(p ConsensusPayload[crypto.Uint256, crypto.Uint160], _ []crypto.PublicKey, ind uint16) ConsensusPayload[crypto.Uint256, crypto.Uint160] { if m.prepareRequest == nil { return nil } @@ -113,12 +112,12 @@ func (m *recoveryMessage) GetPrepareRequest(p ConsensusPayload, _ []crypto.Publi } // GetPrepareResponses implements RecoveryMessage interface. -func (m *recoveryMessage) GetPrepareResponses(p ConsensusPayload, _ []crypto.PublicKey) []ConsensusPayload { +func (m *recoveryMessage) GetPrepareResponses(p ConsensusPayload[crypto.Uint256, crypto.Uint160], _ []crypto.PublicKey) []ConsensusPayload[crypto.Uint256, crypto.Uint160] { if m.preparationHash == nil { return nil } - payloads := make([]ConsensusPayload, len(m.preparationPayloads)) + payloads := make([]ConsensusPayload[crypto.Uint256, crypto.Uint160], len(m.preparationPayloads)) for i, resp := range m.preparationPayloads { payloads[i] = fromPayload(PrepareResponseType, p, &prepareResponse{ @@ -131,8 +130,8 @@ func (m *recoveryMessage) GetPrepareResponses(p ConsensusPayload, _ []crypto.Pub } // GetChangeViews implements RecoveryMessage interface. -func (m *recoveryMessage) GetChangeViews(p ConsensusPayload, _ []crypto.PublicKey) []ConsensusPayload { - payloads := make([]ConsensusPayload, len(m.changeViewPayloads)) +func (m *recoveryMessage) GetChangeViews(p ConsensusPayload[crypto.Uint256, crypto.Uint160], _ []crypto.PublicKey) []ConsensusPayload[crypto.Uint256, crypto.Uint160] { + payloads := make([]ConsensusPayload[crypto.Uint256, crypto.Uint160], len(m.changeViewPayloads)) for i, cv := range m.changeViewPayloads { payloads[i] = fromPayload(ChangeViewType, p, &changeView{ @@ -146,8 +145,8 @@ func (m *recoveryMessage) GetChangeViews(p ConsensusPayload, _ []crypto.PublicKe } // GetCommits implements RecoveryMessage interface. -func (m *recoveryMessage) GetCommits(p ConsensusPayload, _ []crypto.PublicKey) []ConsensusPayload { - payloads := make([]ConsensusPayload, len(m.commitPayloads)) +func (m *recoveryMessage) GetCommits(p ConsensusPayload[crypto.Uint256, crypto.Uint160], _ []crypto.PublicKey) []ConsensusPayload[crypto.Uint256, crypto.Uint160] { + payloads := make([]ConsensusPayload[crypto.Uint256, crypto.Uint160], len(m.commitPayloads)) for i, c := range m.commitPayloads { payloads[i] = fromPayload(CommitType, p, &commit{signature: c.Signature}) @@ -173,7 +172,7 @@ func (m recoveryMessage) EncodeBinary(w *gob.Encoder) error { return err } } else { - if err := w.Encode(util.Uint256Size); err != nil { + if err := w.Encode(crypto.Uint256Size); err != nil { return err } if err := w.Encode(m.preparationHash); err != nil { @@ -205,13 +204,13 @@ func (m *recoveryMessage) DecodeBinary(r *gob.Decoder) error { return err } if l != 0 { - if l == util.Uint256Size { - m.preparationHash = new(util.Uint256) + if l == crypto.Uint256Size { + m.preparationHash = new(crypto.Uint256) if err := r.Decode(m.preparationHash); err != nil { return err } } else { - return errors.New("wrong util.Uint256 length") + return errors.New("wrong crypto.Uint256 length") } } else { m.preparationHash = nil diff --git a/send.go b/send.go index 2c86fd3c..64f71bca 100644 --- a/send.go +++ b/send.go @@ -5,7 +5,7 @@ import ( "go.uber.org/zap" ) -func (d *DBFT) broadcast(msg payload.ConsensusPayload) { +func (d *DBFT[H, A]) broadcast(msg payload.ConsensusPayload[H, A]) { d.Logger.Debug("broadcasting message", zap.Stringer("type", msg.Type()), zap.Uint32("height", d.BlockIndex), @@ -15,7 +15,7 @@ func (d *DBFT) broadcast(msg payload.ConsensusPayload) { d.Broadcast(msg) } -func (c *Context) makePrepareRequest() payload.ConsensusPayload { +func (c *Context[H, A]) makePrepareRequest() payload.ConsensusPayload[H, A] { c.Fill() req := c.Config.NewPrepareRequest() @@ -27,7 +27,7 @@ func (c *Context) makePrepareRequest() payload.ConsensusPayload { return c.Config.NewConsensusPayload(c, payload.PrepareRequestType, req) } -func (d *DBFT) sendPrepareRequest() { +func (d *DBFT[H, A]) sendPrepareRequest() { msg := d.makePrepareRequest() d.PreparationPayloads[d.MyIndex] = msg d.broadcast(msg) @@ -42,7 +42,7 @@ func (d *DBFT) sendPrepareRequest() { d.checkPrepare() } -func (c *Context) makeChangeView(ts uint64, reason payload.ChangeViewReason) payload.ConsensusPayload { +func (c *Context[H, A]) makeChangeView(ts uint64, reason payload.ChangeViewReason) payload.ConsensusPayload[H, A] { cv := c.Config.NewChangeView() cv.SetNewViewNumber(c.ViewNumber + 1) cv.SetTimestamp(ts) @@ -54,7 +54,7 @@ func (c *Context) makeChangeView(ts uint64, reason payload.ChangeViewReason) pay return msg } -func (d *DBFT) sendChangeView(reason payload.ChangeViewReason) { +func (d *DBFT[H, A]) sendChangeView(reason payload.ChangeViewReason) { if d.Context.WatchOnly() { return } @@ -91,7 +91,7 @@ func (d *DBFT) sendChangeView(reason payload.ChangeViewReason) { d.checkChangeView(newView) } -func (c *Context) makePrepareResponse() payload.ConsensusPayload { +func (c *Context[H, A]) makePrepareResponse() payload.ConsensusPayload[H, A] { resp := c.Config.NewPrepareResponse() resp.SetPreparationHash(c.PreparationPayloads[c.PrimaryIndex].Hash()) @@ -101,14 +101,14 @@ func (c *Context) makePrepareResponse() payload.ConsensusPayload { return msg } -func (d *DBFT) sendPrepareResponse() { +func (d *DBFT[H, A]) 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) makeCommit() payload.ConsensusPayload { +func (c *Context[H, A]) makeCommit() payload.ConsensusPayload[H, A] { if msg := c.CommitPayloads[c.MyIndex]; msg != nil { return msg } @@ -128,14 +128,14 @@ func (c *Context) makeCommit() payload.ConsensusPayload { return nil } -func (d *DBFT) sendCommit() { +func (d *DBFT[H, A]) sendCommit() { msg := d.makeCommit() d.CommitPayloads[d.MyIndex] = msg d.Logger.Info("sending Commit", zap.Uint32("height", d.BlockIndex), zap.Uint("view", uint(d.ViewNumber))) d.broadcast(msg) } -func (d *DBFT) sendRecoveryRequest() { +func (d *DBFT[H, A]) 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() { @@ -146,7 +146,7 @@ func (d *DBFT) sendRecoveryRequest() { d.broadcast(d.Config.NewConsensusPayload(&d.Context, payload.RecoveryRequestType, req)) } -func (c *Context) makeRecoveryMessage() payload.ConsensusPayload { +func (c *Context[H, A]) makeRecoveryMessage() payload.ConsensusPayload[H, A] { recovery := c.Config.NewRecoveryMessage() for _, p := range c.PreparationPayloads { @@ -176,19 +176,6 @@ func (c *Context) makeRecoveryMessage() payload.ConsensusPayload { return c.Config.NewConsensusPayload(c, payload.RecoveryMessageType, recovery) } -func (d *DBFT) sendRecoveryMessage() { +func (d *DBFT[H, A]) sendRecoveryMessage() { d.broadcast(d.makeRecoveryMessage()) } - -// defaultNewConsensusPayload is default function for creating -// consensus payload of specific type. -func defaultNewConsensusPayload(c *Context, t payload.MessageType, msg any) payload.ConsensusPayload { - cp := payload.NewConsensusPayload() - cp.SetHeight(c.BlockIndex) - cp.SetValidatorIndex(uint16(c.MyIndex)) - cp.SetViewNumber(c.ViewNumber) - cp.SetType(t) - cp.SetPayload(msg) - - return cp -} diff --git a/simulation/main.go b/simulation/main.go index faae3a06..8482bfd2 100644 --- a/simulation/main.go +++ b/simulation/main.go @@ -20,7 +20,6 @@ import ( "github.com/nspcc-dev/dbft/block" "github.com/nspcc-dev/dbft/crypto" "github.com/nspcc-dev/dbft/payload" - "github.com/nspcc-dev/neo-go/pkg/util" "github.com/spaolacci/murmur3" "go.uber.org/zap" ) @@ -28,8 +27,8 @@ import ( type ( simNode struct { id int - d *dbft.DBFT - messages chan payload.ConsensusPayload + d *dbft.DBFT[crypto.Uint256, crypto.Uint160] + messages chan payload.ConsensusPayload[crypto.Uint256, crypto.Uint160] key crypto.PrivateKey pub crypto.PublicKey pool *memPool @@ -37,7 +36,7 @@ type ( log *zap.Logger height uint32 - lastHash util.Uint256 + lastHash crypto.Uint256 validators []crypto.PublicKey } ) @@ -111,11 +110,24 @@ func initNodes(nodes []*simNode, log *zap.Logger) { } } +// defaultNewConsensusPayload is default function for creating +// consensus payload of specific type. +func defaultNewConsensusPayload(c *dbft.Context[crypto.Uint256, crypto.Uint160], t payload.MessageType, msg any) payload.ConsensusPayload[crypto.Uint256, crypto.Uint160] { + cp := payload.NewConsensusPayload() + cp.SetHeight(c.BlockIndex) + cp.SetValidatorIndex(uint16(c.MyIndex)) + cp.SetViewNumber(c.ViewNumber) + cp.SetType(t) + cp.SetPayload(msg) + + return cp +} + func initSimNode(nodes []*simNode, i int, log *zap.Logger) error { key, pub := crypto.Generate(rand.Reader) nodes[i] = &simNode{ id: i, - messages: make(chan payload.ConsensusPayload, defaultChanSize), + messages: make(chan payload.ConsensusPayload[crypto.Uint256, crypto.Uint160], defaultChanSize), key: key, pub: pub, pool: newMemoryPool(), @@ -123,19 +135,29 @@ func initSimNode(nodes []*simNode, i int, log *zap.Logger) error { cluster: nodes, } - nodes[i].d = dbft.New( - dbft.WithLogger(nodes[i].log), - dbft.WithSecondsPerBlock(time.Second*5), - dbft.WithKeyPair(key, pub), - dbft.WithGetTx(nodes[i].pool.Get), - dbft.WithGetVerified(nodes[i].pool.GetVerified), - dbft.WithBroadcast(nodes[i].Broadcast), - dbft.WithProcessBlock(nodes[i].ProcessBlock), - dbft.WithCurrentHeight(nodes[i].CurrentHeight), - dbft.WithCurrentBlockHash(nodes[i].CurrentBlockHash), - dbft.WithGetValidators(nodes[i].GetValidators), - dbft.WithVerifyPrepareRequest(nodes[i].VerifyPayload), - dbft.WithVerifyPrepareResponse(nodes[i].VerifyPayload), + nodes[i].d = dbft.New[crypto.Uint256, crypto.Uint160]( + dbft.WithLogger[crypto.Uint256, crypto.Uint160](nodes[i].log), + dbft.WithSecondsPerBlock[crypto.Uint256, crypto.Uint160](time.Second*5), + dbft.WithKeyPair[crypto.Uint256, crypto.Uint160](key, pub), + dbft.WithGetTx[crypto.Uint256, crypto.Uint160](nodes[i].pool.Get), + dbft.WithGetVerified[crypto.Uint256, crypto.Uint160](nodes[i].pool.GetVerified), + dbft.WithBroadcast[crypto.Uint256, crypto.Uint160](nodes[i].Broadcast), + dbft.WithProcessBlock[crypto.Uint256, crypto.Uint160](nodes[i].ProcessBlock), + dbft.WithCurrentHeight[crypto.Uint256, crypto.Uint160](nodes[i].CurrentHeight), + dbft.WithCurrentBlockHash[crypto.Uint256, crypto.Uint160](nodes[i].CurrentBlockHash), + dbft.WithGetValidators[crypto.Uint256, crypto.Uint160](nodes[i].GetValidators), + dbft.WithVerifyPrepareRequest[crypto.Uint256, crypto.Uint160](nodes[i].VerifyPayload), + dbft.WithVerifyPrepareResponse[crypto.Uint256, crypto.Uint160](nodes[i].VerifyPayload), + + dbft.WithNewBlockFromContext[crypto.Uint256, crypto.Uint160](dbft.NewBlockFromContext), + dbft.WithGetConsensusAddress[crypto.Uint256, crypto.Uint160](func(...crypto.PublicKey) crypto.Uint160 { return crypto.Uint160{} }), + dbft.WithNewConsensusPayload[crypto.Uint256, crypto.Uint160](defaultNewConsensusPayload), + dbft.WithNewPrepareRequest[crypto.Uint256, crypto.Uint160](payload.NewPrepareRequest), + dbft.WithNewPrepareResponse[crypto.Uint256, crypto.Uint160](payload.NewPrepareResponse), + dbft.WithNewChangeView[crypto.Uint256, crypto.Uint160](payload.NewChangeView), + dbft.WithNewCommit[crypto.Uint256, crypto.Uint160](payload.NewCommit), + dbft.WithNewRecoveryMessage[crypto.Uint256, crypto.Uint160](payload.NewRecoveryMessage), + dbft.WithNewRecoveryRequest[crypto.Uint256, crypto.Uint160](payload.NewRecoveryRequest), ) if nodes[i].d == nil { @@ -168,7 +190,7 @@ func sortValidators(pubs []crypto.PublicKey) { }) } -func (n *simNode) Broadcast(m payload.ConsensusPayload) { +func (n *simNode) Broadcast(m payload.ConsensusPayload[crypto.Uint256, crypto.Uint160]) { for i, node := range n.cluster { if i != n.id { select { @@ -180,15 +202,15 @@ func (n *simNode) Broadcast(m payload.ConsensusPayload) { } } -func (n *simNode) CurrentHeight() uint32 { return n.height } -func (n *simNode) CurrentBlockHash() util.Uint256 { return n.lastHash } +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(...block.Transaction) []crypto.PublicKey { +func (n *simNode) GetValidators(...block.Transaction[crypto.Uint256]) []crypto.PublicKey { return n.validators } -func (n *simNode) ProcessBlock(b block.Block) { +func (n *simNode) ProcessBlock(b block.Block[crypto.Uint256, crypto.Uint160]) { n.d.Logger.Debug("received block", zap.Uint32("height", b.Index())) for _, tx := range b.Transactions() { @@ -200,7 +222,7 @@ func (n *simNode) ProcessBlock(b block.Block) { } // VerifyPrepareRequest verifies that payload was received from a good validator. -func (n *simNode) VerifyPayload(p payload.ConsensusPayload) error { +func (n *simNode) VerifyPayload(p payload.ConsensusPayload[crypto.Uint256, crypto.Uint160]) error { if *blocked != -1 && p.ValidatorIndex() == uint16(*blocked) { return fmt.Errorf("message from blocked validator: %d", *blocked) } @@ -220,9 +242,9 @@ func (n *simNode) addTx(count int) { type tx64 uint64 -var _ block.Transaction = (*tx64)(nil) +var _ block.Transaction[crypto.Uint256] = (*tx64)(nil) -func (t *tx64) Hash() (h util.Uint256) { +func (t *tx64) Hash() (h crypto.Uint256) { binary.LittleEndian.PutUint64(h[:], uint64(*t)) return } @@ -252,17 +274,17 @@ func (t *tx64) UnmarshalBinary(data []byte) error { type memPool struct { mtx *sync.RWMutex - store map[util.Uint256]block.Transaction + store map[crypto.Uint256]block.Transaction[crypto.Uint256] } func newMemoryPool() *memPool { return &memPool{ mtx: new(sync.RWMutex), - store: make(map[util.Uint256]block.Transaction), + store: make(map[crypto.Uint256]block.Transaction[crypto.Uint256]), } } -func (p *memPool) Add(tx block.Transaction) { +func (p *memPool) Add(tx block.Transaction[crypto.Uint256]) { p.mtx.Lock() h := tx.Hash() @@ -273,7 +295,7 @@ func (p *memPool) Add(tx block.Transaction) { p.mtx.Unlock() } -func (p *memPool) Get(h util.Uint256) (tx block.Transaction) { +func (p *memPool) Get(h crypto.Uint256) (tx block.Transaction[crypto.Uint256]) { p.mtx.RLock() tx = p.store[h] p.mtx.RUnlock() @@ -281,19 +303,19 @@ func (p *memPool) Get(h util.Uint256) (tx block.Transaction) { return } -func (p *memPool) Delete(h util.Uint256) { +func (p *memPool) Delete(h crypto.Uint256) { p.mtx.Lock() delete(p.store, h) p.mtx.Unlock() } -func (p *memPool) GetVerified() (txx []block.Transaction) { +func (p *memPool) GetVerified() (txx []block.Transaction[crypto.Uint256]) { n := *txPerBlock if n == 0 { return } - txx = make([]block.Transaction, 0, n) + txx = make([]block.Transaction[crypto.Uint256], 0, n) for _, tx := range p.store { txx = append(txx, tx)