Skip to content

Commit

Permalink
BFT chain unit tests: start a network
Browse files Browse the repository at this point in the history
Signed-off-by: May Rosenbaum <[email protected]>
Signed-off-by: Emil Elizarov <[email protected]>
  • Loading branch information
MayRosenbaum committed Feb 21, 2024
1 parent a307846 commit 4d1e56d
Show file tree
Hide file tree
Showing 4 changed files with 346 additions and 227 deletions.
56 changes: 2 additions & 54 deletions orderer/consensus/smartbft/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ import (
"sync/atomic"
"time"

"github.com/hyperledger/fabric/common/channelconfig"
"github.com/hyperledger/fabric/internal/pkg/identity"

"github.com/SmartBFT-Go/consensus/pkg/api"
smartbft "github.com/SmartBFT-Go/consensus/pkg/consensus"
"github.com/SmartBFT-Go/consensus/pkg/types"
Expand All @@ -29,7 +26,6 @@ import (
"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/orderer/common/blockcutter"
"github.com/hyperledger/fabric/orderer/common/cluster"
"github.com/hyperledger/fabric/orderer/common/msgprocessor"
types2 "github.com/hyperledger/fabric/orderer/common/types"
Expand Down Expand Up @@ -72,62 +68,14 @@ type signerSerializer interface {

// ConsenterSupport provides the resources available to a Consenter implementation.
type ConsenterSupport interface {
identity.SignerSerializer
msgprocessor.Processor

// SignatureVerifier verifies a signature of a block.
SignatureVerifier() protoutil.BlockVerifierFunc

// BlockCutter returns the block cutting helper for this channel.
BlockCutter() blockcutter.Receiver

// SharedConfig provides the shared config from the channel's current config block.
SharedConfig() channelconfig.Orderer

// ChannelConfig provides the channel config from the channel's current config block.
ChannelConfig() channelconfig.Channel

// CreateNextBlock takes a list of messages and creates the next block based on the block with highest block number committed to the ledger
// Note that either WriteBlock or WriteConfigBlock must be called before invoking this method a second time.
CreateNextBlock(messages []*cb.Envelope) *cb.Block

// Block returns a block with the given number,
// or nil if such a block doesn't exist.
Block(number uint64) *cb.Block

// WriteBlock commits a block to the ledger.
WriteBlock(block *cb.Block, encodedMetadataValue []byte)

// WriteConfigBlock commits a block to the ledger, and applies the config update inside.
WriteConfigBlock(block *cb.Block, encodedMetadataValue []byte)

// Sequence returns the current config sequence.
Sequence() uint64

// ChannelID returns the channel ID this support is associated with.
ChannelID() string

// Height returns the number of blocks in the chain this channel is associated with.
Height() uint64
// Append appends a new block to the ledger in its raw form,
// unlike WriteBlock that also mutates its metadata.
Append(block *cb.Block) error
consensus.ConsenterSupport
}

//go:generate mockery --dir . --name Communicator --case underscore --with-expecter=true --output mocks

// Communicator defines communication for a consenter
type Communicator interface {
// Remote returns a RemoteContext for the given RemoteNode ID in the context
// of the given channel, or error if connection cannot be established, or
// the channel wasn't configured
Remote(channel string, id uint64) (*cluster.RemoteContext, error)
// Configure configures the communication to connect to all
// given members, and disconnect from any members not among the given
// members.
Configure(channel string, members []cluster.RemoteNode)
// Shutdown shuts down the communicator
Shutdown()
cluster.Communicator
}

// BFTChain implements Chain interface to wire with
Expand Down
182 changes: 9 additions & 173 deletions orderer/consensus/smartbft/chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,182 +5,18 @@
package smartbft_test

import (
"fmt"
"os"
"path"
"path/filepath"
"testing"
"time"

cb "github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric/common/crypto/tlsgen"
"github.com/hyperledger/fabric/core/config/configtest"
"github.com/hyperledger/fabric/internal/configtxgen/encoder"
"github.com/hyperledger/fabric/internal/configtxgen/genesisconfig"
smartBFTMocks "github.com/hyperledger/fabric/orderer/consensus/smartbft/mocks"
"github.com/hyperledger/fabric/protoutil"
"github.com/stretchr/testify/mock"

"github.com/SmartBFT-Go/consensus/pkg/api"
"github.com/SmartBFT-Go/consensus/pkg/types"
"github.com/SmartBFT-Go/consensus/pkg/wal"
"github.com/hyperledger/fabric/bccsp/sw"
"github.com/hyperledger/fabric/common/metrics/disabled"
"github.com/hyperledger/fabric/orderer/consensus/smartbft"
"github.com/stretchr/testify/require"
)

// TestNewChain create a new bft chain which will be in the future exposed to all nodes in the network.
// the chain is created using mocks
func TestNewChain(t *testing.T) {
nodeId := uint64(1)
// Scenario:
// 1. Start a network of 4 nodes
// 2. Submit a TX
// 3. Wait for the TX to be received by all nodes
func TestSuccessfulTxPropagation(t *testing.T) {
dir := t.TempDir()
channelId := "testchannel"
block := createConfigBlock(t, channelId)

// In the future we will have a node that holds a ptr to the chain, so SelfID should be changed to the corresponding node id.
config := types.Configuration{
SelfID: nodeId,
RequestBatchMaxCount: 100,
RequestBatchMaxBytes: 10485760,
RequestBatchMaxInterval: 50 * time.Millisecond,
IncomingMessageBufferSize: 200,
RequestPoolSize: 400,
RequestForwardTimeout: 2 * time.Second,
RequestComplainTimeout: 20 * time.Second,
RequestAutoRemoveTimeout: 3 * time.Minute,
ViewChangeResendInterval: 5 * time.Second,
ViewChangeTimeout: 20 * time.Second,
LeaderHeartbeatTimeout: 1 * time.Minute,
LeaderHeartbeatCount: 10,
NumOfTicksBehindBeforeSyncing: types.DefaultConfig.NumOfTicksBehindBeforeSyncing,
CollectTimeout: 1 * time.Second,
SyncOnStart: types.DefaultConfig.SyncOnStart,
SpeedUpViewChange: types.DefaultConfig.SpeedUpViewChange,
LeaderRotation: types.DefaultConfig.LeaderRotation,
DecisionsPerLeader: types.DefaultConfig.DecisionsPerLeader,
RequestMaxBytes: types.DefaultConfig.RequestMaxBytes,
RequestPoolSubmitTimeout: types.DefaultConfig.RequestPoolSubmitTimeout,
}

rootDir := t.TempDir()
walDir := filepath.Join(rootDir, "wal")
err := os.Mkdir(walDir, os.ModePerm)
require.NoError(t, err)

comm := smartBFTMocks.NewCommunicator(t)
comm.EXPECT().Configure(mock.Anything, mock.Anything)

// the number of blocks should be determined by the number of blocks in the ledger, for now lets assume we have one block
supportMock := smartBFTMocks.NewConsenterSupport(t)
supportMock.EXPECT().ChannelID().Return(channelId)
supportMock.EXPECT().Height().Return(uint64(1))
supportMock.EXPECT().Block(mock.AnythingOfType("uint64")).Return(block)
supportMock.EXPECT().Sequence().Return(uint64(1))

mpc := &smartbft.MetricProviderConverter{MetricsProvider: &disabled.Provider{}}
metricsBFT := api.NewMetrics(mpc, channelId)
metricsWalBFT := wal.NewMetrics(mpc, channelId)

cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
require.NoError(t, err)

bftChain, err := smartbft.NewChain(
nil,
nodeId,
config,
walDir,
nil,
comm,
nil,
nil,
supportMock,
smartbft.NewMetrics(&disabled.Provider{}),
metricsBFT,
metricsWalBFT,
cryptoProvider)

require.NoError(t, err)
require.NotNil(t, bftChain)
}

func createConfigBlock(t *testing.T, channelId string) *cb.Block {
certDir := t.TempDir()
tlsCA, err := tlsgen.NewCA()
require.NoError(t, err)
configProfile := genesisconfig.Load(genesisconfig.SampleAppChannelSmartBftProfile, configtest.GetDevConfigDir())
tlsCertPath := filepath.Join(configtest.GetDevConfigDir(), "msp", "tlscacerts", "tlsroot.pem")

// make all BFT nodes belong to the same MSP ID
for _, consenter := range configProfile.Orderer.ConsenterMapping {
consenter.MSPID = "SampleOrg"
consenter.Identity = tlsCertPath
consenter.ClientTLSCert = tlsCertPath
consenter.ServerTLSCert = tlsCertPath
}

generateCertificatesSmartBFT(t, configProfile, tlsCA, certDir)

channelGroup, err := encoder.NewChannelGroup(configProfile)
require.NoError(t, err)
require.NotNil(t, channelGroup)

_, err = sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
require.NoError(t, err)

block := blockWithGroups(channelGroup, channelId, 1)
return block
}

func generateCertificatesSmartBFT(t *testing.T, confAppSmartBFT *genesisconfig.Profile, tlsCA tlsgen.CA, certDir string) {
for i, c := range confAppSmartBFT.Orderer.ConsenterMapping {
t.Logf("BFT Consenter: %+v", c)
srvC, err := tlsCA.NewServerCertKeyPair(c.Host)
require.NoError(t, err)
srvP := path.Join(certDir, fmt.Sprintf("server%d.crt", i))
err = os.WriteFile(srvP, srvC.Cert, 0o644)
require.NoError(t, err)

clnC, err := tlsCA.NewClientCertKeyPair()
require.NoError(t, err)
clnP := path.Join(certDir, fmt.Sprintf("client%d.crt", i))
err = os.WriteFile(clnP, clnC.Cert, 0o644)
require.NoError(t, err)

c.Identity = srvP
c.ServerTLSCert = srvP
c.ClientTLSCert = clnP
}
}

func blockWithGroups(groups *cb.ConfigGroup, channelID string, blockNumber uint64) *cb.Block {
block := protoutil.NewBlock(blockNumber, nil)
block.Data = &cb.BlockData{
Data: [][]byte{
protoutil.MarshalOrPanic(&cb.Envelope{
Payload: protoutil.MarshalOrPanic(&cb.Payload{
Data: protoutil.MarshalOrPanic(&cb.ConfigEnvelope{
Config: &cb.Config{
ChannelGroup: groups,
},
}),
Header: &cb.Header{
ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{
Type: int32(cb.HeaderType_CONFIG),
ChannelId: channelID,
}),
},
}),
}),
},
}
block.Header.DataHash = protoutil.ComputeBlockDataHash(block.Data)
block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{
Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
LastConfig: &cb.LastConfig{
Index: uint64(blockNumber),
},
}),
})

return block
networkSetupInfo := NewNetworkSetupInfo(t, channelId, dir)
_ = networkSetupInfo.CreateNodes(4)
networkSetupInfo.StartAllNodes()
}
7 changes: 7 additions & 0 deletions orderer/consensus/smartbft/interfaces_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package smartbft
Loading

0 comments on commit 4d1e56d

Please sign in to comment.