Skip to content

Commit 60706a7

Browse files
committed
[FAB-1094] util to parse config tx blocks
These functions parse a Block into Envelopes, Payloads, ConfigurationEnvelopes and ConfigurationItems Each function has an *OrPanic version which can be used when the caller wants to stop all operations on error. These can also be useful when making an inline call. added unit tests, functions from Kosta's [FAB-996] and updates from Murali's comments 11/15/2016 added *OrPanic versions of the functions, updated code per comments 11/16/2016 clean up *OrPanic function signature 11/17/2016 force new commit to clean up gerrit patch setss Change-Id: Ic47cfed75652896b53115fb46b096acf0cb2f86a Signed-off-by: tuand27613 <[email protected]>
1 parent 3b6c70d commit 60706a7

File tree

2 files changed

+384
-0
lines changed

2 files changed

+384
-0
lines changed

protos/utils/configtxutils.go

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package utils
18+
19+
import (
20+
"fmt"
21+
22+
pb "github.com/hyperledger/fabric/protos/common"
23+
24+
"github.com/golang/protobuf/proto"
25+
)
26+
27+
// no need to break out block header as none of its parts are serialized
28+
29+
// no need to break out block metadata as it's just a byte slice
30+
31+
// no need to break Block into constituents. Nothing to unmarshall
32+
33+
// BreakOutBlockDataOrPanic executes BreakOutBlockData() but panics on error
34+
func BreakOutBlockDataOrPanic(blockData *pb.BlockData) ([]*pb.Payload, [][]byte) {
35+
payloads, envelopeSignatures, err := BreakOutBlockData(blockData)
36+
if err != nil {
37+
panic(err)
38+
}
39+
return payloads, envelopeSignatures
40+
} // BreakOutBlockDataOrPanic
41+
42+
// BreakOutBlockData decomposes a blockData into its payloads and signatures.
43+
// since a BlockData contains a slice of Envelopes, this functions returns slices of Payloads and Envelope.Signatures.
44+
// the Payload/Signature pair has the same array index
45+
func BreakOutBlockData(blockData *pb.BlockData) ([]*pb.Payload, [][]byte, error) {
46+
var err error
47+
48+
var envelopeSignatures [][]byte
49+
var payloads []*pb.Payload
50+
51+
var envelope *pb.Envelope
52+
var payload *pb.Payload
53+
for _, envelopeBytes := range blockData.Data {
54+
envelope = &pb.Envelope{}
55+
err = proto.Unmarshal(envelopeBytes, envelope)
56+
if err != nil {
57+
return nil, nil, err
58+
}
59+
payload = &pb.Payload{}
60+
err = proto.Unmarshal(envelope.Payload, payload)
61+
if err != nil {
62+
return nil, nil, err
63+
}
64+
envelopeSignatures = append(envelopeSignatures, envelope.Signature)
65+
payloads = append(payloads, payload)
66+
}
67+
68+
return payloads, envelopeSignatures, nil
69+
} // BreakOutBlockData
70+
71+
// BreakOutPayloadDataToConfigurationEnvelopeOrPanic calls BreakOutPayloadDataToConfigurationEnvelope() but panics on error
72+
func BreakOutPayloadDataToConfigurationEnvelopeOrPanic(payloadData []byte) *pb.ConfigurationEnvelope {
73+
configEnvelope, err := BreakOutPayloadDataToConfigurationEnvelope(payloadData)
74+
if err != nil {
75+
panic(err)
76+
}
77+
return configEnvelope
78+
} // BreakOutPayloadDataToConfigurationEnvelopeOrPanic
79+
80+
// BreakOutPayloadDataToConfigurationEnvelope decomposes a Payload.Data item into its constituent ConfigurationEnvelope
81+
func BreakOutPayloadDataToConfigurationEnvelope(payloadData []byte) (*pb.ConfigurationEnvelope, error) {
82+
if payloadData == nil {
83+
return nil, fmt.Errorf("input Payload data is null\n")
84+
}
85+
86+
configEnvelope := &pb.ConfigurationEnvelope{}
87+
err := proto.Unmarshal(payloadData, configEnvelope)
88+
if err != nil {
89+
return nil, err
90+
}
91+
92+
return configEnvelope, nil
93+
} //BreakOutPayloadToConfigurationEnvelope
94+
95+
// BreakOutConfigEnvelopeToConfigItemsOrPanic calls BreakOutConfigEnvelopeToConfigItems() but panics on error
96+
func BreakOutConfigEnvelopeToConfigItemsOrPanic(configEnvelope *pb.ConfigurationEnvelope) ([]*pb.ConfigurationItem, [][]*pb.ConfigurationSignature) {
97+
configItems, configSignatures, err := BreakOutConfigEnvelopeToConfigItems(configEnvelope)
98+
if err != nil {
99+
panic(err)
100+
}
101+
return configItems, configSignatures
102+
} // BreakOutConfigEnvelopeToConfigItemsOrPanic
103+
104+
// BreakOutConfigEnvelopeToConfigItems decomposes a ConfigurationEnvelope to its constituent ConfigurationItems and ConfigurationSignatures
105+
// Note that a ConfigurationItem can have multiple signatures so each element in the returned ConfigurationItems slice is associated with a slice of ConfigurationSignatures
106+
func BreakOutConfigEnvelopeToConfigItems(configEnvelope *pb.ConfigurationEnvelope) ([]*pb.ConfigurationItem, [][]*pb.ConfigurationSignature, error) {
107+
if configEnvelope == nil {
108+
return nil, nil, fmt.Errorf("BreakOutConfigEnvelopeToConfigItems received null input\n")
109+
}
110+
111+
var configItems []*pb.ConfigurationItem
112+
var configSignatures [][]*pb.ConfigurationSignature
113+
114+
var err error
115+
var configItem *pb.ConfigurationItem
116+
for i, signedConfigItem := range configEnvelope.Items {
117+
configItem = &pb.ConfigurationItem{}
118+
err = proto.Unmarshal(signedConfigItem.ConfigurationItem, configItem)
119+
if err != nil {
120+
return nil, nil, fmt.Errorf("BreakOutConfigEnvelopToConfigItems cannot unmarshall signedConfigurationItem: %v\n", err)
121+
}
122+
configItems = append(configItems, configItem)
123+
for _, signedConfigItemSignature := range signedConfigItem.Signatures {
124+
configSignatures[i] = append(configSignatures[i], signedConfigItemSignature)
125+
}
126+
}
127+
128+
return configItems, configSignatures, nil
129+
} // BreakOutConfigEnvelopeToConfigItems
130+
131+
// BreakOutBlockToConfigurationEnvelopeOrPanic calls BreakOutBlockToConfigurationEnvelope() but panics on error
132+
func BreakOutBlockToConfigurationEnvelopeOrPanic(block *pb.Block) (*pb.ConfigurationEnvelope, []byte) {
133+
configEnvelope, envelopeSignature, err := BreakOutBlockToConfigurationEnvelope(block)
134+
if err != nil {
135+
panic(err)
136+
}
137+
return configEnvelope, envelopeSignature
138+
} // BreakOutBlockToConfigurationEnvelopeOrPanic
139+
140+
// BreakOutBlockToConfigurationEnvelope decomposes a configuration transaction Block to its ConfigurationEnvelope
141+
func BreakOutBlockToConfigurationEnvelope(block *pb.Block) (*pb.ConfigurationEnvelope, []byte, error) {
142+
if block == nil || block.Data == nil || len(block.Data.Data) > 1 {
143+
return nil, nil, fmt.Errorf("Block.BlockData is not an array of 1. This is not a configuration transaction\n")
144+
}
145+
146+
payloads, envelopeSignatures, err := BreakOutBlockData(block.Data)
147+
148+
if payloads[0].Header.ChainHeader.Type != int32(pb.HeaderType_CONFIGURATION_TRANSACTION) {
149+
return nil, nil, fmt.Errorf("Payload Header type is not configuration_transaction. This is not a configuration transaction\n")
150+
}
151+
var configEnvelope *pb.ConfigurationEnvelope
152+
configEnvelope, err = BreakOutPayloadDataToConfigurationEnvelope(payloads[0].Data)
153+
if err != nil {
154+
return nil, nil, fmt.Errorf("Error breaking out configurationEnvelope: %v\n", err)
155+
}
156+
157+
return configEnvelope, envelopeSignatures[0], nil
158+
} // BreakOutPayloadToConfigurationEnvelope

