go_study/fabric-main/protoutil/blockutils_test.go

492 lines
16 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package protoutil_test
import (
"crypto/sha256"
"encoding/asn1"
"math"
"testing"
"github.com/golang/protobuf/proto"
cb "github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric-protos-go/msp"
configtxtest "github.com/hyperledger/fabric/common/configtx/test"
"github.com/hyperledger/fabric/protoutil"
"github.com/hyperledger/fabric/protoutil/mocks"
"github.com/stretchr/testify/require"
)
var testChannelID = "myuniquetestchainid"
func TestNewBlock(t *testing.T) {
var block *cb.Block
require.Nil(t, block.GetHeader())
require.Nil(t, block.GetData())
require.Nil(t, block.GetMetadata())
data := &cb.BlockData{
Data: [][]byte{{0, 1, 2}},
}
block = protoutil.NewBlock(uint64(0), []byte("datahash"))
require.Equal(t, []byte("datahash"), block.Header.PreviousHash, "Incorrect previous hash")
require.NotNil(t, block.GetData())
require.NotNil(t, block.GetMetadata())
block.GetHeader().DataHash = protoutil.BlockDataHash(data)
asn1Bytes, err := asn1.Marshal(struct {
Number int64
PreviousHash []byte
DataHash []byte
}{
Number: 0,
DataHash: protoutil.BlockDataHash(data),
PreviousHash: []byte("datahash"),
})
headerHash := sha256.Sum256(asn1Bytes)
require.NoError(t, err)
require.Equal(t, asn1Bytes, protoutil.BlockHeaderBytes(block.Header), "Incorrect marshaled blockheader bytes")
require.Equal(t, headerHash[:], protoutil.BlockHeaderHash(block.Header), "Incorrect blockheader hash")
}
func TestGoodBlockHeaderBytes(t *testing.T) {
goodBlockHeader := &cb.BlockHeader{
Number: 1,
PreviousHash: []byte("foo"),
DataHash: []byte("bar"),
}
_ = protoutil.BlockHeaderBytes(goodBlockHeader) // Should not panic
goodBlockHeaderMaxNumber := &cb.BlockHeader{
Number: math.MaxUint64,
PreviousHash: []byte("foo"),
DataHash: []byte("bar"),
}
_ = protoutil.BlockHeaderBytes(goodBlockHeaderMaxNumber) // Should not panic
}
func TestGetChannelIDFromBlockBytes(t *testing.T) {
gb, err := configtxtest.MakeGenesisBlock(testChannelID)
require.NoError(t, err, "Failed to create test configuration block")
bytes, err := proto.Marshal(gb)
require.NoError(t, err)
cid, err := protoutil.GetChannelIDFromBlockBytes(bytes)
require.NoError(t, err)
require.Equal(t, testChannelID, cid, "Failed to return expected chain ID")
// bad block bytes
_, err = protoutil.GetChannelIDFromBlockBytes([]byte("bad block"))
require.Error(t, err, "Expected error with malformed block bytes")
}
func TestGetChannelIDFromBlock(t *testing.T) {
var err error
var gb *cb.Block
var cid string
// nil block
_, err = protoutil.GetChannelIDFromBlock(gb)
require.Error(t, err, "Expected error getting channel id from nil block")
gb, err = configtxtest.MakeGenesisBlock(testChannelID)
require.NoError(t, err, "Failed to create test configuration block")
cid, err = protoutil.GetChannelIDFromBlock(gb)
require.NoError(t, err, "Failed to get chain ID from block")
require.Equal(t, testChannelID, cid, "Failed to return expected chain ID")
// missing data
badBlock := gb
badBlock.Data = nil
_, err = protoutil.GetChannelIDFromBlock(badBlock)
require.Error(t, err, "Expected error with missing block data")
// no envelope
badBlock = &cb.Block{
Data: &cb.BlockData{
Data: [][]byte{[]byte("bad envelope")},
},
}
_, err = protoutil.GetChannelIDFromBlock(badBlock)
require.Error(t, err, "Expected error with no envelope in data")
// bad payload
env, _ := proto.Marshal(&cb.Envelope{
Payload: []byte("bad payload"),
})
badBlock = &cb.Block{
Data: &cb.BlockData{
Data: [][]byte{env},
},
}
_, err = protoutil.GetChannelIDFromBlock(badBlock)
require.Error(t, err, "Expected error - malformed payload")
// bad channel header
payload, _ := proto.Marshal(&cb.Payload{
Header: &cb.Header{
ChannelHeader: []byte("bad header"),
},
})
env, _ = proto.Marshal(&cb.Envelope{
Payload: payload,
})
badBlock = &cb.Block{
Data: &cb.BlockData{
Data: [][]byte{env},
},
}
_, err = protoutil.GetChannelIDFromBlock(badBlock)
require.Error(t, err, "Expected error with malformed channel header")
// nil payload header
payload, _ = proto.Marshal(&cb.Payload{})
env, _ = proto.Marshal(&cb.Envelope{
Payload: payload,
})
badBlock = &cb.Block{
Data: &cb.BlockData{
Data: [][]byte{env},
},
}
_, err = protoutil.GetChannelIDFromBlock(badBlock)
require.Error(t, err, "Expected error when payload header is nil")
}
func TestGetBlockFromBlockBytes(t *testing.T) {
testChainID := "myuniquetestchainid"
gb, err := configtxtest.MakeGenesisBlock(testChainID)
require.NoError(t, err, "Failed to create test configuration block")
blockBytes, err := protoutil.Marshal(gb)
require.NoError(t, err, "Failed to marshal block")
_, err = protoutil.UnmarshalBlock(blockBytes)
require.NoError(t, err, "to get block from block bytes")
// bad block bytes
_, err = protoutil.UnmarshalBlock([]byte("bad block"))
require.Error(t, err, "Expected error for malformed block bytes")
}
func TestGetMetadataFromBlock(t *testing.T) {
t.Run("new block", func(t *testing.T) {
block := protoutil.NewBlock(0, nil)
md, err := protoutil.GetMetadataFromBlock(block, cb.BlockMetadataIndex_ORDERER)
require.NoError(t, err, "Unexpected error extracting metadata from new block")
require.Nil(t, md.Value, "Expected metadata field value to be nil")
require.Equal(t, 0, len(md.Value), "Expected length of metadata field value to be 0")
md = protoutil.GetMetadataFromBlockOrPanic(block, cb.BlockMetadataIndex_ORDERER)
require.NotNil(t, md, "Expected to get metadata from block")
})
t.Run("no metadata", func(t *testing.T) {
block := protoutil.NewBlock(0, nil)
block.Metadata = nil
_, err := protoutil.GetMetadataFromBlock(block, cb.BlockMetadataIndex_ORDERER)
require.Error(t, err, "Expected error with nil metadata")
require.Contains(t, err.Error(), "no metadata in block")
})
t.Run("no metadata at index", func(t *testing.T) {
block := protoutil.NewBlock(0, nil)
block.Metadata.Metadata = [][]byte{{1, 2, 3}}
_, err := protoutil.GetMetadataFromBlock(block, cb.BlockMetadataIndex_LAST_CONFIG)
require.Error(t, err, "Expected error with nil metadata")
require.Contains(t, err.Error(), "no metadata at index")
})
t.Run("malformed metadata", func(t *testing.T) {
block := protoutil.NewBlock(0, nil)
block.Metadata.Metadata[cb.BlockMetadataIndex_ORDERER] = []byte("bad metadata")
_, err := protoutil.GetMetadataFromBlock(block, cb.BlockMetadataIndex_ORDERER)
require.Error(t, err, "Expected error with malformed metadata")
require.Contains(t, err.Error(), "error unmarshalling metadata at index [ORDERER]")
require.Panics(t, func() {
_ = protoutil.GetMetadataFromBlockOrPanic(block, cb.BlockMetadataIndex_ORDERER)
}, "Expected panic with malformed metadata")
})
}
func TestGetConsenterMetadataFromBlock(t *testing.T) {
cases := []struct {
name string
value []byte
signatures []byte
orderer []byte
pass bool
}{
{
name: "empty",
value: nil,
signatures: nil,
orderer: nil,
pass: true,
},
{
name: "signature only",
value: []byte("hello"),
signatures: protoutil.MarshalOrPanic(&cb.Metadata{
Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
ConsenterMetadata: protoutil.MarshalOrPanic(&cb.Metadata{Value: []byte("hello")}),
}),
}),
orderer: nil,
pass: true,
},
{
name: "orderer only",
value: []byte("hello"),
signatures: nil,
orderer: protoutil.MarshalOrPanic(&cb.Metadata{Value: []byte("hello")}),
pass: true,
},
{
name: "both signatures and orderer",
value: []byte("hello"),
signatures: protoutil.MarshalOrPanic(&cb.Metadata{
Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
ConsenterMetadata: protoutil.MarshalOrPanic(&cb.Metadata{Value: []byte("hello")}),
}),
}),
orderer: protoutil.MarshalOrPanic(&cb.Metadata{Value: []byte("hello")}),
pass: true,
},
{
name: "malformed OrdererBlockMetadata",
signatures: protoutil.MarshalOrPanic(&cb.Metadata{Value: []byte("malformed")}),
orderer: nil,
pass: false,
},
}
for _, test := range cases {
block := protoutil.NewBlock(0, nil)
block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = test.signatures
block.Metadata.Metadata[cb.BlockMetadataIndex_ORDERER] = test.orderer
result, err := protoutil.GetConsenterMetadataFromBlock(block)
if test.pass {
require.NoError(t, err)
require.Equal(t, result.Value, test.value)
} else {
require.Error(t, err)
}
}
}
func TestInitBlockMeta(t *testing.T) {
// block with no metadata
block := &cb.Block{}
protoutil.InitBlockMetadata(block)
// should have 3 entries
require.Equal(t, 5, len(block.Metadata.Metadata), "Expected block to have 5 metadata entries")
// block with a single entry
block = &cb.Block{
Metadata: &cb.BlockMetadata{},
}
block.Metadata.Metadata = append(block.Metadata.Metadata, []byte{})
protoutil.InitBlockMetadata(block)
// should have 3 entries
require.Equal(t, 5, len(block.Metadata.Metadata), "Expected block to have 5 metadata entries")
}
func TestCopyBlockMetadata(t *testing.T) {
srcBlock := protoutil.NewBlock(0, nil)
dstBlock := &cb.Block{}
metadata, _ := proto.Marshal(&cb.Metadata{
Value: []byte("orderer metadata"),
})
srcBlock.Metadata.Metadata[cb.BlockMetadataIndex_ORDERER] = metadata
protoutil.CopyBlockMetadata(srcBlock, dstBlock)
// check that the copy worked
require.Equal(t, len(srcBlock.Metadata.Metadata), len(dstBlock.Metadata.Metadata),
"Expected target block to have same number of metadata entries after copy")
require.Equal(t, metadata, dstBlock.Metadata.Metadata[cb.BlockMetadataIndex_ORDERER],
"Unexpected metadata from target block")
}
func TestGetLastConfigIndexFromBlock(t *testing.T) {
index := uint64(2)
block := protoutil.NewBlock(0, nil)
t.Run("block with last config metadata in signatures field", func(t *testing.T) {
block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{
Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
LastConfig: &cb.LastConfig{Index: 2},
}),
})
result, err := protoutil.GetLastConfigIndexFromBlock(block)
require.NoError(t, err, "Unexpected error returning last config index")
require.Equal(t, index, result, "Unexpected last config index returned from block")
result = protoutil.GetLastConfigIndexFromBlockOrPanic(block)
require.Equal(t, index, result, "Unexpected last config index returned from block")
})
t.Run("block with malformed signatures", func(t *testing.T) {
block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = []byte("apple")
_, err := protoutil.GetLastConfigIndexFromBlock(block)
require.Error(t, err)
require.Contains(t, err.Error(), "failed to retrieve metadata: error unmarshalling metadata at index [SIGNATURES]")
})
t.Run("block with malformed orderer block metadata", func(t *testing.T) {
block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{Value: []byte("banana")})
_, err := protoutil.GetLastConfigIndexFromBlock(block)
require.Error(t, err)
require.Contains(t, err.Error(), "failed to unmarshal orderer block metadata")
})
// TODO: FAB-15864 remove the tests below when we stop supporting upgrade from
// pre-1.4.1 orderer
t.Run("block with deprecated (pre-1.4.1) last config", func(t *testing.T) {
block = protoutil.NewBlock(0, nil)
block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&cb.Metadata{
Value: protoutil.MarshalOrPanic(&cb.LastConfig{
Index: index,
}),
})
result, err := protoutil.GetLastConfigIndexFromBlock(block)
require.NoError(t, err, "Unexpected error returning last config index")
require.Equal(t, index, result, "Unexpected last config index returned from block")
result = protoutil.GetLastConfigIndexFromBlockOrPanic(block)
require.Equal(t, index, result, "Unexpected last config index returned from block")
})
t.Run("malformed metadata", func(t *testing.T) {
block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = []byte("bad metadata")
_, err := protoutil.GetLastConfigIndexFromBlock(block)
require.Error(t, err)
require.Contains(t, err.Error(), "failed to retrieve metadata: error unmarshalling metadata at index [LAST_CONFIG]")
})
t.Run("malformed last config", func(t *testing.T) {
block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&cb.Metadata{
Value: []byte("bad last config"),
})
_, err := protoutil.GetLastConfigIndexFromBlock(block)
require.Error(t, err, "Expected error with malformed last config metadata")
require.Contains(t, err.Error(), "error unmarshalling LastConfig")
require.Panics(t, func() {
_ = protoutil.GetLastConfigIndexFromBlockOrPanic(block)
}, "Expected panic with malformed last config metadata")
})
}
func TestBlockSignatureVerifierEmptyMetadata(t *testing.T) {
policies := mocks.Policy{}
verify := protoutil.BlockSignatureVerifier(true, nil, &policies)
header := &cb.BlockHeader{}
md := &cb.BlockMetadata{}
err := verify(header, md)
require.ErrorContains(t, err, "no signatures in block metadata")
}
func TestBlockSignatureVerifierByIdentifier(t *testing.T) {
consenters := []*cb.Consenter{
{
Id: 1,
Host: "host1",
Port: 8001,
MspId: "msp1",
Identity: []byte("identity1"),
},
{
Id: 2,
Host: "host2",
Port: 8002,
MspId: "msp2",
Identity: []byte("identity2"),
},
{
Id: 3,
Host: "host3",
Port: 8003,
MspId: "msp3",
Identity: []byte("identity3"),
},
}
policies := mocks.Policy{}
verify := protoutil.BlockSignatureVerifier(true, consenters, &policies)
header := &cb.BlockHeader{}
md := &cb.BlockMetadata{
Metadata: [][]byte{
protoutil.MarshalOrPanic(&cb.Metadata{Signatures: []*cb.MetadataSignature{
{
Signature: []byte{},
IdentifierHeader: protoutil.MarshalOrPanic(&cb.IdentifierHeader{Identifier: 1}),
},
{
Signature: []byte{},
IdentifierHeader: protoutil.MarshalOrPanic(&cb.IdentifierHeader{Identifier: 3}),
},
}}),
},
}
err := verify(header, md)
require.NoError(t, err)
signatureSet := policies.EvaluateSignedDataArgsForCall(0)
require.Len(t, signatureSet, 2)
require.Equal(t, protoutil.MarshalOrPanic(&msp.SerializedIdentity{Mspid: "msp1", IdBytes: []byte("identity1")}), signatureSet[0].Identity)
require.Equal(t, protoutil.MarshalOrPanic(&msp.SerializedIdentity{Mspid: "msp3", IdBytes: []byte("identity3")}), signatureSet[1].Identity)
}
func TestBlockSignatureVerifierByCreator(t *testing.T) {
consenters := []*cb.Consenter{
{
Id: 1,
Host: "host1",
Port: 8001,
MspId: "msp1",
Identity: []byte("identity1"),
},
{
Id: 2,
Host: "host2",
Port: 8002,
MspId: "msp2",
Identity: []byte("identity2"),
},
{
Id: 3,
Host: "host3",
Port: 8003,
MspId: "msp3",
Identity: []byte("identity3"),
},
}
policies := mocks.Policy{}
verify := protoutil.BlockSignatureVerifier(true, consenters, &policies)
header := &cb.BlockHeader{}
md := &cb.BlockMetadata{
Metadata: [][]byte{
protoutil.MarshalOrPanic(&cb.Metadata{Signatures: []*cb.MetadataSignature{
{
Signature: []byte{},
SignatureHeader: protoutil.MarshalOrPanic(&cb.SignatureHeader{Creator: []byte("creator1")}),
},
}}),
},
}
err := verify(header, md)
require.NoError(t, err)
signatureSet := policies.EvaluateSignedDataArgsForCall(0)
require.Len(t, signatureSet, 1)
require.Equal(t, []byte("creator1"), signatureSet[0].Identity)
}