-
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.
[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 <jyellick@us.ibm.com>
- Loading branch information
Jason Yellick
committed
Jan 13, 2017
1 parent
86213ca
commit 3e8d0ae
Showing
4 changed files
with
275 additions
and
25 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,149 @@ | ||
/* | ||
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 configtx | ||
|
||
import ( | ||
"github.com/hyperledger/fabric/common/util" | ||
cb "github.com/hyperledger/fabric/protos/common" | ||
ab "github.com/hyperledger/fabric/protos/orderer" | ||
"github.com/hyperledger/fabric/protos/utils" | ||
|
||
"github.com/golang/protobuf/proto" | ||
) | ||
|
||
const ( | ||
CreationPolicyKey = "CreationPolicy" | ||
msgVersion = int32(0) | ||
epoch = 0 | ||
) | ||
|
||
// Template can be used to faciliate creation of configuration transactions | ||
type Template interface { | ||
// Items returns a set of SignedConfigurationItems for the given chainID | ||
Items(chainID string) ([]*cb.SignedConfigurationItem, error) | ||
} | ||
|
||
type simpleTemplate struct { | ||
items []*cb.ConfigurationItem | ||
} | ||
|
||
// NewSimpleTemplate creates a Template using the supplied items | ||
func NewSimpleTemplate(items ...*cb.ConfigurationItem) Template { | ||
return &simpleTemplate{items: items} | ||
} | ||
|
||
// Items returns a set of SignedConfigurationItems for the given chainID, and errors only on marshaling errors | ||
func (st *simpleTemplate) Items(chainID string) ([]*cb.SignedConfigurationItem, error) { | ||
signedItems := make([]*cb.SignedConfigurationItem, len(st.items)) | ||
for i := range st.items { | ||
st.items[i].Header = &cb.ChainHeader{ChainID: chainID} | ||
mItem, err := proto.Marshal(st.items[i]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
signedItems[i] = &cb.SignedConfigurationItem{ConfigurationItem: mItem} | ||
} | ||
|
||
return signedItems, nil | ||
} | ||
|
||
type compositeTemplate struct { | ||
templates []Template | ||
} | ||
|
||
// NewSimpleTemplate creates a Template using the source Templates | ||
func NewCompositeTemplate(templates ...Template) Template { | ||
return &compositeTemplate{templates: templates} | ||
} | ||
|
||
// Items returns a set of SignedConfigurationItems for the given chainID, and errors only on marshaling errors | ||
func (ct *compositeTemplate) Items(chainID string) ([]*cb.SignedConfigurationItem, error) { | ||
items := make([][]*cb.SignedConfigurationItem, len(ct.templates)) | ||
var err error | ||
for i := range ct.templates { | ||
items[i], err = ct.templates[i].Items(chainID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
return join(items...), nil | ||
} | ||
|
||
type newChainTemplate struct { | ||
creationPolicy string | ||
template Template | ||
} | ||
|
||
// NewChainCreationTemplate takes a CreationPolicy and a Template to produce a Template which outputs an appropriately | ||
// constructed list of SignedConfigurationItem including an appropriate digest. Note, using this Template in | ||
// a CompositeTemplate will invalidate the CreationPolicy | ||
func NewChainCreationTemplate(creationPolicy string, template Template) Template { | ||
return &newChainTemplate{ | ||
creationPolicy: creationPolicy, | ||
template: template, | ||
} | ||
} | ||
|
||
// Items returns a set of SignedConfigurationItems for the given chainID, and errors only on marshaling errors | ||
func (nct *newChainTemplate) Items(chainID string) ([]*cb.SignedConfigurationItem, error) { | ||
items, err := nct.template.Items(chainID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
creationPolicy := &cb.SignedConfigurationItem{ | ||
ConfigurationItem: utils.MarshalOrPanic(&cb.ConfigurationItem{ | ||
Header: utils.MakeChainHeader(cb.HeaderType_CONFIGURATION_ITEM, msgVersion, chainID, epoch), | ||
Key: CreationPolicyKey, | ||
Value: utils.MarshalOrPanic(&ab.CreationPolicy{ | ||
Policy: CreationPolicyKey, | ||
Digest: HashItems(items), | ||
}), | ||
}), | ||
} | ||
|
||
return join([]*cb.SignedConfigurationItem{creationPolicy}, items), nil | ||
} | ||
|
||
// HashItems is a utility method for computing the hash of the concatenation of the marshaled ConfigurationItems | ||
// in a []*cb.SignedConfigurationItem | ||
func HashItems(items []*cb.SignedConfigurationItem) []byte { | ||
sourceBytes := make([][]byte, len(items)) | ||
for i := range items { | ||
sourceBytes[i] = items[i].ConfigurationItem | ||
} | ||
return util.ComputeCryptoHash(util.ConcatenateBytes(sourceBytes...)) | ||
} | ||
|
||
// join takes a number of SignedConfigurationItem slices and produces a single item | ||
func join(sets ...[]*cb.SignedConfigurationItem) []*cb.SignedConfigurationItem { | ||
total := 0 | ||
for _, set := range sets { | ||
total += len(set) | ||
} | ||
result := make([]*cb.SignedConfigurationItem, total) | ||
last := 0 | ||
for _, set := range sets { | ||
for i := range set { | ||
result[i+last] = set[i] | ||
} | ||
last += len(set) | ||
} | ||
|
||
return result | ||
} |
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,107 @@ | ||
/* | ||
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 configtx | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
cb "github.com/hyperledger/fabric/protos/common" | ||
"github.com/hyperledger/fabric/protos/utils" | ||
) | ||
|
||
func verifyItemsResult(t *testing.T, template Template, count int) { | ||
newChainID := "foo" | ||
|
||
result, err := template.Items(newChainID) | ||
if err != nil { | ||
t.Fatalf("Should not have errored") | ||
} | ||
|
||
if len(result) != count { | ||
t.Errorf("Expected %d items, but got %d", count, len(result)) | ||
} | ||
|
||
for i, signedItem := range result { | ||
item := utils.UnmarshalConfigurationItemOrPanic(signedItem.ConfigurationItem) | ||
if item.Header.ChainID != newChainID { | ||
t.Errorf("Should have appropriately set new chainID") | ||
} | ||
if expected := fmt.Sprintf("%d", i); string(item.Value) != expected { | ||
t.Errorf("Expected %s but got %s", expected, item.Value) | ||
} | ||
} | ||
} | ||
|
||
func TestSimpleTemplate(t *testing.T) { | ||
simple := NewSimpleTemplate( | ||
&cb.ConfigurationItem{Value: []byte("0")}, | ||
&cb.ConfigurationItem{Value: []byte("1")}, | ||
) | ||
|
||
verifyItemsResult(t, simple, 2) | ||
} | ||
|
||
func TestCompositeTemplate(t *testing.T) { | ||
composite := NewCompositeTemplate( | ||
NewSimpleTemplate( | ||
&cb.ConfigurationItem{Value: []byte("0")}, | ||
&cb.ConfigurationItem{Value: []byte("1")}, | ||
), | ||
NewSimpleTemplate( | ||
&cb.ConfigurationItem{Value: []byte("2")}, | ||
), | ||
) | ||
|
||
verifyItemsResult(t, composite, 3) | ||
} | ||
|
||
func TestNewChainTemplate(t *testing.T) { | ||
simple := NewSimpleTemplate( | ||
&cb.ConfigurationItem{Value: []byte("1")}, | ||
&cb.ConfigurationItem{Value: []byte("2")}, | ||
) | ||
|
||
creationPolicy := "Test" | ||
nct := NewChainCreationTemplate(creationPolicy, simple) | ||
|
||
newChainID := "foo" | ||
items, err := nct.Items(newChainID) | ||
if err != nil { | ||
t.Fatalf("Error creation a chain creation configuration") | ||
} | ||
|
||
if expected := 3; len(items) != expected { | ||
t.Fatalf("Expected %d items, but got %d", expected, len(items)) | ||
} | ||
|
||
for i, signedItem := range items { | ||
item := utils.UnmarshalConfigurationItemOrPanic(signedItem.ConfigurationItem) | ||
if item.Header.ChainID != newChainID { | ||
t.Errorf("Should have appropriately set new chainID") | ||
} | ||
if i == 0 { | ||
if item.Key != CreationPolicyKey { | ||
t.Errorf("First item should have been the creation policy") | ||
} | ||
} else { | ||
if expected := fmt.Sprintf("%d", i); string(item.Value) != expected { | ||
t.Errorf("Expected %s but got %s", expected, item.Value) | ||
} | ||
} | ||
} | ||
} |
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
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