Skip to content

Commit cb9a29b

Browse files
committed
[FAB-996] Introduce orderer/commons/util package
https://jira.hyperledger.org/browse/FAB-996 The goal is to avoid rewriting the same functions again and again. For functions that may return an error, two variants and provided: - The plain one that returns the error and allows the caller to handle it - The *OrPanic one that panics if an error comes up. It makes sense to use the *OrPanic functions in contexts where you either know the chances of an error are zero, or you're willing to defer/recover in the calling code. The static package is a perfect example of this. (Also see note below.) The *OrPanic functions can be used inline as function arguments since they return a single object. This changeset ripples into the static bootstrapper, simplifying it substantially, and providing a clear guide into creating a sample genesis block. The Make* functions of the util package are tested in the static bootstrapper's tests. Unit tests for functions that just create proto messages don't make sense, as any errors in the message construction results in immediate 'go build' errors. (All the other functions in the util package come with unit tests, as they should.) UPDATE: Revised for the latest changes in protos. Change-Id: I4212d1366cca556fe4c5c6cb16ea3c1f52dd7503 Signed-off-by: Kostas Christidis <[email protected]>
1 parent 9bd29d3 commit cb9a29b

File tree

5 files changed

+356
-109
lines changed

5 files changed

+356
-109
lines changed

orderer/common/bootstrap/static/static.go

+21-88
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,17 @@ package static
1818

1919
import (
2020
"fmt"
21-
"time"
2221

2322
"github.com/hyperledger/fabric/core/crypto/primitives"
2423
"github.com/hyperledger/fabric/orderer/common/bootstrap"
2524
"github.com/hyperledger/fabric/orderer/common/cauthdsl"
2625
"github.com/hyperledger/fabric/orderer/common/configtx"
26+
"github.com/hyperledger/fabric/orderer/common/util"
2727
cb "github.com/hyperledger/fabric/protos/common"
28-
29-
"github.com/golang/protobuf/proto"
30-
"github.com/golang/protobuf/ptypes/timestamp"
3128
)
3229

30+
const msgVersion = int32(1)
31+
3332
type bootstrapper struct {
3433
chainID []byte
3534
}
@@ -43,93 +42,27 @@ func New() bootstrap.Helper {
4342
return &bootstrapper{chainID}
4443
}
4544

