Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

*: add Hash/Address generic parameters to dBFT #94

Merged
merged 3 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
39 changes: 19 additions & 20 deletions block/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

"github.com/nspcc-dev/dbft/crypto"
"github.com/nspcc-dev/dbft/merkle"
"github.com/nspcc-dev/neo-go/pkg/util"
)

type (
Expand All @@ -17,29 +16,29 @@
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.
Index() uint32
// 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
Expand All @@ -49,18 +48,18 @@
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
}
)

Expand All @@ -70,7 +69,7 @@
}

// PrevHash implements Block interface.
func (b *neoBlock) PrevHash() util.Uint256 {
func (b *neoBlock) PrevHash() crypto.Uint256 {
return b.base.PrevHash
}

Expand All @@ -85,12 +84,12 @@
}

// 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
}

Expand All @@ -100,17 +99,17 @@
}

// 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] {

Check warning on line 112 in block/block.go

View check run for this annotation

Codecov / codecov/patch

block/block.go#L112

Added line #L112 was not covered by tests
block := new(neoBlock)
block.base.Timestamp = uint32(timestamp / 1000000000)
block.base.Index = index
Expand Down Expand Up @@ -165,7 +164,7 @@
}

// 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 {
Expand Down
19 changes: 9 additions & 10 deletions block/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,16 @@ 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"
)

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())

Expand All @@ -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
Expand Down Expand Up @@ -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
}
6 changes: 3 additions & 3 deletions block/transaction.go
Original file line number Diff line number Diff line change
@@ -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
}
6 changes: 3 additions & 3 deletions check.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
Loading
Loading