Skip to content

Commit

Permalink
Merge pull request #94 from nspcc-dev/generic-dbft
Browse files Browse the repository at this point in the history
  • Loading branch information
roman-khimov committed Feb 13, 2024
2 parents 92d9aec + 87f247c commit 3c9c1c8
Show file tree
Hide file tree
Showing 27 changed files with 619 additions and 605 deletions.
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 @@ import (

"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 @@ 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.
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 @@ 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
}
)

Expand All @@ -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
}

Expand All @@ -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
}

Expand All @@ -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
Expand Down Expand Up @@ -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 {
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

0 comments on commit 3c9c1c8

Please sign in to comment.