github.com/true-sqn/fabric@v2.1.1+incompatible/orderer/consensus/etcdraft/util_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package etcdraft
     8  
     9  import (
    10  	"encoding/base64"
    11  	"io/ioutil"
    12  	"path/filepath"
    13  	"testing"
    14  
    15  	"github.com/golang/protobuf/proto"
    16  	"github.com/hyperledger/fabric-protos-go/common"
    17  	etcdraftproto "github.com/hyperledger/fabric-protos-go/orderer/etcdraft"
    18  	"github.com/hyperledger/fabric/bccsp/sw"
    19  	"github.com/hyperledger/fabric/common/crypto/tlsgen"
    20  	"github.com/hyperledger/fabric/orderer/common/cluster"
    21  	"github.com/hyperledger/fabric/protoutil"
    22  	"github.com/stretchr/testify/assert"
    23  )
    24  
    25  func TestIsConsenterOfChannel(t *testing.T) {
    26  	certInsideConfigBlock, err := base64.StdEncoding.DecodeString("LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNmekNDQWlhZ0F3SUJBZ0l" +
    27  		"SQUo4bjFLYTVzS1ZaTXRMTHJ1dldERDB3Q2dZSUtvWkl6ajBFQXdJd2JERUwKTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnVENrTmhiR" +
    28  		"2xtYjNKdWFXRXhGakFVQmdOVkJBY1REVk5oYmlCRwpjbUZ1WTJselkyOHhGREFTQmdOVkJBb1RDMlY0WVcxd2JHVXVZMjl0TVJvd0dBWUR" +
    29  		"WUVFERXhGMGJITmpZUzVsCmVHRnRjR3hsTG1OdmJUQWVGdzB4T0RFeE1EWXdPVFE1TURCYUZ3MHlPREV4TURNd09UUTVNREJhTUZreEN6QU" +
    30  		"oKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saE1SWXdGQVlEVlFRSEV3MVRZVzRnUm5KaApibU5wYzJOdk1SMHdH" +
    31  		"d1lEVlFRREV4UnZjbVJsY21WeU1TNWxlR0Z0Y0d4bExtTnZiVEJaTUJNR0J5cUdTTTQ5CkFnRUdDQ3FHU000OUF3RUhBMElBQkRUVlFZc0" +
    32  		"ZKZWxUcFZDMDFsek5DSkx6OENRMFFGVDBvN1BmSnBwSkl2SXgKUCtRVjQvRGRCSnRqQ0cvcGsvMGFxZXRpSjhZRUFMYmMrOUhmWnExN2tJ" +
    33  		"Q2pnYnN3Z2Jnd0RnWURWUjBQQVFILwpCQVFEQWdXZ01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUJCZ2dyQmdFRkJRY0RBakFNQmdOV" +
    34  		"khSTUJBZjhFCkFqQUFNQ3NHQTFVZEl3UWtNQ0tBSUVBOHFrSVJRTVBuWkxBR2g0TXZla2gzZFpHTmNxcEhZZWlXdzE3Rmw0ZlMKTUV3R0" +
    35  		"ExVWRFUVJGTUVPQ0ZHOXlaR1Z5WlhJeExtVjRZVzF3YkdVdVkyOXRnZ2h2Y21SbGNtVnlNWUlKYkc5agpZV3hvYjNOMGh3Ui9BQUFCaHh" +
    36  		"BQUFBQUFBQUFBQUFBQUFBQUFBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDCklFckJZRFVzV0JwOHB0ZVFSaTZyNjNVelhJQi81Sn" +
    37  		"YxK0RlTkRIUHc3aDljQWlCakYrM3V5TzBvMEdRclB4MEUKUWptYlI5T3BVREN2LzlEUkNXWU9GZitkVlE9PQotLS0tLUVORCBDRVJUSU" +
    38  		"ZJQ0FURS0tLS0tCg==")
    39  	assert.NoError(t, err)
    40  
    41  	validBlock := func() *common.Block {
    42  		b, err := ioutil.ReadFile(filepath.Join("testdata", "etcdraftgenesis.block"))
    43  		assert.NoError(t, err)
    44  		block := &common.Block{}
    45  		err = proto.Unmarshal(b, block)
    46  		assert.NoError(t, err)
    47  		return block
    48  	}
    49  	for _, testCase := range []struct {
    50  		name          string
    51  		expectedError string
    52  		configBlock   *common.Block
    53  		certificate   []byte
    54  	}{
    55  		{
    56  			name:          "nil block",
    57  			expectedError: "nil block",
    58  		},
    59  		{
    60  			name:          "no block data",
    61  			expectedError: "block data is nil",
    62  			configBlock:   &common.Block{},
    63  		},
    64  		{
    65  			name: "invalid envelope inside block",
    66  			expectedError: "failed to unmarshal payload from envelope:" +
    67  				" error unmarshaling Payload: proto: common.Payload: illegal tag 0 (wire type 1)",
    68  			configBlock: &common.Block{
    69  				Data: &common.BlockData{
    70  					Data: [][]byte{protoutil.MarshalOrPanic(&common.Envelope{
    71  						Payload: []byte{1, 2, 3},
    72  					})},
    73  				},
    74  			},
    75  		},
    76  		{
    77  			name:          "valid config block with cert mismatch",
    78  			configBlock:   validBlock(),
    79  			certificate:   certInsideConfigBlock[2:],
    80  			expectedError: cluster.ErrNotInChannel.Error(),
    81  		},
    82  		{
    83  			name:        "valid config block with matching cert",
    84  			configBlock: validBlock(),
    85  			certificate: certInsideConfigBlock,
    86  		},
    87  	} {
    88  		t.Run(testCase.name, func(t *testing.T) {
    89  			cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
    90  			assert.NoError(t, err)
    91  
    92  			consenterCertificate := &ConsenterCertificate{
    93  				ConsenterCertificate: testCase.certificate,
    94  				CryptoProvider:       cryptoProvider,
    95  			}
    96  			err = consenterCertificate.IsConsenterOfChannel(testCase.configBlock)
    97  			if testCase.expectedError != "" {
    98  				assert.EqualError(t, err, testCase.expectedError)
    99  			} else {
   100  				assert.NoError(t, err)
   101  			}
   102  		})
   103  	}
   104  }
   105  
   106  func TestCheckConfigMetadata(t *testing.T) {
   107  	tlsCA, err := tlsgen.NewCA()
   108  	if err != nil {
   109  		panic(err)
   110  	}
   111  	serverPair, err := tlsCA.NewServerCertKeyPair("localhost")
   112  	serverCert := serverPair.Cert
   113  	if err != nil {
   114  		panic(err)
   115  	}
   116  	clientPair, err := tlsCA.NewClientCertKeyPair()
   117  	clientCert := clientPair.Cert
   118  	if err != nil {
   119  		panic(err)
   120  	}
   121  	validOptions := &etcdraftproto.Options{
   122  		TickInterval:         "500ms",
   123  		ElectionTick:         10,
   124  		HeartbeatTick:        1,
   125  		MaxInflightBlocks:    5,
   126  		SnapshotIntervalSize: 20 * 1024 * 1024, // 20 MB
   127  	}
   128  	singleConsenter := &etcdraftproto.Consenter{
   129  		Host:          "host1",
   130  		Port:          10001,
   131  		ClientTlsCert: clientCert,
   132  		ServerTlsCert: serverCert,
   133  	}
   134  
   135  	// valid metadata should give nil error
   136  	goodMetadata := &etcdraftproto.ConfigMetadata{
   137  		Options: validOptions,
   138  		Consenters: []*etcdraftproto.Consenter{
   139  			singleConsenter,
   140  		},
   141  	}
   142  	assert.Nil(t, CheckConfigMetadata(goodMetadata))
   143  
   144  	// test variety of bad metadata
   145  	for _, testCase := range []struct {
   146  		description string
   147  		metadata    *etcdraftproto.ConfigMetadata
   148  		errRegex    string
   149  	}{
   150  		{
   151  			description: "nil metadata",
   152  			metadata:    nil,
   153  			errRegex:    "nil Raft config metadata",
   154  		},
   155  		{
   156  			description: "nil options",
   157  			metadata:    &etcdraftproto.ConfigMetadata{},
   158  			errRegex:    "nil Raft config metadata options",
   159  		},
   160  		{
   161  			description: "HeartbeatTick is 0",
   162  			metadata: &etcdraftproto.ConfigMetadata{
   163  				Options: &etcdraftproto.Options{
   164  					HeartbeatTick: 0,
   165  				},
   166  			},
   167  			errRegex: "none of HeartbeatTick .* can be zero",
   168  		},
   169  		{
   170  			description: "ElectionTick is 0",
   171  			metadata: &etcdraftproto.ConfigMetadata{
   172  				Options: &etcdraftproto.Options{
   173  					HeartbeatTick: validOptions.HeartbeatTick,
   174  					ElectionTick:  0,
   175  				},
   176  			},
   177  			errRegex: "none of .* ElectionTick .* can be zero",
   178  		},
   179  		{
   180  			description: "MaxInflightBlocks is 0",
   181  			metadata: &etcdraftproto.ConfigMetadata{
   182  				Options: &etcdraftproto.Options{
   183  					HeartbeatTick:     validOptions.HeartbeatTick,
   184  					ElectionTick:      validOptions.ElectionTick,
   185  					MaxInflightBlocks: 0,
   186  				},
   187  			},
   188  			errRegex: "none of .* MaxInflightBlocks .* can be zero",
   189  		},
   190  		{
   191  			description: "ElectionTick is less than HeartbeatTick",
   192  			metadata: &etcdraftproto.ConfigMetadata{
   193  				Options: &etcdraftproto.Options{
   194  					HeartbeatTick:     10,
   195  					ElectionTick:      1,
   196  					MaxInflightBlocks: validOptions.MaxInflightBlocks,
   197  				},
   198  			},
   199  			errRegex: "ElectionTick .* must be greater than HeartbeatTick",
   200  		},
   201  		{
   202  			description: "TickInterval is not parsable",
   203  			metadata: &etcdraftproto.ConfigMetadata{
   204  				Options: &etcdraftproto.Options{
   205  					HeartbeatTick:     validOptions.HeartbeatTick,
   206  					ElectionTick:      validOptions.ElectionTick,
   207  					MaxInflightBlocks: validOptions.MaxInflightBlocks,
   208  					TickInterval:      "abcd",
   209  				},
   210  			},
   211  			errRegex: "failed to parse TickInterval .* to time duration",
   212  		},
   213  		{
   214  			description: "TickInterval is 0",
   215  			metadata: &etcdraftproto.ConfigMetadata{
   216  				Options: &etcdraftproto.Options{
   217  					HeartbeatTick:     validOptions.HeartbeatTick,
   218  					ElectionTick:      validOptions.ElectionTick,
   219  					MaxInflightBlocks: validOptions.MaxInflightBlocks,
   220  					TickInterval:      "0s",
   221  				},
   222  			},
   223  			errRegex: "TickInterval cannot be zero",
   224  		},
   225  		{
   226  			description: "consenter set is empty",
   227  			metadata: &etcdraftproto.ConfigMetadata{
   228  				Options:    validOptions,
   229  				Consenters: []*etcdraftproto.Consenter{},
   230  			},
   231  			errRegex: "empty consenter set",
   232  		},
   233  		{
   234  			description: "metadata has nil consenter",
   235  			metadata: &etcdraftproto.ConfigMetadata{
   236  				Options: validOptions,
   237  				Consenters: []*etcdraftproto.Consenter{
   238  					nil,
   239  				},
   240  			},
   241  			errRegex: "metadata has nil consenter",
   242  		},
   243  		{
   244  			description: "consenter has invalid server cert",
   245  			metadata: &etcdraftproto.ConfigMetadata{
   246  				Options: validOptions,
   247  				Consenters: []*etcdraftproto.Consenter{
   248  					{
   249  						ServerTlsCert: []byte("invalid"),
   250  						ClientTlsCert: clientCert,
   251  					},
   252  				},
   253  			},
   254  			errRegex: "server TLS certificate is not PEM encoded",
   255  		},
   256  		{
   257  			description: "consenter has invalid client cert",
   258  			metadata: &etcdraftproto.ConfigMetadata{
   259  				Options: validOptions,
   260  				Consenters: []*etcdraftproto.Consenter{
   261  					{
   262  						ServerTlsCert: serverCert,
   263  						ClientTlsCert: []byte("invalid"),
   264  					},
   265  				},
   266  			},
   267  			errRegex: "client TLS certificate is not PEM encoded",
   268  		},
   269  		{
   270  			description: "metadata has duplicate consenters",
   271  			metadata: &etcdraftproto.ConfigMetadata{
   272  				Options: validOptions,
   273  				Consenters: []*etcdraftproto.Consenter{
   274  					singleConsenter,
   275  					singleConsenter,
   276  				},
   277  			},
   278  			errRegex: "duplicate consenter",
   279  		},
   280  	} {
   281  		err := CheckConfigMetadata(testCase.metadata)
   282  		assert.NotNil(t, err, testCase.description)
   283  		assert.Regexp(t, testCase.errRegex, err)
   284  	}
   285  }