github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/protoutil/blockutils_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package protoutil_test
     8  
     9  import (
    10  	"crypto/sha256"
    11  	"encoding/asn1"
    12  	"math"
    13  	"testing"
    14  
    15  	"github.com/golang/protobuf/proto"
    16  	"github.com/hyperledger/fabric-protos-go/common"
    17  	cb "github.com/hyperledger/fabric-protos-go/common"
    18  	configtxtest "github.com/osdi23p228/fabric/common/configtx/test"
    19  	"github.com/osdi23p228/fabric/protoutil"
    20  	"github.com/stretchr/testify/assert"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  var testChannelID = "myuniquetestchainid"
    25  
    26  func TestNewBlock(t *testing.T) {
    27  	var block *cb.Block
    28  	assert.Nil(t, block.GetHeader())
    29  	assert.Nil(t, block.GetData())
    30  	assert.Nil(t, block.GetMetadata())
    31  
    32  	data := &cb.BlockData{
    33  		Data: [][]byte{{0, 1, 2}},
    34  	}
    35  	block = protoutil.NewBlock(uint64(0), []byte("datahash"))
    36  	assert.Equal(t, []byte("datahash"), block.Header.PreviousHash, "Incorrect previous hash")
    37  	assert.NotNil(t, block.GetData())
    38  	assert.NotNil(t, block.GetMetadata())
    39  	block.GetHeader().DataHash = protoutil.BlockDataHash(data)
    40  
    41  	asn1Bytes, err := asn1.Marshal(struct {
    42  		Number       int64
    43  		PreviousHash []byte
    44  		DataHash     []byte
    45  	}{
    46  		Number:       0,
    47  		DataHash:     protoutil.BlockDataHash(data),
    48  		PreviousHash: []byte("datahash"),
    49  	})
    50  	headerHash := sha256.Sum256(asn1Bytes)
    51  	assert.NoError(t, err)
    52  	assert.Equal(t, asn1Bytes, protoutil.BlockHeaderBytes(block.Header), "Incorrect marshaled blockheader bytes")
    53  	assert.Equal(t, headerHash[:], protoutil.BlockHeaderHash(block.Header), "Incorrect blockheader hash")
    54  }
    55  
    56  func TestGoodBlockHeaderBytes(t *testing.T) {
    57  	goodBlockHeader := &common.BlockHeader{
    58  		Number:       1,
    59  		PreviousHash: []byte("foo"),
    60  		DataHash:     []byte("bar"),
    61  	}
    62  
    63  	_ = protoutil.BlockHeaderBytes(goodBlockHeader) // Should not panic
    64  
    65  	goodBlockHeaderMaxNumber := &common.BlockHeader{
    66  		Number:       math.MaxUint64,
    67  		PreviousHash: []byte("foo"),
    68  		DataHash:     []byte("bar"),
    69  	}
    70  
    71  	_ = protoutil.BlockHeaderBytes(goodBlockHeaderMaxNumber) // Should not panic
    72  }
    73  
    74  func TestGetChannelIDFromBlockBytes(t *testing.T) {
    75  	gb, err := configtxtest.MakeGenesisBlock(testChannelID)
    76  	assert.NoError(t, err, "Failed to create test configuration block")
    77  	bytes, err := proto.Marshal(gb)
    78  	assert.NoError(t, err)
    79  	cid, err := protoutil.GetChannelIDFromBlockBytes(bytes)
    80  	assert.NoError(t, err)
    81  	assert.Equal(t, testChannelID, cid, "Failed to return expected chain ID")
    82  
    83  	// bad block bytes
    84  	_, err = protoutil.GetChannelIDFromBlockBytes([]byte("bad block"))
    85  	assert.Error(t, err, "Expected error with malformed block bytes")
    86  }
    87  
    88  func TestGetChannelIDFromBlock(t *testing.T) {
    89  	var err error
    90  	var gb *common.Block
    91  	var cid string
    92  
    93  	// nil block
    94  	_, err = protoutil.GetChannelIDFromBlock(gb)
    95  	assert.Error(t, err, "Expected error getting channel id from nil block")
    96  
    97  	gb, err = configtxtest.MakeGenesisBlock(testChannelID)
    98  	assert.NoError(t, err, "Failed to create test configuration block")
    99  
   100  	cid, err = protoutil.GetChannelIDFromBlock(gb)
   101  	assert.NoError(t, err, "Failed to get chain ID from block")
   102  	assert.Equal(t, testChannelID, cid, "Failed to return expected chain ID")
   103  
   104  	// missing data
   105  	badBlock := gb
   106  	badBlock.Data = nil
   107  	_, err = protoutil.GetChannelIDFromBlock(badBlock)
   108  	assert.Error(t, err, "Expected error with missing block data")
   109  
   110  	// no envelope
   111  	badBlock = &cb.Block{
   112  		Data: &cb.BlockData{
   113  			Data: [][]byte{[]byte("bad envelope")},
   114  		},
   115  	}
   116  	_, err = protoutil.GetChannelIDFromBlock(badBlock)
   117  	assert.Error(t, err, "Expected error with no envelope in data")
   118  
   119  	// bad payload
   120  	env, _ := proto.Marshal(&cb.Envelope{
   121  		Payload: []byte("bad payload"),
   122  	})
   123  	badBlock = &cb.Block{
   124  		Data: &cb.BlockData{
   125  			Data: [][]byte{env},
   126  		},
   127  	}
   128  	_, err = protoutil.GetChannelIDFromBlock(badBlock)
   129  	assert.Error(t, err, "Expected error - malformed payload")
   130  
   131  	// bad channel header
   132  	payload, _ := proto.Marshal(&cb.Payload{
   133  		Header: &cb.Header{
   134  			ChannelHeader: []byte("bad header"),
   135  		},
   136  	})
   137  	env, _ = proto.Marshal(&cb.Envelope{
   138  		Payload: payload,
   139  	})
   140  	badBlock = &cb.Block{
   141  		Data: &cb.BlockData{
   142  			Data: [][]byte{env},
   143  		},
   144  	}
   145  	_, err = protoutil.GetChannelIDFromBlock(badBlock)
   146  	assert.Error(t, err, "Expected error with malformed channel header")
   147  
   148  	// nil payload header
   149  	payload, _ = proto.Marshal(&cb.Payload{})
   150  	env, _ = proto.Marshal(&cb.Envelope{
   151  		Payload: payload,
   152  	})
   153  	badBlock = &cb.Block{
   154  		Data: &cb.BlockData{
   155  			Data: [][]byte{env},
   156  		},
   157  	}
   158  	_, err = protoutil.GetChannelIDFromBlock(badBlock)
   159  	assert.Error(t, err, "Expected error when payload header is nil")
   160  }
   161  
   162  func TestGetBlockFromBlockBytes(t *testing.T) {
   163  	testChainID := "myuniquetestchainid"
   164  	gb, err := configtxtest.MakeGenesisBlock(testChainID)
   165  	assert.NoError(t, err, "Failed to create test configuration block")
   166  	blockBytes, err := protoutil.Marshal(gb)
   167  	assert.NoError(t, err, "Failed to marshal block")
   168  	_, err = protoutil.UnmarshalBlock(blockBytes)
   169  	assert.NoError(t, err, "to get block from block bytes")
   170  
   171  	// bad block bytes
   172  	_, err = protoutil.UnmarshalBlock([]byte("bad block"))
   173  	assert.Error(t, err, "Expected error for malformed block bytes")
   174  }
   175  
   176  func TestGetMetadataFromBlock(t *testing.T) {
   177  	t.Run("new block", func(t *testing.T) {
   178  		block := protoutil.NewBlock(0, nil)
   179  		md, err := protoutil.GetMetadataFromBlock(block, cb.BlockMetadataIndex_ORDERER)
   180  		assert.NoError(t, err, "Unexpected error extracting metadata from new block")
   181  		assert.Nil(t, md.Value, "Expected metadata field value to be nil")
   182  		assert.Equal(t, 0, len(md.Value), "Expected length of metadata field value to be 0")
   183  		md = protoutil.GetMetadataFromBlockOrPanic(block, cb.BlockMetadataIndex_ORDERER)
   184  		assert.NotNil(t, md, "Expected to get metadata from block")
   185  	})
   186  	t.Run("no metadata", func(t *testing.T) {
   187  		block := protoutil.NewBlock(0, nil)
   188  		block.Metadata = nil
   189  		_, err := protoutil.GetMetadataFromBlock(block, cb.BlockMetadataIndex_ORDERER)
   190  		assert.Error(t, err, "Expected error with nil metadata")
   191  		assert.Contains(t, err.Error(), "no metadata in block")
   192  	})
   193  	t.Run("no metadata at index", func(t *testing.T) {
   194  		block := protoutil.NewBlock(0, nil)
   195  		block.Metadata.Metadata = [][]byte{{1, 2, 3}}
   196  		_, err := protoutil.GetMetadataFromBlock(block, cb.BlockMetadataIndex_LAST_CONFIG)
   197  		assert.Error(t, err, "Expected error with nil metadata")
   198  		assert.Contains(t, err.Error(), "no metadata at index")
   199  	})
   200  	t.Run("malformed metadata", func(t *testing.T) {
   201  		block := protoutil.NewBlock(0, nil)
   202  		block.Metadata.Metadata[cb.BlockMetadataIndex_ORDERER] = []byte("bad metadata")
   203  		_, err := protoutil.GetMetadataFromBlock(block, cb.BlockMetadataIndex_ORDERER)
   204  		assert.Error(t, err, "Expected error with malformed metadata")
   205  		assert.Contains(t, err.Error(), "error unmarshaling metadata at index [ORDERER]")
   206  		assert.Panics(t, func() {
   207  			_ = protoutil.GetMetadataFromBlockOrPanic(block, cb.BlockMetadataIndex_ORDERER)
   208  		}, "Expected panic with malformed metadata")
   209  	})
   210  }
   211  
   212  func TestGetConsenterMetadataFromBlock(t *testing.T) {
   213  	var cases = []struct {
   214  		name       string
   215  		value      []byte
   216  		signatures []byte
   217  		orderer    []byte
   218  		pass       bool
   219  	}{
   220  		{
   221  			name:       "empty",
   222  			value:      nil,
   223  			signatures: nil,
   224  			orderer:    nil,
   225  			pass:       true,
   226  		},
   227  		{
   228  			name:  "signature only",
   229  			value: []byte("hello"),
   230  			signatures: protoutil.MarshalOrPanic(&cb.Metadata{
   231  				Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
   232  					ConsenterMetadata: protoutil.MarshalOrPanic(&cb.Metadata{Value: []byte("hello")}),
   233  				}),
   234  			}),
   235  			orderer: nil,
   236  			pass:    true,
   237  		},
   238  		{
   239  			name:       "orderer only",
   240  			value:      []byte("hello"),
   241  			signatures: nil,
   242  			orderer:    protoutil.MarshalOrPanic(&cb.Metadata{Value: []byte("hello")}),
   243  			pass:       true,
   244  		},
   245  		{
   246  			name:  "both signatures and orderer",
   247  			value: []byte("hello"),
   248  			signatures: protoutil.MarshalOrPanic(&cb.Metadata{
   249  				Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
   250  					ConsenterMetadata: protoutil.MarshalOrPanic(&cb.Metadata{Value: []byte("hello")}),
   251  				}),
   252  			}),
   253  			orderer: protoutil.MarshalOrPanic(&cb.Metadata{Value: []byte("hello")}),
   254  			pass:    true,
   255  		},
   256  		{
   257  			name:       "malformed OrdererBlockMetadata",
   258  			signatures: protoutil.MarshalOrPanic(&cb.Metadata{Value: []byte("malformed")}),
   259  			orderer:    nil,
   260  			pass:       false,
   261  		},
   262  	}
   263  
   264  	for _, test := range cases {
   265  		block := protoutil.NewBlock(0, nil)
   266  		block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = test.signatures
   267  		block.Metadata.Metadata[cb.BlockMetadataIndex_ORDERER] = test.orderer
   268  		result, err := protoutil.GetConsenterMetadataFromBlock(block)
   269  
   270  		if test.pass {
   271  			require.NoError(t, err)
   272  			require.Equal(t, result.Value, test.value)
   273  		} else {
   274  			require.Error(t, err)
   275  		}
   276  	}
   277  }
   278  
   279  func TestInitBlockMeta(t *testing.T) {
   280  	// block with no metadata
   281  	block := &cb.Block{}
   282  	protoutil.InitBlockMetadata(block)
   283  	// should have 3 entries
   284  	assert.Equal(t, 5, len(block.Metadata.Metadata), "Expected block to have 5 metadata entries")
   285  
   286  	// block with a single entry
   287  	block = &cb.Block{
   288  		Metadata: &cb.BlockMetadata{},
   289  	}
   290  	block.Metadata.Metadata = append(block.Metadata.Metadata, []byte{})
   291  	protoutil.InitBlockMetadata(block)
   292  	// should have 3 entries
   293  	assert.Equal(t, 5, len(block.Metadata.Metadata), "Expected block to have 5 metadata entries")
   294  }
   295  
   296  func TestCopyBlockMetadata(t *testing.T) {
   297  	srcBlock := protoutil.NewBlock(0, nil)
   298  	dstBlock := &cb.Block{}
   299  
   300  	metadata, _ := proto.Marshal(&cb.Metadata{
   301  		Value: []byte("orderer metadata"),
   302  	})
   303  	srcBlock.Metadata.Metadata[cb.BlockMetadataIndex_ORDERER] = metadata
   304  	protoutil.CopyBlockMetadata(srcBlock, dstBlock)
   305  
   306  	// check that the copy worked
   307  	assert.Equal(t, len(srcBlock.Metadata.Metadata), len(dstBlock.Metadata.Metadata),
   308  		"Expected target block to have same number of metadata entries after copy")
   309  	assert.Equal(t, metadata, dstBlock.Metadata.Metadata[cb.BlockMetadataIndex_ORDERER],
   310  		"Unexpected metadata from target block")
   311  }
   312  
   313  func TestGetLastConfigIndexFromBlock(t *testing.T) {
   314  	index := uint64(2)
   315  	block := protoutil.NewBlock(0, nil)
   316  
   317  	t.Run("block with last config metadata in signatures field", func(t *testing.T) {
   318  		block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{
   319  			Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
   320  				LastConfig: &cb.LastConfig{Index: 2},
   321  			}),
   322  		})
   323  		result, err := protoutil.GetLastConfigIndexFromBlock(block)
   324  		assert.NoError(t, err, "Unexpected error returning last config index")
   325  		assert.Equal(t, index, result, "Unexpected last config index returned from block")
   326  		result = protoutil.GetLastConfigIndexFromBlockOrPanic(block)
   327  		assert.Equal(t, index, result, "Unexpected last config index returned from block")
   328  	})
   329  
   330  	t.Run("block with malformed signatures", func(t *testing.T) {
   331  		block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = []byte("apple")
   332  		_, err := protoutil.GetLastConfigIndexFromBlock(block)
   333  		assert.Error(t, err)
   334  		assert.Contains(t, err.Error(), "failed to retrieve metadata: error unmarshaling metadata at index [SIGNATURES]")
   335  	})
   336  
   337  	t.Run("block with malformed orderer block metadata", func(t *testing.T) {
   338  		block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{Value: []byte("banana")})
   339  		_, err := protoutil.GetLastConfigIndexFromBlock(block)
   340  		assert.Error(t, err)
   341  		assert.Contains(t, err.Error(), "failed to unmarshal orderer block metadata")
   342  	})
   343  
   344  	// TODO: FAB-15864 remove the tests below when we stop supporting upgrade from
   345  	//       pre-1.4.1 orderer
   346  	t.Run("block with deprecated (pre-1.4.1) last config", func(t *testing.T) {
   347  		block = protoutil.NewBlock(0, nil)
   348  		block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&cb.Metadata{
   349  			Value: protoutil.MarshalOrPanic(&cb.LastConfig{
   350  				Index: index,
   351  			}),
   352  		})
   353  		result, err := protoutil.GetLastConfigIndexFromBlock(block)
   354  		assert.NoError(t, err, "Unexpected error returning last config index")
   355  		assert.Equal(t, index, result, "Unexpected last config index returned from block")
   356  		result = protoutil.GetLastConfigIndexFromBlockOrPanic(block)
   357  		assert.Equal(t, index, result, "Unexpected last config index returned from block")
   358  	})
   359  
   360  	t.Run("malformed metadata", func(t *testing.T) {
   361  		block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = []byte("bad metadata")
   362  		_, err := protoutil.GetLastConfigIndexFromBlock(block)
   363  		assert.Error(t, err)
   364  		assert.Contains(t, err.Error(), "failed to retrieve metadata: error unmarshaling metadata at index [LAST_CONFIG]")
   365  	})
   366  
   367  	t.Run("malformed last config", func(t *testing.T) {
   368  		block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&cb.Metadata{
   369  			Value: []byte("bad last config"),
   370  		})
   371  		_, err := protoutil.GetLastConfigIndexFromBlock(block)
   372  		assert.Error(t, err, "Expected error with malformed last config metadata")
   373  		assert.Contains(t, err.Error(), "error unmarshaling LastConfig")
   374  		assert.Panics(t, func() {
   375  			_ = protoutil.GetLastConfigIndexFromBlockOrPanic(block)
   376  		}, "Expected panic with malformed last config metadata")
   377  	})
   378  }