Skip to content

Commit 3e8d0ae

Browse files
author
Jason Yellick
committed
[FAB-1613] Add configuration item templates
https://jira.hyperledger.org/browse/FAB-1613 This changeset adds the notion of templating configuration transactions to enable clean construction of configuration transactions, compositing multiple pieces of configuration from different pieces of code and ultimately producing chain configuration transaction templates. Change-Id: I63f054252e68112c945d06d912b38a2130da8144 Signed-off-by: Jason Yellick <[email protected]>
1 parent 86213ca commit 3e8d0ae

File tree

4 files changed

+275
-25
lines changed

4 files changed

+275
-25
lines changed

common/configtx/template.go

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
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 configtx
18+
19+
import (
20+
"github.com/hyperledger/fabric/common/util"
21+
cb "github.com/hyperledger/fabric/protos/common"
22+
ab "github.com/hyperledger/fabric/protos/orderer"
23+
"github.com/hyperledger/fabric/protos/utils"
24+
25+
"github.com/golang/protobuf/proto"
26+
)
27+
28+
const (
29+
CreationPolicyKey = "CreationPolicy"
30+
msgVersion = int32(0)
31+
epoch = 0
32+
)
33+
34+
// Template can be used to faciliate creation of configuration transactions
35+
type Template interface {
36+
// Items returns a set of SignedConfigurationItems for the given chainID
37+
Items(chainID string) ([]*cb.SignedConfigurationItem, error)
38+
}
39+
40+
type simpleTemplate struct {
41+
items []*cb.ConfigurationItem
42+
}
43+
44+
// NewSimpleTemplate creates a Template using the supplied items
45+
func NewSimpleTemplate(items ...*cb.ConfigurationItem) Template {
46+
return &simpleTemplate{items: items}
47+
}
48+
49+
// Items returns a set of SignedConfigurationItems for the given chainID, and errors only on marshaling errors
50+
func (st *simpleTemplate) Items(chainID string) ([]*cb.SignedConfigurationItem, error) {
51+
signedItems := make([]*cb.SignedConfigurationItem, len(st.items))
52+
for i := range st.items {
53+
st.items[i].Header = &cb.ChainHeader{ChainID: chainID}
54+
mItem, err := proto.Marshal(st.items[i])
55+
if err != nil {
56+
return nil, err
57+
}
58+
signedItems[i] = &cb.SignedConfigurationItem{ConfigurationItem: mItem}
59+
}
60+
61+
return signedItems, nil
62+
}
63+
64+
type compositeTemplate struct {
65+
templates []Template
66+
}
67+
68+
// NewSimpleTemplate creates a Template using the source Templates
69+
func NewCompositeTemplate(templates ...Template) Template {
70+
return &compositeTemplate{templates: templates}
71+
}
72+
73+
// Items returns a set of SignedConfigurationItems for the given chainID, and errors only on marshaling errors
74+
func (ct *compositeTemplate) Items(chainID string) ([]*cb.SignedConfigurationItem, error) {
75+
items := make([][]*cb.SignedConfigurationItem, len(ct.templates))
76+
var err error
77+
for i := range ct.templates {
78+
items[i], err = ct.templates[i].Items(chainID)
79+
if err != nil {
80+
return nil, err
81+
}
82+
}
83+
84+
return join(items...), nil
85+
}
86+
87+
type newChainTemplate struct {
88+
creationPolicy string
89+
template Template
90+
}
91+
92+
// NewChainCreationTemplate takes a CreationPolicy and a Template to produce a Template which outputs an appropriately
93+
// constructed list of SignedConfigurationItem including an appropriate digest. Note, using this Template in
94+
// a CompositeTemplate will invalidate the CreationPolicy
95+
func NewChainCreationTemplate(creationPolicy string, template Template) Template {
96+
return &newChainTemplate{
97+
creationPolicy: creationPolicy,
98+
template: template,
99+
}
100+
}
101+
102+
// Items returns a set of SignedConfigurationItems for the given chainID, and errors only on marshaling errors
103+
func (nct *newChainTemplate) Items(chainID string) ([]*cb.SignedConfigurationItem, error) {
104+
items, err := nct.template.Items(chainID)
105+
if err != nil {
106+
return nil, err
107+
}
108+
109+
creationPolicy := &cb.SignedConfigurationItem{
110+
ConfigurationItem: utils.MarshalOrPanic(&cb.ConfigurationItem{
111+
Header: utils.MakeChainHeader(cb.HeaderType_CONFIGURATION_ITEM, msgVersion, chainID, epoch),
112+
Key: CreationPolicyKey,
113+
Value: utils.MarshalOrPanic(&ab.CreationPolicy{
114+
Policy: CreationPolicyKey,
115+
Digest: HashItems(items),
116+
}),
117+
}),
118+
}
119+
120+
return join([]*cb.SignedConfigurationItem{creationPolicy}, items), nil
121+
}
122+
123+
// HashItems is a utility method for computing the hash of the concatenation of the marshaled ConfigurationItems
124+
// in a []*cb.SignedConfigurationItem
125+
func HashItems(items []*cb.SignedConfigurationItem) []byte {
126+
sourceBytes := make([][]byte, len(items))
127+
for i := range items {
128+
sourceBytes[i] = items[i].ConfigurationItem
129+
}
130+
return util.ComputeCryptoHash(util.ConcatenateBytes(sourceBytes...))
131+
}
132+
133+
// join takes a number of SignedConfigurationItem slices and produces a single item
134+
func join(sets ...[]*cb.SignedConfigurationItem) []*cb.SignedConfigurationItem {
135+
total := 0
136+
for _, set := range sets {
137+
total += len(set)
138+
}
139+
result := make([]*cb.SignedConfigurationItem, total)
140+
last := 0
141+
for _, set := range sets {
142+
for i := range set {
143+
result[i+last] = set[i]
144+
}
145+
last += len(set)
146+
}
147+
148+
return result
149+
}