46-
// errorlessMarshal prevents poluting this code with panics
47-
// If the genesis block cannot be created, the system cannot start so panic is correct
48-
func errorlessMarshal(thing proto.Message) []byte {
49-
data, err := proto.Marshal(thing)
50-
if err != nil {
51-
panic(err)
52-
}
53-
return data
54-
}
55-
56-
func makeChainHeader(headerType cb.HeaderType, version int32, chainID []byte, epoch uint64) *cb.ChainHeader {
57-
return &cb.ChainHeader{
58-
Type: int32(headerType),
59-
Version: version,
60-
Timestamp: &timestamp.Timestamp{
61-
Seconds: time.Now().Unix(),
62-
Nanos: 0,
63-
},
64-
ChainID: chainID,
65-
Epoch: epoch,
66-
}
67-
}
68-
69-
func makeSignatureHeader(serializedCreatorCertChain []byte, nonce []byte) *cb.SignatureHeader {
70-
return &cb.SignatureHeader{
71-
Creator: serializedCreatorCertChain,
72-
Nonce: nonce,
73-
}
74-
}
75-
76-
func (b *bootstrapper) makeSignedConfigurationItem(configurationItemType cb.ConfigurationItem_ConfigurationType, modificationPolicyID string, key string, value []byte) *cb.SignedConfigurationItem {
77-
marshaledConfigurationItem := errorlessMarshal(&cb.ConfigurationItem{
78-
Header: makeChainHeader(cb.HeaderType_CONFIGURATION_ITEM, 1, b.chainID, 0),
79-
Type: configurationItemType,
80-
LastModified: 0,
81-
ModificationPolicy: modificationPolicyID,
82-
Key: key,
83-
Value: value,
84-
})
85-
86-
return &cb.SignedConfigurationItem{
87-
ConfigurationItem: marshaledConfigurationItem,
88-
Signatures: nil,
89-
}
90-
}
91-
92-
func (b *bootstrapper) makeConfigurationEnvelope(items ...*cb.SignedConfigurationItem) *cb.ConfigurationEnvelope {
93-
return &cb.ConfigurationEnvelope{
94-
Items: items,
95-
}
96-
}
97-
98-
func (b *bootstrapper) makeEnvelope(configurationEnvelope *cb.ConfigurationEnvelope) *cb.Envelope {
99-
nonce, err := primitives.GetRandomNonce()
100-
if err != nil {
101-
panic(fmt.Errorf("Cannot generate random nonce: %s", err))
102-
}
103-
marshaledPayload := errorlessMarshal(&cb.Payload{
104-
Header: &cb.Header{
105-
ChainHeader: makeChainHeader(cb.HeaderType_CONFIGURATION_TRANSACTION, 1, b.chainID, 0),
106-
SignatureHeader: makeSignatureHeader(nil, nonce),
107-
},
108-
Data: errorlessMarshal(configurationEnvelope),
109-
})
110-
return &cb.Envelope{
111-
Payload: marshaledPayload,
112-
Signature: nil,
113-
}
114-
}
115-
116-
func sigPolicyToPolicy(sigPolicy *cb.SignaturePolicyEnvelope) []byte {
117-
policy := &cb.Policy{
118-
Type: &cb.Policy_SignaturePolicy{
119-
SignaturePolicy: sigPolicy,
120-
},
121-
}
122-
return errorlessMarshal(policy)
123-
}
124-
12545
// GenesisBlock returns the genesis block to be used for bootstrapping
12646
func (b *bootstrapper) GenesisBlock() (*cb.Block, error) {
12747
// Lock down the default modification policy to prevent any further policy modifications
128-
lockdownDefaultModificationPolicy := b.makeSignedConfigurationItem(cb.ConfigurationItem_Policy, configtx.DefaultModificationPolicyID, configtx.DefaultModificationPolicyID, sigPolicyToPolicy(cauthdsl.RejectAllPolicy))
129-
130-
blockData := &cb.BlockData{
131-
Data: [][]byte{errorlessMarshal(b.makeEnvelope(b.makeConfigurationEnvelope(lockdownDefaultModificationPolicy)))},
132-
}
48+
configItemKey := configtx.DefaultModificationPolicyID
49+
configItemValue := util.MarshalOrPanic(util.MakePolicyOrPanic(cauthdsl.RejectAllPolicy))
50+
modPolicy := configtx.DefaultModificationPolicyID
51+
52+
lastModified := uint64(0)
53+
epoch := uint64(0)
54+
configItemChainHeader := util.MakeChainHeader(cb.HeaderType_CONFIGURATION_ITEM, msgVersion, b.chainID, epoch)
55+
configItem := util.MakeConfigurationItem(configItemChainHeader, cb.ConfigurationItem_Policy, lastModified, modPolicy, configItemKey, configItemValue)
56+
signedConfigItem := &cb.SignedConfigurationItem{ConfigurationItem: util.MarshalOrPanic(configItem), Signatures: nil}
57+
58+
configEnvelope := util.MakeConfigurationEnvelope(signedConfigItem)
59+
payloadChainHeader := util.MakeChainHeader(cb.HeaderType_CONFIGURATION_TRANSACTION, configItemChainHeader.Version, b.chainID, epoch)
60+
payloadSignatureHeader := util.MakeSignatureHeader(nil, util.CreateNonceOrPanic())
61+
payloadHeader := util.MakePayloadHeader(payloadChainHeader, payloadSignatureHeader)
62+
payload := &cb.Payload{Header: payloadHeader, Data: util.MarshalOrPanic(configEnvelope)}
63+
envelope := &cb.Envelope{Payload: util.MarshalOrPanic(payload), Signature: nil}
64+
65+
blockData := &cb.BlockData{Data: [][]byte{util.MarshalOrPanic(envelope)}}
13366

13467
return &cb.Block{
13568
Header: &cb.BlockHeader{

orderer/common/bootstrap/static/static_test.go

+13-15
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ import (
2020
"bytes"
2121
"testing"
2222

23+
"github.com/golang/protobuf/proto"
2324
"github.com/hyperledger/fabric/orderer/common/cauthdsl"
2425
"github.com/hyperledger/fabric/orderer/common/configtx"
26+
"github.com/hyperledger/fabric/orderer/common/util"
2527
cb "github.com/hyperledger/fabric/protos/common"
26-
27-
"github.com/golang/protobuf/proto"
2828
)
2929

3030
func TestGenesisBlockCreation(t *testing.T) {
@@ -49,13 +49,19 @@ func TestGenesisBlockHeader(t *testing.T) {
4949
}
5050

5151
func TestGenesisBlockData(t *testing.T) {
52+
defer func() {
53+
if r := recover(); r != nil {
54+
t.Fatalf("OrPanicked unexpectedly: %s", r)
55+
}
56+
}()
57+
5258
expectedBlockDataLength := 1
5359
expectedPayloadChainHeaderType := int32(cb.HeaderType_CONFIGURATION_TRANSACTION)
54-
expectedChainHeaderVersion := int32(1)
60+
expectedChainHeaderVersion := msgVersion
5561
expectedChainHeaderEpoch := uint64(0)
5662
expectedConfigEnvelopeItemsLength := 1
5763
expectedConfigurationItemChainHeaderType := int32(cb.HeaderType_CONFIGURATION_ITEM)
58-
expectedConfigurationItemChainHeaderVersion := int32(1)
64+
expectedConfigurationItemChainHeaderVersion := msgVersion
5965
expectedConfigurationItemType := cb.ConfigurationItem_Policy
6066
expectedConfigEnvelopeSequence := uint64(0)
6167
expectedConfigurationItemModificationPolicy := configtx.DefaultModificationPolicyID
@@ -67,22 +73,14 @@ func TestGenesisBlockData(t *testing.T) {
6773
t.Fatalf("Expected genesis block data length %d, got %d", expectedBlockDataLength, len(genesisBlock.Data.Data))
6874
}
6975

70-
marshaledEnvelope := genesisBlock.Data.Data[0]
71-
envelope := &cb.Envelope{}
72-
if err := proto.Unmarshal(marshaledEnvelope, envelope); err != nil {
73-
t.Fatalf("Expected genesis block to carry an Envelope")
74-
}
76+
envelope := util.ExtractEnvelopeOrPanic(genesisBlock, 0)
7577

7678
envelopeSignature := envelope.Signature
7779
if !bytes.Equal(envelopeSignature, nil) {
7880
t.Fatalf("Expected envelope signature to be nil, got %x", envelopeSignature)
7981
}
8082

81-
marshaledPayload := envelope.Payload
82-
payload := &cb.Payload{}
83-
if err := proto.Unmarshal(marshaledPayload, payload); err != nil {
84-
t.Fatalf("Expected genesis block to carry a Payload")
85-
}
83+
payload := util.ExtractPayloadOrPanic(envelope)
8684

8785
signatureHeader := payload.Header.SignatureHeader
8886
if !bytes.Equal(signatureHeader.Creator, nil) {
@@ -100,7 +98,7 @@ func TestGenesisBlockData(t *testing.T) {
10098
t.Fatalf("Expected payload chain header version %d, got %d", expectedChainHeaderVersion, payloadChainHeader.Version)
10199
}
102100
if payloadChainHeader.Epoch != expectedChainHeaderEpoch {
103-
t.Fatalf("Expected payload chain header epoch to be %d, got %d", expectedChainHeaderEpoch, payloadChainHeader.Epoch)
101+
t.Fatalf("Expected payload chain header header epoch to be %d, got %d", expectedChainHeaderEpoch, payloadChainHeader.Epoch)
104102
}
105103

106104
marshaledConfigurationEnvelope := payload.Data

orderer/common/util/util.go

+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
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 util
18+
19+
import (
20+
"fmt"
21+
"time"
22+
23+
"github.com/hyperledger/fabric/core/crypto/primitives"
24+
cb "github.com/hyperledger/fabric/protos/common"
25+
26+
"github.com/golang/protobuf/proto"
27+
"github.com/golang/protobuf/ptypes/timestamp"
28+
)
29+
30+
// MarshalOrPanic serializes a protobuf message and panics if this operation fails.
31+
func MarshalOrPanic(pb proto.Message) []byte {
32+
data, err := proto.Marshal(pb)
33+
if err != nil {
34+
panic(err)
35+
}
36+
return data
37+
}
38+
39+
// Marshal serializes a protobuf message.
40+
func Marshal(pb proto.Message) ([]byte, error) {
41+
return proto.Marshal(pb)
42+
}
43+
44+
// CreateNonceOrPanic generates a nonce using the crypto/primitives package
45+
// and panics if this operation fails.
46+
func CreateNonceOrPanic() []byte {
47+
nonce, err := primitives.GetRandomNonce()
48+
if err != nil {
49+
panic(fmt.Errorf("Cannot generate random nonce: %s", err))
50+
}
51+
return nonce
52+
}
53+
54+
// CreateNonce generates a nonce using the crypto/primitives package.
55+
func CreateNonce() ([]byte, error) {
56+
nonce, err := primitives.GetRandomNonce()
57+
if err != nil {
58+
return nil, fmt.Errorf("Cannot generate random nonce: %s", err)
59+
}
60+
return nonce, nil
61+
}
62+
63+
// ExtractEnvelopeOrPanic retrieves the requested envelope from a given block and unmarshals it -- it panics if either of these operation fail.
64+
func ExtractEnvelopeOrPanic(block *cb.Block, index int) *cb.Envelope {
65+
envelopeCount := len(block.Data.Data)
66+
if index < 0 || index >= envelopeCount {
67+
panic("Envelope index out of bounds")
68+
}
69+
marshaledEnvelope := block.Data.Data[index]
70+
envelope := &cb.Envelope{}
71+
if err := proto.Unmarshal(marshaledEnvelope, envelope); err != nil {
72+
panic(fmt.Errorf("Block data does not carry an envelope at index %d: %s", index, err))
73+
}
74+
return envelope
75+
}
76+
77+
// ExtractEnvelope retrieves the requested envelope from a given block and unmarshals it.
78+
func ExtractEnvelope(block *cb.Block, index int) (*cb.Envelope, error) {
79+
envelopeCount := len(block.Data.Data)
80+
if index < 0 || index >= envelopeCount {
81+
return nil, fmt.Errorf("Envelope index out of bounds")
82+
}
83+
marshaledEnvelope := block.Data.Data[index]
84+
envelope := &cb.Envelope{}
85+
if err := proto.Unmarshal(marshaledEnvelope, envelope); err != nil {
86+
return nil, fmt.Errorf("Block data does not carry an envelope at index %d: %s", index, err)
87+
}
88+
return envelope, nil
89+
}
90+
91+
// ExtractPayloadOrPanic retrieves the payload of a given envelope and unmarshals it -- it panics if either of these operations fail.
92+
func ExtractPayloadOrPanic(envelope *cb.Envelope) *cb.Payload {
93+
payload := &cb.Payload{}
94+
if err := proto.Unmarshal(envelope.Payload, payload); err != nil {
95+
panic(fmt.Errorf("Envelope does not carry a Payload: %s", err))
96+
}
97+
return payload
98+
}
99+
100+
// ExtractPayload retrieves the payload of a given envelope and unmarshals it.
101+
func ExtractPayload(envelope *cb.Envelope) (*cb.Payload, error) {
102+
payload := &cb.Payload{}
103+
if err := proto.Unmarshal(envelope.Payload, payload); err != nil {
104+
return nil, fmt.Errorf("Envelope does not carry a Payload: %s", err)
105+
}
106+
return payload, nil
107+
}
108+
109+
// MakeChainHeader creates a ChainHeader.
110+
func MakeChainHeader(headerType cb.HeaderType, version int32, chainID []byte, epoch uint64) *cb.ChainHeader {
111+
return &cb.ChainHeader{
112+
Type: int32(headerType),
113+
Version: version,
114+
Timestamp: &timestamp.Timestamp{
115+
Seconds: time.Now().Unix(),
116+
Nanos: 0,
117+
},
118+
ChainID: chainID,
119+
Epoch: epoch,
120+
}
121+
}
122+
123+
// MakeSignatureHeader creates a SignatureHeader.
124+
func MakeSignatureHeader(serializedCreatorCertChain []byte, nonce []byte) *cb.SignatureHeader {
125+
return &cb.SignatureHeader{
126+
Creator: serializedCreatorCertChain,
127+
Nonce: nonce,
128+
}
129+
}
130+
131+
// MakePayloadHeader creates a Payload Header.
132+
func MakePayloadHeader(ch *cb.ChainHeader, sh *cb.SignatureHeader) *cb.Header {
133+
return &cb.Header{
134+
ChainHeader: ch,
135+
SignatureHeader: sh,
136+
}
137+
}
138+
139+
// MakeConfigurationItem makes a ConfigurationItem.
140+
func MakeConfigurationItem(ch *cb.ChainHeader, configItemType cb.ConfigurationItem_ConfigurationType, lastModified uint64, modPolicyID string, key string, value []byte) *cb.ConfigurationItem {
141+
return &cb.ConfigurationItem{
142+
Header: ch,
143+
Type: configItemType,
144+
LastModified: lastModified,
145+
ModificationPolicy: modPolicyID,
146+
Key: key,
147+
Value: value,
148+
}
149+
}
150+
151+
// MakeConfigurationEnvelope makes a ConfigurationEnvelope.
152+
func MakeConfigurationEnvelope(items ...*cb.SignedConfigurationItem) *cb.ConfigurationEnvelope {
153+
return &cb.ConfigurationEnvelope{Items: items}
154+
}
155+
156+
// MakePolicyOrPanic creates a Policy proto message out of a SignaturePolicyEnvelope, and panics if this operation fails.
157+
// NOTE Expand this as more policy types as supported.
158+
func MakePolicyOrPanic(policyEnvelope interface{}) *cb.Policy {
159+
switch pe := policyEnvelope.(type) {
160+
case *cb.SignaturePolicyEnvelope:
161+
return &cb.Policy{
162+
Type: &cb.Policy_SignaturePolicy{
163+
SignaturePolicy: pe,
164+
},
165+
}
166+
default:
167+
panic("Unknown policy envelope type given")
168+
}
169+
}
170+
171+
// MakePolicy creates a Policy proto message out of a SignaturePolicyEnvelope.
172+
// NOTE Expand this as more policy types as supported.
173+
func MakePolicy(policyEnvelope interface{}) (*cb.Policy, error) {
174+
switch pe := policyEnvelope.(type) {
175+
case *cb.SignaturePolicyEnvelope:
176+
return &cb.Policy{
177+
Type: &cb.Policy_SignaturePolicy{
178+
SignaturePolicy: pe,
179+
},
180+
}, nil
181+
default:
182+
return nil, fmt.Errorf("Unknown policy envelope type given")
183+
}
184+
}

0 commit comments

Comments
 (0)