go_study/fabric-main/core/deliverservice/deliveryclient_test.go

282 lines
8.7 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package deliverservice
import (
"fmt"
"io/ioutil"
"path"
"testing"
"time"
cb "github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric/bccsp/sw"
"github.com/hyperledger/fabric/common/crypto/tlsgen"
"github.com/hyperledger/fabric/core/config/configtest"
"github.com/hyperledger/fabric/core/deliverservice/fake"
"github.com/hyperledger/fabric/internal/configtxgen/encoder"
"github.com/hyperledger/fabric/internal/configtxgen/genesisconfig"
"github.com/hyperledger/fabric/internal/pkg/comm"
"github.com/hyperledger/fabric/internal/pkg/peer/blocksprovider"
"github.com/stretchr/testify/require"
)
//go:generate counterfeiter -o fake/ledger_info.go --fake-name LedgerInfo . ledgerInfo
type ledgerInfo interface {
blocksprovider.LedgerInfo
}
func TestStartDeliverForChannel(t *testing.T) {
fakeLedgerInfo := &fake.LedgerInfo{}
fakeLedgerInfo.LedgerHeightReturns(0, fmt.Errorf("fake-ledger-error"))
secOpts := comm.SecureOptions{
UseTLS: true,
RequireClientCert: true,
// The below certificates were taken from the peer TLS
// dir as output by cryptogen.
// They are server.crt and server.key respectively.
Certificate: []byte(`-----BEGIN CERTIFICATE-----
MIIChTCCAiygAwIBAgIQOrr7/tDzKhhCba04E6QVWzAKBggqhkjOPQQDAjB2MQsw
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
YW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxz
Y2Eub3JnMS5leGFtcGxlLmNvbTAeFw0xOTA4MjcyMDA2MDBaFw0yOTA4MjQyMDA2
MDBaMFsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH
Ew1TYW4gRnJhbmNpc2NvMR8wHQYDVQQDExZwZWVyMC5vcmcxLmV4YW1wbGUuY29t
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExglppLxiAYSasrdFsrZJDxRULGBb
wHlArrap9SmAzGIeeIuqe9t3F23Q5Jry9lAnIh8h3UlkvZZpClXcjRiCeqOBtjCB
szAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
MAwGA1UdEwEB/wQCMAAwKwYDVR0jBCQwIoAgL35aqafj6SNnWdI4aMLh+oaFJvsA
aoHgYMkcPvvkiWcwRwYDVR0RBEAwPoIWcGVlcjAub3JnMS5leGFtcGxlLmNvbYIF
cGVlcjCCFnBlZXIwLm9yZzEuZXhhbXBsZS5jb22CBXBlZXIwMAoGCCqGSM49BAMC
A0cAMEQCIAiAGoYeKPMd3bqtixZji8q2zGzLmIzq83xdTJoZqm50AiAKleso2EVi
2TwsekWGpMaCOI6JV1+ZONyti6vBChhUYg==
-----END CERTIFICATE-----`),
Key: []byte(`-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgxiyAFyD0Eg1NxjbS
U2EKDLoTQr3WPK8z7WyeOSzr+GGhRANCAATGCWmkvGIBhJqyt0WytkkPFFQsYFvA
eUCutqn1KYDMYh54i6p723cXbdDkmvL2UCciHyHdSWS9lmkKVdyNGIJ6
-----END PRIVATE KEY-----`,
),
}
confAppRaft := genesisconfig.Load(genesisconfig.SampleAppChannelEtcdRaftProfile, configtest.GetDevConfigDir())
certDir := t.TempDir()
tlsCA, err := tlsgen.NewCA()
require.NoError(t, err)
generateCertificates(t, confAppRaft, tlsCA, certDir)
bootstrapper, err := encoder.NewBootstrapper(confAppRaft)
require.NoError(t, err)
channelConfigProto := &cb.Config{ChannelGroup: bootstrapper.GenesisChannelGroup()}
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
require.NoError(t, err)
t.Run("Green Path With Mutual TLS", func(t *testing.T) {
ds := NewDeliverService(&Config{
DeliverServiceConfig: &DeliverServiceConfig{
SecOpts: secOpts,
},
ChannelConfig: channelConfigProto,
CryptoProvider: cryptoProvider,
}).(*deliverServiceImpl)
finalized := make(chan struct{})
err = ds.StartDeliverForChannel("channel-id", fakeLedgerInfo, func() {
close(finalized)
})
require.NoError(t, err)
select {
case <-finalized:
case <-time.After(time.Second):
require.FailNow(t, "finalizer should have executed")
}
require.NotNil(t, ds.blockDeliverer)
bpd := ds.blockDeliverer.(*blocksprovider.Deliverer)
require.Equal(t, "76f7a03f8dfdb0ef7c4b28b3901fe163c730e906c70e4cdf887054ad5f608bed", fmt.Sprintf("%x", bpd.TLSCertHash))
})
t.Run("Green Path without mutual TLS", func(t *testing.T) {
ds := NewDeliverService(&Config{
DeliverServiceConfig: &DeliverServiceConfig{},
ChannelConfig: channelConfigProto,
CryptoProvider: cryptoProvider,
}).(*deliverServiceImpl)
finalized := make(chan struct{})
err := ds.StartDeliverForChannel("channel-id", fakeLedgerInfo, func() {
close(finalized)
})
require.NoError(t, err)
select {
case <-finalized:
case <-time.After(time.Second):
require.FailNow(t, "finalizer should have executed")
}
require.NotNil(t, ds.blockDeliverer)
bpd := ds.blockDeliverer.(*blocksprovider.Deliverer)
require.Nil(t, bpd.TLSCertHash)
})
t.Run("Leader yields and re-elected: Start->Stop->Start", func(t *testing.T) {
ds := NewDeliverService(&Config{
DeliverServiceConfig: &DeliverServiceConfig{},
ChannelConfig: channelConfigProto,
CryptoProvider: cryptoProvider,
}).(*deliverServiceImpl)
finalized := make(chan struct{})
err := ds.StartDeliverForChannel("channel-id", fakeLedgerInfo, func() {
close(finalized)
})
require.NoError(t, err)
select {
case <-finalized:
case <-time.After(time.Second):
require.FailNow(t, "finalizer should have executed")
}
require.NotNil(t, ds.blockDeliverer)
bpd := ds.blockDeliverer.(*blocksprovider.Deliverer)
require.Nil(t, bpd.TLSCertHash)
err = ds.StopDeliverForChannel()
require.NoError(t, err)
require.Nil(t, ds.blockDeliverer)
finalized2 := make(chan struct{})
err = ds.StartDeliverForChannel("channel-id", fakeLedgerInfo, func() {
close(finalized2)
})
require.NoError(t, err)
select {
case <-finalized2:
case <-time.After(time.Second):
require.FailNow(t, "finalizer should have executed")
}
})
t.Run("Exists", func(t *testing.T) {
ds := NewDeliverService(&Config{
DeliverServiceConfig: &DeliverServiceConfig{},
ChannelConfig: channelConfigProto,
CryptoProvider: cryptoProvider,
}).(*deliverServiceImpl)
err := ds.StartDeliverForChannel("channel-id", fakeLedgerInfo, func() {})
require.NoError(t, err)
err = ds.StartDeliverForChannel("channel-id", fakeLedgerInfo, func() {})
require.EqualError(t, err, "block deliverer for channel `channel-id` already exists")
})
t.Run("Stopping", func(t *testing.T) {
ds := NewDeliverService(&Config{
DeliverServiceConfig: &DeliverServiceConfig{},
}).(*deliverServiceImpl)
ds.Stop()
err := ds.StartDeliverForChannel("channel-id", fakeLedgerInfo, func() {})
require.EqualError(t, err, "block deliverer for channel `channel-id` is stopping")
})
}
func TestStopDeliverForChannel(t *testing.T) {
t.Run("Green path", func(t *testing.T) {
ds := NewDeliverService(&Config{}).(*deliverServiceImpl)
doneA := make(chan struct{})
ds.blockDeliverer = &blocksprovider.Deliverer{
DoneC: doneA,
}
ds.channelID = "channel-id"
err := ds.StopDeliverForChannel()
require.NoError(t, err)
select {
case <-doneA:
default:
require.Fail(t, "should have stopped the blocksprovider")
}
})
t.Run("Already stopping", func(t *testing.T) {
ds := NewDeliverService(&Config{}).(*deliverServiceImpl)
ds.blockDeliverer = &blocksprovider.Deliverer{
DoneC: make(chan struct{}),
}
ds.channelID = "channel-id"
ds.Stop()
err := ds.StopDeliverForChannel()
require.EqualError(t, err, "block deliverer for channel `channel-id` is already stopped")
})
t.Run("Already stopped", func(t *testing.T) {
ds := NewDeliverService(&Config{}).(*deliverServiceImpl)
ds.blockDeliverer = &blocksprovider.Deliverer{
DoneC: make(chan struct{}),
}
ds.channelID = "channel-id"
ds.StopDeliverForChannel()
err := ds.StopDeliverForChannel()
require.EqualError(t, err, "block deliverer for channel `channel-id` is <nil>, can't stop delivery")
})
}
func TestStop(t *testing.T) {
ds := NewDeliverService(&Config{}).(*deliverServiceImpl)
ds.blockDeliverer = &blocksprovider.Deliverer{
DoneC: make(chan struct{}),
}
require.False(t, ds.stopping)
bpd := ds.blockDeliverer.(*blocksprovider.Deliverer)
select {
case <-bpd.DoneC:
require.Fail(t, "block providers should not be closed")
default:
}
ds.Stop()
require.True(t, ds.stopping)
select {
case <-bpd.DoneC:
default:
require.Fail(t, "block providers should te closed")
}
}
// TODO this pattern repeats itself in several places. Make it common in the 'genesisconfig' package to easily create
// Raft genesis blocks
func generateCertificates(t *testing.T, confAppRaft *genesisconfig.Profile, tlsCA tlsgen.CA, certDir string) {
for i, c := range confAppRaft.Orderer.EtcdRaft.Consenters {
srvC, err := tlsCA.NewServerCertKeyPair(c.Host)
require.NoError(t, err)
srvP := path.Join(certDir, fmt.Sprintf("server%d.crt", i))
err = ioutil.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 = ioutil.WriteFile(clnP, clnC.Cert, 0o644)
require.NoError(t, err)
c.ServerTlsCert = []byte(srvP)
c.ClientTlsCert = []byte(clnP)
}
}