-
Notifications
You must be signed in to change notification settings - Fork 8.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge "[FAB-1094] util to parse config tx blocks"
- Loading branch information
Showing
2 changed files
with
384 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/* | ||
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" | ||
|
||
pb "github.com/hyperledger/fabric/protos/common" | ||
|
||
"github.com/golang/protobuf/proto" | ||
) | ||
|
||
// no need to break out block header as none of its parts are serialized | ||
|
||
// no need to break out block metadata as it's just a byte slice | ||
|
||
// no need to break Block into constituents. Nothing to unmarshall | ||
|
||
// BreakOutBlockDataOrPanic executes BreakOutBlockData() but panics on error | ||
func BreakOutBlockDataOrPanic(blockData *pb.BlockData) ([]*pb.Payload, [][]byte) { | ||
payloads, envelopeSignatures, err := BreakOutBlockData(blockData) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return payloads, envelopeSignatures | ||
} // BreakOutBlockDataOrPanic | ||
|
||
// BreakOutBlockData decomposes a blockData into its payloads and signatures. | ||
// since a BlockData contains a slice of Envelopes, this functions returns slices of Payloads and Envelope.Signatures. | ||
// the Payload/Signature pair has the same array index | ||
func BreakOutBlockData(blockData *pb.BlockData) ([]*pb.Payload, [][]byte, error) { | ||
var err error | ||
|
||
var envelopeSignatures [][]byte | ||
var payloads []*pb.Payload | ||
|
||
var envelope *pb.Envelope | ||
var payload *pb.Payload | ||
for _, envelopeBytes := range blockData.Data { | ||
envelope = &pb.Envelope{} | ||
err = proto.Unmarshal(envelopeBytes, envelope) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
payload = &pb.Payload{} | ||
err = proto.Unmarshal(envelope.Payload, payload) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
envelopeSignatures = append(envelopeSignatures, envelope.Signature) | ||
payloads = append(payloads, payload) | ||
} | ||
|
||
return payloads, envelopeSignatures, nil | ||
} // BreakOutBlockData | ||
|
||
// BreakOutPayloadDataToConfigurationEnvelopeOrPanic calls BreakOutPayloadDataToConfigurationEnvelope() but panics on error | ||
func BreakOutPayloadDataToConfigurationEnvelopeOrPanic(payloadData []byte) *pb.ConfigurationEnvelope { | ||
configEnvelope, err := BreakOutPayloadDataToConfigurationEnvelope(payloadData) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return configEnvelope | ||
} // BreakOutPayloadDataToConfigurationEnvelopeOrPanic | ||
|
||
// BreakOutPayloadDataToConfigurationEnvelope decomposes a Payload.Data item into its constituent ConfigurationEnvelope | ||
func BreakOutPayloadDataToConfigurationEnvelope(payloadData []byte) (*pb.ConfigurationEnvelope, error) { | ||
if payloadData == nil { | ||
return nil, fmt.Errorf("input Payload data is null\n") | ||
} | ||
|
||
configEnvelope := &pb.ConfigurationEnvelope{} | ||
err := proto.Unmarshal(payloadData, configEnvelope) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return configEnvelope, nil | ||
} //BreakOutPayloadToConfigurationEnvelope | ||
|
||
// BreakOutConfigEnvelopeToConfigItemsOrPanic calls BreakOutConfigEnvelopeToConfigItems() but panics on error | ||
func BreakOutConfigEnvelopeToConfigItemsOrPanic(configEnvelope *pb.ConfigurationEnvelope) ([]*pb.ConfigurationItem, [][]*pb.ConfigurationSignature) { | ||
configItems, configSignatures, err := BreakOutConfigEnvelopeToConfigItems(configEnvelope) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return configItems, configSignatures | ||
} // BreakOutConfigEnvelopeToConfigItemsOrPanic | ||
|
||
// BreakOutConfigEnvelopeToConfigItems decomposes a ConfigurationEnvelope to its constituent ConfigurationItems and ConfigurationSignatures | ||
// Note that a ConfigurationItem can have multiple signatures so each element in the returned ConfigurationItems slice is associated with a slice of ConfigurationSignatures | ||
func BreakOutConfigEnvelopeToConfigItems(configEnvelope *pb.ConfigurationEnvelope) ([]*pb.ConfigurationItem, [][]*pb.ConfigurationSignature, error) { | ||
if configEnvelope == nil { | ||
return nil, nil, fmt.Errorf("BreakOutConfigEnvelopeToConfigItems received null input\n") | ||
} | ||
|
||
var configItems []*pb.ConfigurationItem | ||
var configSignatures [][]*pb.ConfigurationSignature | ||
|
||
var err error | ||
var configItem *pb.ConfigurationItem | ||
for i, signedConfigItem := range configEnvelope.Items { | ||
configItem = &pb.ConfigurationItem{} | ||
err = proto.Unmarshal(signedConfigItem.ConfigurationItem, configItem) | ||
if err != nil { | ||
return nil, nil, fmt.Errorf("BreakOutConfigEnvelopToConfigItems cannot unmarshall signedConfigurationItem: %v\n", err) | ||
} | ||
configItems = append(configItems, configItem) | ||
for _, signedConfigItemSignature := range signedConfigItem.Signatures { | ||
configSignatures[i] = append(configSignatures[i], signedConfigItemSignature) | ||
} | ||
} | ||
|
||
return configItems, configSignatures, nil | ||
} // BreakOutConfigEnvelopeToConfigItems | ||
|
||
// BreakOutBlockToConfigurationEnvelopeOrPanic calls BreakOutBlockToConfigurationEnvelope() but panics on error | ||
func BreakOutBlockToConfigurationEnvelopeOrPanic(block *pb.Block) (*pb.ConfigurationEnvelope, []byte) { | ||
configEnvelope, envelopeSignature, err := BreakOutBlockToConfigurationEnvelope(block) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return configEnvelope, envelopeSignature | ||
} // BreakOutBlockToConfigurationEnvelopeOrPanic | ||
|
||
// BreakOutBlockToConfigurationEnvelope decomposes a configuration transaction Block to its ConfigurationEnvelope | ||
func BreakOutBlockToConfigurationEnvelope(block *pb.Block) (*pb.ConfigurationEnvelope, []byte, error) { | ||
if block == nil || block.Data == nil || len(block.Data.Data) > 1 { | ||
return nil, nil, fmt.Errorf("Block.BlockData is not an array of 1. This is not a configuration transaction\n") | ||
} | ||
|
||
payloads, envelopeSignatures, err := BreakOutBlockData(block.Data) | ||
|
||
if payloads[0].Header.ChainHeader.Type != int32(pb.HeaderType_CONFIGURATION_TRANSACTION) { | ||
return nil, nil, fmt.Errorf("Payload Header type is not configuration_transaction. This is not a configuration transaction\n") | ||
} | ||
var configEnvelope *pb.ConfigurationEnvelope | ||
configEnvelope, err = BreakOutPayloadDataToConfigurationEnvelope(payloads[0].Data) | ||
if err != nil { | ||
return nil, nil, fmt.Errorf("Error breaking out configurationEnvelope: %v\n", err) | ||
} | ||
|
||
return configEnvelope, envelopeSignatures[0], nil | ||
} // BreakOutPayloadToConfigurationEnvelope |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
/* | ||
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 ( | ||
"bytes" | ||
"testing" | ||
"time" | ||
|
||
pb "github.com/hyperledger/fabric/protos/common" | ||
|
||
"github.com/golang/protobuf/proto" | ||
"github.com/golang/protobuf/ptypes/timestamp" | ||
) | ||
|
||
// added comment to force gerrit commit | ||
|
||
func TestBreakOutBlockDataBadData(t *testing.T) { | ||
fakeBlockData := &pb.BlockData{} | ||
payloads, sigs, _ := BreakOutBlockData(fakeBlockData) | ||
if len(payloads) > 0 || len(sigs) > 0 { | ||
t.Errorf("TestBreakOutBlockData should not work with blank input.\n") | ||
} // TestBreakOutBlockDataBadData | ||
} // TestBreakOutBlockDataBadData | ||
|
||
func TestBreakOutBlockData(t *testing.T) { | ||
block := testBlock() | ||
payloads, _, _ := BreakOutBlockData(block.Data) // TODO: test for signature | ||
if len(payloads) != 1 { | ||
t.Errorf("TestBreakOutBlock did not unmarshall to array of 1 payloads\n") | ||
} | ||
if payloads[0].Header.ChainHeader.Version != 1 || payloads[0].Header.ChainHeader.Type != int32(pb.HeaderType_CONFIGURATION_TRANSACTION) || !bytes.Equal(payloads[0].Header.ChainHeader.ChainID, []byte("test")) { | ||
t.Errorf("TestBreakOutBlockData payload header is %+v . Expected type is %v and Version == 1\n", payloads[0].Header.ChainHeader, int32(pb.HeaderType_CONFIGURATION_TRANSACTION)) | ||
} | ||
if !bytes.Equal(payloads[0].Data, []byte("test")) { | ||
t.Errorf("TestBreakOutBlockData payload data is %s . Expected 'test'\n", payloads[0].Data) | ||
} | ||
|
||
} // TesttBreakOutBlockData | ||
|
||
func TestBreakOutPayloadDataToConfigurationEnvelopePanic(t *testing.T) { | ||
defer func() { | ||
if r := recover(); r == nil { | ||
t.Errorf("TestBreakOutPayloadDataToConfigurationEnvelopePanic should have panicked") | ||
} | ||
}() | ||
_ = BreakOutPayloadDataToConfigurationEnvelopeOrPanic(nil) | ||
} // TestBreakOutPayloadDataToConfigurationEnvelopePanic | ||
|
||
func TestBreakOutPayloadDataToConfigurationEnvelopeBadData(t *testing.T) { | ||
_, err := BreakOutPayloadDataToConfigurationEnvelope(nil) | ||
if err == nil { | ||
t.Errorf("TestBreakOutPayloadDataToConfigurationEnvelopeBadData should have returned error on null input\n ") | ||
} | ||
} // TestBreakOutPayloadDataToConfigurationEnvelopeBadData | ||
|
||
func TestBreakOutPayloadDataToConfigurationEnvelope(t *testing.T) { | ||
payload := testPayloadConfigEnvelope() | ||
configEnvelope, _ := BreakOutPayloadDataToConfigurationEnvelope(payload.Data) | ||
if len(configEnvelope.Items) != 1 { | ||
t.Errorf("TestBreakOutPayloadDataToConfigurationEnvelope: configEnvelope.Items array should have 1 item") | ||
} | ||
} // TestBreakOutPayloadDataToConfigurationEnvelope | ||
|
||
func TestBreakOutConfigEnvelopeToConfigItemsPanic(t *testing.T) { | ||
defer func() { | ||
if r := recover(); r == nil { | ||
t.Errorf("TestBreakOutConfigEnvelopeToConfigItemsPanic should have panicked") | ||
} | ||
}() | ||
_, _ = BreakOutConfigEnvelopeToConfigItemsOrPanic(nil) | ||
} // TestBreakOutConfigEnvelopeToConfigItemsPanic | ||
|
||
func TestBreakOutConfigEnvelopeToConfigItemsBadData(t *testing.T) { | ||
_, _, err := BreakOutConfigEnvelopeToConfigItems(nil) | ||
if err == nil { | ||
t.Errorf("TestBreakOutConfigEnvelopeToConfigItemsBadData not handling nil input\n") | ||
} | ||
} // TestBreakOutConfigEnvelopeToConfigItemsBadData | ||
|
||
func TestBreakOutConfigEnvelopeToConfigItems(t *testing.T) { | ||
configEnv := testConfigurationEnvelope() | ||
configItems, _, _ := BreakOutConfigEnvelopeToConfigItems(configEnv) // TODO: test signatures | ||
if len(configItems) != 1 { | ||
t.Errorf("TestBreakOutPayloadDataToConfigurationEnvelope did not return array of 1 config item\n") | ||
} | ||
if configItems[0].Header.Type != int32(pb.HeaderType_CONFIGURATION_TRANSACTION) || !bytes.Equal(configItems[0].Header.ChainID, []byte("test")) { | ||
t.Errorf("TestBreakOutConfigEnvelopeToConfigItems, configItem header does not match original %+v . Expected config_transaction and chainid 'test'\n", configItems[0].Header) | ||
} | ||
if configItems[0].Type != pb.ConfigurationItem_Orderer || configItems[0].Key != "abc" || !bytes.Equal(configItems[0].Value, []byte("test")) { | ||
t.Errorf("TestBreakOutConfigEnvelopeToConfigItems configItem type,Key,Value do not match original %+v\n. Expected orderer, 'abc', 'test'", configItems[0]) | ||
} | ||
} // TestBreakOutConfigEnvelopeToConfigItems | ||
|
||
func TestBreakOutBlockToConfigurationEnvelopePanic(t *testing.T) { | ||
defer func() { | ||
if r := recover(); r == nil { | ||
t.Errorf("TestBreakOutBlockToConfigurationEnvelopePanic should have panicked") | ||
} | ||
}() | ||
_, _ = BreakOutBlockToConfigurationEnvelopeOrPanic(nil) | ||
} // TestBreakOutBlockToConfigurationEnvelopePanic | ||
|
||
func TestBreakOutBlockToConfigurationEnvelopeBadData(t *testing.T) { | ||
_, _, err := BreakOutBlockToConfigurationEnvelope(nil) | ||
if err == nil { | ||
t.Errorf("TestBreakOutBlockToConfigurationEnvelopeBadData should have rejected null input\n") | ||
} | ||
} // TestBreakOutBlockToConfigurationEnvelopeBadData | ||
|
||
func TestBreakOutBlockToConfigurationEnvelope(t *testing.T) { | ||
block := testConfigurationBlock() | ||
configEnvelope, _, _ := BreakOutBlockToConfigurationEnvelope(block) // TODO: test envelope signature | ||
if len(configEnvelope.Items) != 1 { | ||
t.Errorf("TestBreakOutBlockToConfigurationEnvelope should have an array of 1 signedConfigurationItems\n") | ||
} | ||
} // TestBreakOutBlockToConfigurationEnvelopeBadData | ||
|
||
// Helper functions | ||
func testChainHeader() *pb.ChainHeader { | ||
return &pb.ChainHeader{ | ||
Type: int32(pb.HeaderType_CONFIGURATION_TRANSACTION), | ||
Version: 1, | ||
Timestamp: ×tamp.Timestamp{ | ||
Seconds: time.Now().Unix(), | ||
Nanos: 0, | ||
}, | ||
ChainID: []byte("test"), | ||
} | ||
} | ||
|
||
func testPayloadHeader() *pb.Header { | ||
return &pb.Header{ | ||
ChainHeader: testChainHeader(), | ||
SignatureHeader: nil, | ||
} | ||
} | ||
|
||
func testPayload() *pb.Payload { | ||
return &pb.Payload{ | ||
Header: testPayloadHeader(), | ||
Data: []byte("test"), | ||
} | ||
} | ||
|
||
func testEnvelope() *pb.Envelope { | ||
// No need to set the signature | ||
payloadBytes, _ := proto.Marshal(testPayload()) | ||
return &pb.Envelope{Payload: payloadBytes} | ||
} | ||
|
||
func testPayloadConfigEnvelope() *pb.Payload { | ||
data, _ := proto.Marshal(testConfigurationEnvelope()) | ||
return &pb.Payload{ | ||
Header: testPayloadHeader(), | ||
Data: data, | ||
} | ||
} | ||
|
||
func testEnvelopePayloadConfigEnv() *pb.Envelope { | ||
payloadBytes, _ := proto.Marshal(testPayloadConfigEnvelope()) | ||
return &pb.Envelope{Payload: payloadBytes} | ||
} // testEnvelopePayloadConfigEnv | ||
|
||
func testConfigurationBlock() *pb.Block { | ||
envelopeBytes, _ := proto.Marshal(testEnvelopePayloadConfigEnv()) | ||
return &pb.Block{ | ||
Data: &pb.BlockData{ | ||
Data: [][]byte{envelopeBytes}, | ||
}, | ||
} | ||
} | ||
|
||
func testConfigurationEnvelope() *pb.ConfigurationEnvelope { | ||
chainHeader := testChainHeader() | ||
configItem := makeConfigurationItem(chainHeader, pb.ConfigurationItem_Orderer, 0, "defaultPolicyID", "abc", []byte("test")) | ||
signedConfigItem, _ := makeSignedConfigurationItem(configItem, nil) | ||
return makeConfigurationEnvelope(signedConfigItem) | ||
} // testConfigurationEnvelope | ||
|
||
func testBlock() *pb.Block { | ||
// No need to set the block's Header, or Metadata | ||
envelopeBytes, _ := proto.Marshal(testEnvelope()) | ||
return &pb.Block{ | ||
Data: &pb.BlockData{ | ||
Data: [][]byte{envelopeBytes}, | ||
}, | ||
} | ||
} | ||
|
||
func makeConfigurationItem(ch *pb.ChainHeader, configItemType pb.ConfigurationItem_ConfigurationType, lastModified uint64, modPolicyID string, key string, value []byte) *pb.ConfigurationItem { | ||
return &pb.ConfigurationItem{ | ||
Header: ch, | ||
Type: configItemType, | ||
LastModified: lastModified, | ||
ModificationPolicy: modPolicyID, | ||
Key: key, | ||
Value: value, | ||
} | ||
} | ||
|
||
func makeSignedConfigurationItem(configItem *pb.ConfigurationItem, signatures []*pb.ConfigurationSignature) (*pb.SignedConfigurationItem, error) { | ||
configItemBytes, _ := proto.Marshal(configItem) | ||
return &pb.SignedConfigurationItem{ | ||
ConfigurationItem: configItemBytes, | ||
Signatures: signatures, | ||
}, nil | ||
} // makeSignedConfigurationItem | ||
|
||
func makeConfigurationEnvelope(items ...*pb.SignedConfigurationItem) *pb.ConfigurationEnvelope { | ||
return &pb.ConfigurationEnvelope{Items: items} | ||
} |