common/configtx/template_test.go

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
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 configtx
18+
19+
import (
20+
"fmt"
21+
"testing"
22+
23+
cb "github.com/hyperledger/fabric/protos/common"
24+
"github.com/hyperledger/fabric/protos/utils"
25+
)
26+
27+
func verifyItemsResult(t *testing.T, template Template, count int) {
28+
newChainID := "foo"
29+
30+
result, err := template.Items(newChainID)
31+
if err != nil {
32+
t.Fatalf("Should not have errored")
33+
}
34+
35+
if len(result) != count {
36+
t.Errorf("Expected %d items, but got %d", count, len(result))
37+
}
38+
39+
for i, signedItem := range result {
40+
item := utils.UnmarshalConfigurationItemOrPanic(signedItem.ConfigurationItem)
41+
if item.Header.ChainID != newChainID {
42+
t.Errorf("Should have appropriately set new chainID")
43+
}
44+
if expected := fmt.Sprintf("%d", i); string(item.Value) != expected {
45+
t.Errorf("Expected %s but got %s", expected, item.Value)
46+
}
47+
}
48+
}
49+
50+
func TestSimpleTemplate(t *testing.T) {
51+
simple := NewSimpleTemplate(
52+
&cb.ConfigurationItem{Value: []byte("0")},
53+
&cb.ConfigurationItem{Value: []byte("1")},
54+
)
55+
56+
verifyItemsResult(t, simple, 2)
57+
}
58+
59+
func TestCompositeTemplate(t *testing.T) {
60+
composite := NewCompositeTemplate(
61+
NewSimpleTemplate(
62+
&cb.ConfigurationItem{Value: []byte("0")},
63+
&cb.ConfigurationItem{Value: []byte("1")},
64+
),
65+
NewSimpleTemplate(
66+
&cb.ConfigurationItem{Value: []byte("2")},
67+
),
68+
)
69+
70+
verifyItemsResult(t, composite, 3)
71+
}
72+
73+
func TestNewChainTemplate(t *testing.T) {
74+
simple := NewSimpleTemplate(
75+
&cb.ConfigurationItem{Value: []byte("1")},
76+
&cb.ConfigurationItem{Value: []byte("2")},
77+
)
78+
79+
creationPolicy := "Test"
80+
nct := NewChainCreationTemplate(creationPolicy, simple)
81+
82+
newChainID := "foo"
83+
items, err := nct.Items(newChainID)
84+
if err != nil {
85+
t.Fatalf("Error creation a chain creation configuration")
86+
}
87+
88+
if expected := 3; len(items) != expected {
89+
t.Fatalf("Expected %d items, but got %d", expected, len(items))
90+
}
91+
92+
for i, signedItem := range items {
93+
item := utils.UnmarshalConfigurationItemOrPanic(signedItem.ConfigurationItem)
94+
if item.Header.ChainID != newChainID {
95+
t.Errorf("Should have appropriately set new chainID")
96+
}
97+
if i == 0 {
98+
if item.Key != CreationPolicyKey {
99+
t.Errorf("First item should have been the creation policy")
100+
}
101+
} else {
102+
if expected := fmt.Sprintf("%d", i); string(item.Value) != expected {
103+
t.Errorf("Expected %s but got %s", expected, item.Value)
104+
}
105+
}
106+
}
107+
}

orderer/multichain/systemchain.go

+5-11
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121

