diff --git a/protos/utils/blockutils.go b/protos/utils/blockutils.go new file mode 100644 index 00000000000..8772af64487 --- /dev/null +++ b/protos/utils/blockutils.go @@ -0,0 +1,132 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package utils + +import ( + "fmt" + + "github.com/golang/protobuf/proto" + + "github.com/hyperledger/fabric/orderer/common/cauthdsl" + "github.com/hyperledger/fabric/orderer/common/configtx" + cb "github.com/hyperledger/fabric/protos/common" + ab "github.com/hyperledger/fabric/protos/orderer" +) + +// GetChainIDFromBlock returns chain ID in the block +func GetChainIDFromBlock(block *cb.Block) (string, error) { + if block.Data == nil || block.Data.Data == nil || len(block.Data.Data) == 0 { + return "", fmt.Errorf("Failed to find chain ID because the block is empty.") + } + var err error + envelope := &cb.Envelope{} + if err = proto.Unmarshal(block.Data.Data[0], envelope); err != nil { + return "", fmt.Errorf("Error reconstructing envelope(%s)", err) + } + payload := &cb.Payload{} + if err = proto.Unmarshal(envelope.Payload, payload); err != nil { + return "", fmt.Errorf("Error reconstructing payload(%s)", err) + } + + return payload.Header.ChainHeader.ChainID, nil +} + +// GetBlockFromBlockBytes marshals the bytes into Block +func GetBlockFromBlockBytes(blockBytes []byte) (*cb.Block, error) { + block := &cb.Block{} + err := proto.Unmarshal(blockBytes, block) + return block, err +} + +// MakeConfigurationBlock creates a mock configuration block for testing in +// various modules. This is a convenient function rather than every test +// implements its own +func MakeConfigurationBlock(testChainID string) (*cb.Block, error) { + configItemChainHeader := MakeChainHeader(cb.HeaderType_CONFIGURATION_ITEM, + messageVersion, testChainID, epoch) + + configEnvelope := MakeConfigurationEnvelope( + encodeConsensusType(testChainID), + encodeBatchSize(testChainID), + lockDefaultModificationPolicy(testChainID), + ) + payloadChainHeader := MakeChainHeader(cb.HeaderType_CONFIGURATION_TRANSACTION, + configItemChainHeader.Version, testChainID, epoch) + payloadSignatureHeader := MakeSignatureHeader(nil, CreateNonceOrPanic()) + payloadHeader := MakePayloadHeader(payloadChainHeader, payloadSignatureHeader) + payload := &cb.Payload{Header: payloadHeader, Data: MarshalOrPanic(configEnvelope)} + envelope := &cb.Envelope{Payload: MarshalOrPanic(payload), Signature: nil} + + blockData := &cb.BlockData{Data: [][]byte{MarshalOrPanic(envelope)}} + + return &cb.Block{ + Header: &cb.BlockHeader{ + Number: 0, + PreviousHash: nil, + DataHash: blockData.Hash(), + }, + Data: blockData, + Metadata: nil, + }, nil +} + +const ( + batchSize = 10 + consensusType = "solo" + epoch = uint64(0) + messageVersion = int32(1) + lastModified = uint64(0) + consensusTypeKey = "ConsensusType" + batchSizeKey = "BatchSize" +) + +func createSignedConfigItem(chainID string, + configItemKey string, + configItemValue []byte, + modPolicy string) *cb.SignedConfigurationItem { + + ciChainHeader := MakeChainHeader(cb.HeaderType_CONFIGURATION_ITEM, + messageVersion, chainID, epoch) + configItem := MakeConfigurationItem(ciChainHeader, + cb.ConfigurationItem_Orderer, lastModified, modPolicy, + configItemKey, configItemValue) + + return &cb.SignedConfigurationItem{ + ConfigurationItem: MarshalOrPanic(configItem), + Signatures: nil} +} + +func encodeConsensusType(testChainID string) *cb.SignedConfigurationItem { + return createSignedConfigItem(testChainID, + consensusTypeKey, + MarshalOrPanic(&ab.ConsensusType{Type: consensusType}), + configtx.DefaultModificationPolicyID) +} + +func encodeBatchSize(testChainID string) *cb.SignedConfigurationItem { + return createSignedConfigItem(testChainID, + batchSizeKey, + MarshalOrPanic(&ab.BatchSize{Messages: batchSize}), + configtx.DefaultModificationPolicyID) +} + +func lockDefaultModificationPolicy(testChainID string) *cb.SignedConfigurationItem { + return createSignedConfigItem(testChainID, + configtx.DefaultModificationPolicyID, + MarshalOrPanic(MakePolicyOrPanic(cauthdsl.RejectAllPolicy)), + configtx.DefaultModificationPolicyID) +} diff --git a/protos/utils/blockutils_test.go b/protos/utils/blockutils_test.go new file mode 100644 index 00000000000..5b2af2a2f78 --- /dev/null +++ b/protos/utils/blockutils_test.go @@ -0,0 +1,68 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This package provides unit tests for blocks +package utils + +import ( + "testing" + + "github.com/hyperledger/fabric/protos/common" +) + +func TestGetChainIDFromBlock(t *testing.T) { + var err error + var gb *common.Block + var cid string + + testChainID := "myuniquetestchainid" + + gb, err = MakeConfigurationBlock(testChainID) + if err != nil { + t.Fatalf("failed to create test configuration block: %s", err) + } + cid, err = GetChainIDFromBlock(gb) + if err != nil { + t.Fatalf("failed to get chain ID from block: %s", err) + } + if testChainID != cid { + t.Fatalf("failed with wrong chain ID: Actual=%s; Got=%s", testChainID, cid) + } + + badBlock := gb + badBlock.Data = nil + _, err = GetChainIDFromBlock(badBlock) + // We should get error + if err == nil { + t.Fatalf("error is expected -- the block must not be marshallable") + } +} + +func TestGetBlockFromBlockBytes(t *testing.T) { + testChainID := "myuniquetestchainid" + gb, err := MakeConfigurationBlock(testChainID) + if err != nil { + t.Fatalf("failed to create test configuration block: %s", err) + } + blockBytes, err := Marshal(gb) + if err != nil { + t.Fatalf("failed to marshal block: %s", err) + } + _, err = GetBlockFromBlockBytes(blockBytes) + if err != nil { + t.Fatalf("failed to get block from block bytes: %s", err) + } +}