protos/utils/configtxutils_test.go

+226
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package utils
18+
19+
import (
20+
"bytes"
21+
"testing"
22+
"time"
23+
24+
pb "github.com/hyperledger/fabric/protos/common"
25+
26+
"github.com/golang/protobuf/proto"
27+
"github.com/golang/protobuf/ptypes/timestamp"
28+
)
29+
30+
// added comment to force gerrit commit
31+
32+
func TestBreakOutBlockDataBadData(t *testing.T) {
33+
fakeBlockData := &pb.BlockData{}
34+
payloads, sigs, _ := BreakOutBlockData(fakeBlockData)
35+
if len(payloads) > 0 || len(sigs) > 0 {
36+
t.Errorf("TestBreakOutBlockData should not work with blank input.\n")
37+
} // TestBreakOutBlockDataBadData
38+
} // TestBreakOutBlockDataBadData
39+
40+
func TestBreakOutBlockData(t *testing.T) {
41+
block := testBlock()
42+
payloads, _, _ := BreakOutBlockData(block.Data) // TODO: test for signature
43+
if len(payloads) != 1 {
44+
t.Errorf("TestBreakOutBlock did not unmarshall to array of 1 payloads\n")
45+
}
46+
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")) {
47+
t.Errorf("TestBreakOutBlockData payload header is %+v . Expected type is %v and Version == 1\n", payloads[0].Header.ChainHeader, int32(pb.HeaderType_CONFIGURATION_TRANSACTION))
48+
}
49+
if !bytes.Equal(payloads[0].Data, []byte("test")) {
50+
t.Errorf("TestBreakOutBlockData payload data is %s . Expected 'test'\n", payloads[0].Data)
51+
}
52+
53+
} // TesttBreakOutBlockData
54+
55+
func TestBreakOutPayloadDataToConfigurationEnvelopePanic(t *testing.T) {
56+
defer func() {
57+
if r := recover(); r == nil {
58+
t.Errorf("TestBreakOutPayloadDataToConfigurationEnvelopePanic should have panicked")
59+
}
60+
}()
61+
_ = BreakOutPayloadDataToConfigurationEnvelopeOrPanic(nil)
62+
} // TestBreakOutPayloadDataToConfigurationEnvelopePanic
63+
64+
func TestBreakOutPayloadDataToConfigurationEnvelopeBadData(t *testing.T) {
65+
_, err := BreakOutPayloadDataToConfigurationEnvelope(nil)
66+
if err == nil {
67+
t.Errorf("TestBreakOutPayloadDataToConfigurationEnvelopeBadData should have returned error on null input\n ")
68+
}
69+
} // TestBreakOutPayloadDataToConfigurationEnvelopeBadData
70+
71+
func TestBreakOutPayloadDataToConfigurationEnvelope(t *testing.T) {
72+
payload := testPayloadConfigEnvelope()
73+
configEnvelope, _ := BreakOutPayloadDataToConfigurationEnvelope(payload.Data)
74+
if len(configEnvelope.Items) != 1 {
75+
t.Errorf("TestBreakOutPayloadDataToConfigurationEnvelope: configEnvelope.Items array should have 1 item")
76+
}
77+
} // TestBreakOutPayloadDataToConfigurationEnvelope
78+
79+
func TestBreakOutConfigEnvelopeToConfigItemsPanic(t *testing.T) {
80+
defer func() {
81+
if r := recover(); r == nil {
82+
t.Errorf("TestBreakOutConfigEnvelopeToConfigItemsPanic should have panicked")
83+
}
84+
}()
85+
_, _ = BreakOutConfigEnvelopeToConfigItemsOrPanic(nil)
86+
} // TestBreakOutConfigEnvelopeToConfigItemsPanic
87+
88+
func TestBreakOutConfigEnvelopeToConfigItemsBadData(t *testing.T) {
89+
_, _, err := BreakOutConfigEnvelopeToConfigItems(nil)
90+
if err == nil {
91+
t.Errorf("TestBreakOutConfigEnvelopeToConfigItemsBadData not handling nil input\n")
92+
}
93+
} // TestBreakOutConfigEnvelopeToConfigItemsBadData
94+
95+
func TestBreakOutConfigEnvelopeToConfigItems(t *testing.T) {
96+
configEnv := testConfigurationEnvelope()
97+
configItems, _, _ := BreakOutConfigEnvelopeToConfigItems(configEnv) // TODO: test signatures
98+
if len(configItems) != 1 {
99+
t.Errorf("TestBreakOutPayloadDataToConfigurationEnvelope did not return array of 1 config item\n")
100+
}
101+
if configItems[0].Header.Type != int32(pb.HeaderType_CONFIGURATION_TRANSACTION) || !bytes.Equal(configItems[0].Header.ChainID, []byte("test")) {
102+
t.Errorf("TestBreakOutConfigEnvelopeToConfigItems, configItem header does not match original %+v . Expected config_transaction and chainid 'test'\n", configItems[0].Header)
103+
}
104+
if configItems[0].Type != pb.ConfigurationItem_Orderer || configItems[0].Key != "abc" || !bytes.Equal(configItems[0].Value, []byte("test")) {
105+
t.Errorf("TestBreakOutConfigEnvelopeToConfigItems configItem type,Key,Value do not match original %+v\n. Expected orderer, 'abc', 'test'", configItems[0])
106+
}
107+
} // TestBreakOutConfigEnvelopeToConfigItems
108+
109+
func TestBreakOutBlockToConfigurationEnvelopePanic(t *testing.T) {
110+
defer func() {
111+
if r := recover(); r == nil {
112+
t.Errorf("TestBreakOutBlockToConfigurationEnvelopePanic should have panicked")
113+
}
114+
}()
115+
_, _ = BreakOutBlockToConfigurationEnvelopeOrPanic(nil)
116+
} // TestBreakOutBlockToConfigurationEnvelopePanic
117+
118+
func TestBreakOutBlockToConfigurationEnvelopeBadData(t *testing.T) {
119+
_, _, err := BreakOutBlockToConfigurationEnvelope(nil)
120+
if err == nil {
121+
t.Errorf("TestBreakOutBlockToConfigurationEnvelopeBadData should have rejected null input\n")
122+
}
123+
} // TestBreakOutBlockToConfigurationEnvelopeBadData
124+
125+
func TestBreakOutBlockToConfigurationEnvelope(t *testing.T) {
126+
block := testConfigurationBlock()
127+
configEnvelope, _, _ := BreakOutBlockToConfigurationEnvelope(block) // TODO: test envelope signature
128+
if len(configEnvelope.Items) != 1 {
129+
t.Errorf("TestBreakOutBlockToConfigurationEnvelope should have an array of 1 signedConfigurationItems\n")
130+
}
131+
} // TestBreakOutBlockToConfigurationEnvelopeBadData
132+
133+
// Helper functions
134+
func testChainHeader() *pb.ChainHeader {
135+
return &pb.ChainHeader{
136+
Type: int32(pb.HeaderType_CONFIGURATION_TRANSACTION),
137+
Version: 1,
138+
Timestamp: &timestamp.Timestamp{
139+
Seconds: time.Now().Unix(),
140+
Nanos: 0,
141+
},
142+
ChainID: []byte("test"),
143+
}
144+
}
145+
146+
func testPayloadHeader() *pb.Header {
147+
return &pb.Header{
148+
ChainHeader: testChainHeader(),
149+
SignatureHeader: nil,
150+
}
151+
}
152+
153+
func testPayload() *pb.Payload {
154+
return &pb.Payload{
155+
Header: testPayloadHeader(),
156+
Data: []byte("test"),
157+
}
158+
}
159+
160+
func testEnvelope() *pb.Envelope {
161+
// No need to set the signature
162+
payloadBytes, _ := proto.Marshal(testPayload())
163+
return &pb.Envelope{Payload: payloadBytes}
164+
}
165+
166+
func testPayloadConfigEnvelope() *pb.Payload {
167+
data, _ := proto.Marshal(testConfigurationEnvelope())
168+
return &pb.Payload{
169+
Header: testPayloadHeader(),
170+
Data: data,
171+
}
172+
}
173+
174+
func testEnvelopePayloadConfigEnv() *pb.Envelope {
175+
payloadBytes, _ := proto.Marshal(testPayloadConfigEnvelope())
176+
return &pb.Envelope{Payload: payloadBytes}
177+
} // testEnvelopePayloadConfigEnv
178+
179+
func testConfigurationBlock() *pb.Block {
180+
envelopeBytes, _ := proto.Marshal(testEnvelopePayloadConfigEnv())
181+
return &pb.Block{
182+
Data: &pb.BlockData{
183+
Data: [][]byte{envelopeBytes},
184+
},
185+
}
186+
}
187+
188+
func testConfigurationEnvelope() *pb.ConfigurationEnvelope {
189+
chainHeader := testChainHeader()
190+
configItem := makeConfigurationItem(chainHeader, pb.ConfigurationItem_Orderer, 0, "defaultPolicyID", "abc", []byte("test"))
191+
signedConfigItem, _ := makeSignedConfigurationItem(configItem, nil)
192+
return makeConfigurationEnvelope(signedConfigItem)
193+
} // testConfigurationEnvelope
194+
195+
func testBlock() *pb.Block {
196+
// No need to set the block's Header, or Metadata
197+
envelopeBytes, _ := proto.Marshal(testEnvelope())
198+
return &pb.Block{
199+
Data: &pb.BlockData{
200+
Data: [][]byte{envelopeBytes},
201+
},
202+
}
203+
}
204+
205+
func makeConfigurationItem(ch *pb.ChainHeader, configItemType pb.ConfigurationItem_ConfigurationType, lastModified uint64, modPolicyID string, key string, value []byte) *pb.ConfigurationItem {
206+
return &pb.ConfigurationItem{
207+
Header: ch,
208+
Type: configItemType,
209+
LastModified: lastModified,
210+
ModificationPolicy: modPolicyID,
211+
Key: key,
212+
Value: value,
213+
}
214+
}
215+
216+
func makeSignedConfigurationItem(configItem *pb.ConfigurationItem, signatures []*pb.ConfigurationSignature) (*pb.SignedConfigurationItem, error) {
217+
configItemBytes, _ := proto.Marshal(configItem)
218+
return &pb.SignedConfigurationItem{
219+
ConfigurationItem: configItemBytes,
220+
Signatures: signatures,
221+
}, nil
222+
} // makeSignedConfigurationItem
223+
224+
func makeConfigurationEnvelope(items ...*pb.SignedConfigurationItem) *pb.ConfigurationEnvelope {
225+
return &pb.ConfigurationEnvelope{Items: items}
226+
}

0 commit comments

Comments
 (0)