go_study/fabric-main/internal/peer/channel/create_test.go

687 lines
17 KiB
Go

/*
Copyright Digital Asset Holdings, LLC All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package channel
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"regexp"
"sync"
"testing"
"time"
"github.com/golang/protobuf/proto"
cb "github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric-protos-go/orderer"
"github.com/hyperledger/fabric/core/config/configtest"
"github.com/hyperledger/fabric/internal/peer/common"
"github.com/hyperledger/fabric/internal/peer/common/mock"
"github.com/hyperledger/fabric/internal/pkg/identity"
msptesttools "github.com/hyperledger/fabric/msp/mgmt/testtools"
"github.com/hyperledger/fabric/protoutil"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
)
//go:generate counterfeiter -o mock/signer_serializer.go --fake-name SignerSerializer . signerSerializer
type signerSerializer interface {
identity.SignerSerializer
}
type timeoutOrderer struct {
counter int
net.Listener
*grpc.Server
nextExpectedSeek uint64
t *testing.T
blockChannel chan uint64
}
func newOrderer(port int, t *testing.T) *timeoutOrderer {
srv := grpc.NewServer()
lsnr, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
if err != nil {
panic(err)
}
o := &timeoutOrderer{
Server: srv,
Listener: lsnr,
t: t,
nextExpectedSeek: uint64(1),
blockChannel: make(chan uint64, 1),
counter: int(1),
}
orderer.RegisterAtomicBroadcastServer(srv, o)
go srv.Serve(lsnr)
return o
}
func (o *timeoutOrderer) Shutdown() {
o.Server.Stop()
o.Listener.Close()
}
func (*timeoutOrderer) Broadcast(orderer.AtomicBroadcast_BroadcastServer) error {
panic("Should not have been called")
}
func (o *timeoutOrderer) SendBlock(seq uint64) {
o.blockChannel <- seq
}
func (o *timeoutOrderer) Deliver(stream orderer.AtomicBroadcast_DeliverServer) error {
o.timeoutIncrement()
if o.counter > 5 {
o.sendBlock(stream, 0)
}
return nil
}
func (o *timeoutOrderer) sendBlock(stream orderer.AtomicBroadcast_DeliverServer, seq uint64) {
block := &cb.Block{
Header: &cb.BlockHeader{
Number: seq,
},
}
stream.Send(&orderer.DeliverResponse{
Type: &orderer.DeliverResponse_Block{Block: block},
})
}
func (o *timeoutOrderer) timeoutIncrement() {
o.counter++
}
var once sync.Once
// / mock deliver client for UT
type mockDeliverClient struct {
err error
}
func (m *mockDeliverClient) readBlock() (*cb.Block, error) {
if m.err != nil {
return nil, m.err
}
return &cb.Block{}, nil
}
func (m *mockDeliverClient) GetSpecifiedBlock(num uint64) (*cb.Block, error) {
return m.readBlock()
}
func (m *mockDeliverClient) GetOldestBlock() (*cb.Block, error) {
return m.readBlock()
}
func (m *mockDeliverClient) GetNewestBlock() (*cb.Block, error) {
return m.readBlock()
}
func (m *mockDeliverClient) Close() error {
return nil
}
// InitMSP init MSP
func InitMSP() {
once.Do(initMSP)
}
func initMSP() {
err := msptesttools.LoadMSPSetupForTesting()
if err != nil {
panic(fmt.Errorf("Fatal error when reading MSP config: err %s", err))
}
}
func mockBroadcastClientFactory() (common.BroadcastClient, error) {
return common.GetMockBroadcastClient(nil), nil
}
func TestCreateChain(t *testing.T) {
defer resetFlags()
InitMSP()
configtest.SetDevFabricConfigPath(t)
mockchain := "mockchain"
defer os.Remove(mockchain + ".block")
signer, err := common.GetDefaultSigner()
if err != nil {
t.Fatalf("Get default signer error: %v", err)
}
mockCF := &ChannelCmdFactory{
BroadcastFactory: mockBroadcastClientFactory,
Signer: signer,
DeliverClient: &mockDeliverClient{},
}
cmd := createCmd(mockCF)
AddFlags(cmd)
args := []string{"-c", mockchain, "-o", "localhost:7050"}
cmd.SetArgs(args)
if err := cmd.Execute(); err != nil {
t.Fail()
t.Errorf("expected create command to succeed")
}
filename := mockchain + ".block"
if _, err := os.Stat(filename); err != nil {
t.Fail()
t.Errorf("expected %s to exist", filename)
}
}
func TestCreateChainWithOutputBlock(t *testing.T) {
defer resetFlags()
InitMSP()
configtest.SetDevFabricConfigPath(t)
mockchain := "mockchain"
signer, err := common.GetDefaultSigner()
if err != nil {
t.Fatalf("Get default signer error: %v", err)
}
mockCF := &ChannelCmdFactory{
BroadcastFactory: mockBroadcastClientFactory,
Signer: signer,
DeliverClient: &mockDeliverClient{},
}
cmd := createCmd(mockCF)
AddFlags(cmd)
tempDir := t.TempDir()
outputBlockPath := filepath.Join(tempDir, "output.block")
args := []string{"-c", mockchain, "-o", "localhost:7050", "--outputBlock", outputBlockPath}
cmd.SetArgs(args)
defer func() { outputBlock = "" }()
err = cmd.Execute()
require.NoError(t, err, "execute should succeed")
_, err = os.Stat(outputBlockPath)
require.NoErrorf(t, err, "expected %s to exist", outputBlockPath)
}
func TestCreateChainWithDefaultAnchorPeers(t *testing.T) {
defer resetFlags()
InitMSP()
configtest.SetDevFabricConfigPath(t)
mockchain := "mockchain"
defer os.Remove(mockchain + ".block")
signer, err := common.GetDefaultSigner()
if err != nil {
t.Fatalf("Get default signer error: %v", err)
}
mockCF := &ChannelCmdFactory{
BroadcastFactory: mockBroadcastClientFactory,
Signer: signer,
DeliverClient: &mockDeliverClient{},
}
cmd := createCmd(mockCF)
AddFlags(cmd)
args := []string{"-c", mockchain, "-o", "localhost:7050"}
cmd.SetArgs(args)
if err := cmd.Execute(); err != nil {
t.Fail()
t.Errorf("expected create command to succeed")
}
}
func TestCreateChainWithWaitSuccess(t *testing.T) {
defer resetFlags()
InitMSP()
configtest.SetDevFabricConfigPath(t)
mockchain := "mockchain"
signer, err := common.GetDefaultSigner()
if err != nil {
t.Fatalf("Get default signer error: %v", err)
}
mockCF := &ChannelCmdFactory{
BroadcastFactory: mockBroadcastClientFactory,
Signer: signer,
DeliverClient: &mockDeliverClient{err: nil},
}
fakeOrderer := newOrderer(8101, t)
defer fakeOrderer.Shutdown()
cmd := createCmd(mockCF)
AddFlags(cmd)
args := []string{"-c", mockchain, "-o", "localhost:8101", "-t", "10s"}
cmd.SetArgs(args)
if err := cmd.Execute(); err != nil {
t.Fail()
t.Errorf("expected create command to succeed")
}
}
func TestCreateChainWithTimeoutErr(t *testing.T) {
defer viper.Reset()
defer resetFlags()
InitMSP()
configtest.SetDevFabricConfigPath(t)
mockchain := "mockchain"
signer, err := common.GetDefaultSigner()
if err != nil {
t.Fatalf("Get default signer error: %v", err)
}
mockCF := &ChannelCmdFactory{
BroadcastFactory: mockBroadcastClientFactory,
Signer: signer,
DeliverClient: &mockDeliverClient{err: errors.New("bobsled")},
}
fakeOrderer := newOrderer(8102, t)
defer fakeOrderer.Shutdown()
// failure - connects to orderer but times out waiting for channel to
// be created
cmd := createCmd(mockCF)
AddFlags(cmd)
channelCmd.AddCommand(cmd)
args := []string{"create", "-c", mockchain, "-o", "localhost:8102", "-t", "10ms", "--connTimeout", "1s"}
channelCmd.SetArgs(args)
if err := channelCmd.Execute(); err == nil {
t.Error("expected create chain to fail with deliver error")
} else {
require.Contains(t, err.Error(), "timeout waiting for channel creation")
}
// failure - point to bad port and time out connecting to orderer
args = []string{"create", "-c", mockchain, "-o", "localhost:0", "-t", "1s", "--connTimeout", "10ms"}
channelCmd.SetArgs(args)
if err := channelCmd.Execute(); err == nil {
t.Error("expected create chain to fail with deliver error")
} else {
require.Contains(t, err.Error(), "failed connecting")
}
}
func TestCreateChainBCFail(t *testing.T) {
defer resetFlags()
InitMSP()
configtest.SetDevFabricConfigPath(t)
mockchain := "mockchain"
defer os.Remove(mockchain + ".block")
signer, err := common.GetDefaultSigner()
if err != nil {
t.Fatalf("Get default signer error: %v", err)
}
sendErr := errors.New("luge")
mockCF := &ChannelCmdFactory{
BroadcastFactory: func() (common.BroadcastClient, error) {
return common.GetMockBroadcastClient(sendErr), nil
},
Signer: signer,
DeliverClient: &mockDeliverClient{},
}
cmd := createCmd(mockCF)
AddFlags(cmd)
args := []string{"-c", mockchain, "-o", "localhost:7050"}
cmd.SetArgs(args)
expectedErrMsg := sendErr.Error()
if err := cmd.Execute(); err == nil {
t.Error("expected create chain to fail with broadcast error")
} else {
if err.Error() != expectedErrMsg {
t.Errorf("Run create chain get unexpected error: %s(expected %s)", err.Error(), expectedErrMsg)
}
}
}
func TestCreateChainDeliverFail(t *testing.T) {
defer resetFlags()
InitMSP()
configtest.SetDevFabricConfigPath(t)
mockchain := "mockchain"
defer os.Remove(mockchain + ".block")
signer, err := common.GetDefaultSigner()
if err != nil {
t.Fatalf("Get default signer error: %v", err)
}
sendErr := fmt.Errorf("skeleton")
mockCF := &ChannelCmdFactory{
BroadcastFactory: func() (common.BroadcastClient, error) {
return common.GetMockBroadcastClient(sendErr), nil
},
Signer: signer,
DeliverClient: &mockDeliverClient{err: sendErr},
}
cmd := createCmd(mockCF)
AddFlags(cmd)
args := []string{"-c", mockchain, "-o", "localhost:7050"}
cmd.SetArgs(args)
expectedErrMsg := sendErr.Error()
if err := cmd.Execute(); err == nil {
t.Errorf("expected create chain to fail with deliver error")
} else {
if err.Error() != expectedErrMsg {
t.Errorf("Run create chain get unexpected error: %s(expected %s)", err.Error(), expectedErrMsg)
}
}
}
func createTxFile(filename string, typ cb.HeaderType, channelID string) (*cb.Envelope, error) {
ch := &cb.ChannelHeader{Type: int32(typ), ChannelId: channelID}
data, err := proto.Marshal(ch)
if err != nil {
return nil, err
}
p := &cb.Payload{Header: &cb.Header{ChannelHeader: data}}
data, err = proto.Marshal(p)
if err != nil {
return nil, err
}
env := &cb.Envelope{Payload: data}
data, err = proto.Marshal(env)
if err != nil {
return nil, err
}
if err = ioutil.WriteFile(filename, data, 0o644); err != nil {
return nil, err
}
return env, nil
}
func TestCreateChainFromTx(t *testing.T) {
defer resetFlags()
InitMSP()
configtest.SetDevFabricConfigPath(t)
mockchannel := "mockchannel"
dir := t.TempDir()
// this could be created by the create command
defer os.Remove(mockchannel + ".block")
file := filepath.Join(dir, mockchannel)
signer, err := common.GetDefaultSigner()
if err != nil {
t.Fatalf("Get default signer error: %v", err)
}
mockCF := &ChannelCmdFactory{
BroadcastFactory: mockBroadcastClientFactory,
Signer: signer,
DeliverClient: &mockDeliverClient{},
}
cmd := createCmd(mockCF)
AddFlags(cmd)
// Error case 0
args := []string{"-c", "", "-f", file, "-o", "localhost:7050"}
cmd.SetArgs(args)
err = cmd.Execute()
require.Error(t, err, "Create command should have failed because channel ID is not specified")
require.Contains(t, err.Error(), "must supply channel ID")
// Error case 1
args = []string{"-c", mockchannel, "-f", file, "-o", "localhost:7050"}
cmd.SetArgs(args)
err = cmd.Execute()
require.Error(t, err, "Create command should have failed because tx file does not exist")
msgExpr := regexp.MustCompile(`channel create configuration tx file not found.*no such file or directory`)
require.True(t, msgExpr.MatchString(err.Error()))
// Success case: -f option is empty
args = []string{"-c", mockchannel, "-f", "", "-o", "localhost:7050"}
cmd.SetArgs(args)
err = cmd.Execute()
require.NoError(t, err)
// Success case
args = []string{"-c", mockchannel, "-f", file, "-o", "localhost:7050"}
cmd.SetArgs(args)
_, err = createTxFile(file, cb.HeaderType_CONFIG_UPDATE, mockchannel)
require.NoError(t, err, "Couldn't create tx file")
err = cmd.Execute()
require.NoError(t, err)
}
func TestCreateChainInvalidTx(t *testing.T) {
defer resetFlags()
InitMSP()
configtest.SetDevFabricConfigPath(t)
mockchannel := "mockchannel"
dir := t.TempDir()
// this is created by create command
defer os.Remove(mockchannel + ".block")
file := filepath.Join(dir, mockchannel)
signer, err := common.GetDefaultSigner()
if err != nil {
t.Fatalf("Get default signer error: %v", err)
}
mockCF := &ChannelCmdFactory{
BroadcastFactory: mockBroadcastClientFactory,
Signer: signer,
DeliverClient: &mockDeliverClient{},
}
cmd := createCmd(mockCF)
AddFlags(cmd)
args := []string{"-c", mockchannel, "-f", file, "-o", "localhost:7050"}
cmd.SetArgs(args)
// bad type CONFIG
if _, err = createTxFile(file, cb.HeaderType_CONFIG, mockchannel); err != nil {
t.Fatalf("couldn't create tx file")
}
defer os.Remove(file)
if err = cmd.Execute(); err == nil {
t.Errorf("expected error")
} else if _, ok := err.(InvalidCreateTx); !ok {
t.Errorf("invalid error")
}
// bad channel name - does not match one specified in command
if _, err = createTxFile(file, cb.HeaderType_CONFIG_UPDATE, "different_channel"); err != nil {
t.Fatalf("couldn't create tx file")
}
if err = cmd.Execute(); err == nil {
t.Errorf("expected error")
} else if _, ok := err.(InvalidCreateTx); !ok {
t.Errorf("invalid error")
}
// empty channel
if _, err = createTxFile(file, cb.HeaderType_CONFIG_UPDATE, ""); err != nil {
t.Fatalf("couldn't create tx file")
}
if err := cmd.Execute(); err == nil {
t.Errorf("expected error")
} else if _, ok := err.(InvalidCreateTx); !ok {
t.Errorf("invalid error")
}
}
func TestCreateChainNilCF(t *testing.T) {
defer viper.Reset()
defer resetFlags()
InitMSP()
configtest.SetDevFabricConfigPath(t)
mockchannel := "mockchannel"
dir := t.TempDir()
// this is created by create command
defer os.Remove(mockchannel + ".block")
file := filepath.Join(dir, mockchannel)
// Error case: grpc error
viper.Set("orderer.client.connTimeout", 10*time.Millisecond)
cmd := createCmd(nil)
AddFlags(cmd)
args := []string{"-c", mockchannel, "-f", file, "-o", "localhost:7050"}
cmd.SetArgs(args)
err := cmd.Execute()
require.Error(t, err)
require.Contains(t, err.Error(), "failed to create deliver client")
// Error case: invalid ordering service endpoint
args = []string{"-c", mockchannel, "-f", file, "-o", "localhost"}
cmd.SetArgs(args)
err = cmd.Execute()
require.Error(t, err)
require.Contains(t, err.Error(), "ordering service endpoint localhost is not valid or missing")
// Error case: invalid ca file
defer os.RemoveAll(dir) // clean up
channelCmd.AddCommand(cmd)
args = []string{"create", "-c", mockchannel, "-f", file, "-o", "localhost:7050", "--tls", "true", "--cafile", dir + "/ca.pem"}
channelCmd.SetArgs(args)
err = channelCmd.Execute()
require.Error(t, err)
t.Log(err)
require.Contains(t, err.Error(), "no such file or directory")
}
func TestSanityCheckAndSignChannelCreateTx(t *testing.T) {
defer resetFlags()
signer := &mock.SignerSerializer{}
env := &cb.Envelope{}
env.Payload = make([]byte, 10)
var err error
// Error case 1
_, err = sanityCheckAndSignConfigTx(env, signer)
require.Error(t, err, "Error expected for nil payload")
require.Contains(t, err.Error(), "bad payload")
// Error case 2
p := &cb.Payload{Header: nil}
data, err1 := proto.Marshal(p)
require.NoError(t, err1)
env = &cb.Envelope{Payload: data}
_, err = sanityCheckAndSignConfigTx(env, signer)
require.Error(t, err, "Error expected for bad payload header")
require.Contains(t, err.Error(), "bad header")
// Error case 3
bites := bytes.NewBufferString("foo").Bytes()
p = &cb.Payload{Header: &cb.Header{ChannelHeader: bites}}
data, err = proto.Marshal(p)
require.NoError(t, err)
env = &cb.Envelope{Payload: data}
_, err = sanityCheckAndSignConfigTx(env, signer)
require.Error(t, err, "Error expected for bad channel header")
require.Contains(t, err.Error(), "could not unmarshall channel header")
// Error case 4
mockchannel := "mockchannel"
cid := channelID
channelID = mockchannel
defer func() {
channelID = cid
}()
ch := &cb.ChannelHeader{Type: int32(cb.HeaderType_CONFIG_UPDATE), ChannelId: mockchannel}
data, err = proto.Marshal(ch)
require.NoError(t, err)
p = &cb.Payload{Header: &cb.Header{ChannelHeader: data}, Data: bytes.NewBufferString("foo").Bytes()}
data, err = proto.Marshal(p)
require.NoError(t, err)
env = &cb.Envelope{Payload: data}
_, err = sanityCheckAndSignConfigTx(env, signer)
require.Error(t, err, "Error expected for bad payload data")
require.Contains(t, err.Error(), "Bad config update env")
// Error case 5
signer = &mock.SignerSerializer{}
signer.SerializeReturns(nil, errors.New("bad signer header"))
env, err = protoutil.CreateSignedEnvelope(
cb.HeaderType_CONFIG_UPDATE,
"mockchannel",
nil,
&cb.ConfigEnvelope{},
0,
0)
require.NoError(t, err)
_, err = sanityCheckAndSignConfigTx(env, signer)
require.EqualError(t, err, "bad signer header")
// Error case 6
signer = &mock.SignerSerializer{}
signer.SignReturns(nil, errors.New("signer failed to sign"))
env, err = protoutil.CreateSignedEnvelope(
cb.HeaderType_CONFIG_UPDATE,
"mockchannel",
nil,
&cb.ConfigEnvelope{},
0,
0)
require.NoError(t, err)
_, err = sanityCheckAndSignConfigTx(env, signer)
require.EqualError(t, err, "signer failed to sign")
}