2222
"github.com/hyperledger/fabric/common/configtx"
2323
"github.com/hyperledger/fabric/common/policies"
24-
"github.com/hyperledger/fabric/common/util"
2524
"github.com/hyperledger/fabric/orderer/common/filter"
2625
"github.com/hyperledger/fabric/orderer/common/sharedconfig"
2726
cb "github.com/hyperledger/fabric/protos/common"
@@ -150,6 +149,10 @@ func (sc *systemChain) proposeChain(configTx *cb.Envelope) cb.Status {
150149
}
151150

152151
func (sc *systemChain) authorize(configEnvelope *cb.ConfigurationEnvelope) cb.Status {
152+
if len(configEnvelope.Items) == 0 {
153+
return cb.Status_BAD_REQUEST
154+
}
155+
153156
creationConfigItem := &cb.ConfigurationItem{}
154157
err := proto.Unmarshal(configEnvelope.Items[0].ConfigurationItem, creationConfigItem)
155158
if err != nil {
@@ -191,16 +194,7 @@ func (sc *systemChain) authorize(configEnvelope *cb.ConfigurationEnvelope) cb.St
191194
// XXX actually do policy signature validation
192195
_ = policy
193196

194-
var remainingBytes []byte
195-
for i, item := range configEnvelope.Items {
196-
if i == 0 {
197-
// Do not include the creation policy
198-
continue
199-
}
200-
remainingBytes = util.ConcatenateBytes(remainingBytes, item.ConfigurationItem)
201-
}
202-
203-
configHash := util.ComputeCryptoHash(remainingBytes)
197+
configHash := configtx.HashItems(configEnvelope.Items[1:])
204198

205199
if !bytes.Equal(configHash, creationPolicy.Digest) {
206200
logger.Debugf("Validly signed chain creation did not contain correct digest for remaining configuration %x vs. %x", configHash, creationPolicy.Digest)

protos/utils/blockutils.go

+14-14
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"github.com/golang/protobuf/proto"
2525

2626
"github.com/hyperledger/fabric/common/cauthdsl"
27-
"github.com/hyperledger/fabric/common/configtx"
2827
"github.com/hyperledger/fabric/msp"
2928
cb "github.com/hyperledger/fabric/protos/common"
3029
ab "github.com/hyperledger/fabric/protos/orderer"
@@ -132,14 +131,15 @@ func MakeConfigurationBlock(testChainID string) (*cb.Block, error) {
132131
}
133132

134133
const (
135-
batchSize = 10
136-
consensusType = "solo"
137-
epoch = uint64(0)
138-
messageVersion = int32(1)
139-
lastModified = uint64(0)
140-
consensusTypeKey = "ConsensusType"
141-
batchSizeKey = "BatchSize"
142-
mspKey = "MSP"
134+
batchSize = 10
135+
consensusType = "solo"
136+
epoch = uint64(0)
137+
messageVersion = int32(1)
138+
lastModified = uint64(0)
139+
consensusTypeKey = "ConsensusType"
140+
batchSizeKey = "BatchSize"
141+
mspKey = "MSP"
142+
XXX_DefaultModificationPolicyID = "DefaultModificationPolicy" // Break an import cycle during work to remove the below configtx construction methods
143143
)
144144

145145
func createSignedConfigItem(chainID string,
@@ -162,21 +162,21 @@ func encodeConsensusType(testChainID string) *cb.SignedConfigurationItem {
162162
return createSignedConfigItem(testChainID,
163163
consensusTypeKey,
164164
MarshalOrPanic(&ab.ConsensusType{Type: consensusType}),
165-
configtx.DefaultModificationPolicyID)
165+
XXX_DefaultModificationPolicyID)
166166
}
167167

168168
func encodeBatchSize(testChainID string) *cb.SignedConfigurationItem {
169169
return createSignedConfigItem(testChainID,
170170
batchSizeKey,
171171
MarshalOrPanic(&ab.BatchSize{MaxMessageCount: batchSize}),
172-
configtx.DefaultModificationPolicyID)
172+
XXX_DefaultModificationPolicyID)
173173
}
174174

175175
func lockDefaultModificationPolicy(testChainID string) *cb.SignedConfigurationItem {
176176
return createSignedConfigItem(testChainID,
177-
configtx.DefaultModificationPolicyID,
177+
XXX_DefaultModificationPolicyID,
178178
MarshalOrPanic(MakePolicyOrPanic(cauthdsl.RejectAllPolicy)),
179-
configtx.DefaultModificationPolicyID)
179+
XXX_DefaultModificationPolicyID)
180180
}
181181

182182
// This function is needed to locate the MSP test configuration when running
@@ -200,5 +200,5 @@ func encodeMSP(testChainID string) *cb.SignedConfigurationItem {
200200
return createSignedConfigItem(testChainID,
201201
mspKey,
202202
MarshalOrPanic(conf),
203-
configtx.DefaultModificationPolicyID)
203+
XXX_DefaultModificationPolicyID)
204204
}

0 commit comments

Comments
 (0)