Skip to content

Commit

Permalink
[FAB-1613] Add configuration item templates
Browse files Browse the repository at this point in the history
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
Show file tree
Hide file tree
Showing 4 changed files with 275 additions and 25 deletions.
149 changes: 149 additions & 0 deletions common/configtx/template.go
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
}
107 changes: 107 additions & 0 deletions common/configtx/template_test.go
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)
}
}
}
}
16 changes: 5 additions & 11 deletions orderer/multichain/systemchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (

"github.com/hyperledger/fabric/common/configtx"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/orderer/common/filter"
"github.com/hyperledger/fabric/orderer/common/sharedconfig"
cb "github.com/hyperledger/fabric/protos/common"
Expand Down Expand Up @@ -150,6 +149,10 @@ func (sc *systemChain) proposeChain(configTx *cb.Envelope) cb.Status {
}

func (sc *systemChain) authorize(configEnvelope *cb.ConfigurationEnvelope) cb.Status {
if len(configEnvelope.Items) == 0 {
return cb.Status_BAD_REQUEST
}

creationConfigItem := &cb.ConfigurationItem{}
err := proto.Unmarshal(configEnvelope.Items[0].ConfigurationItem, creationConfigItem)
if err != nil {
Expand Down Expand Up @@ -191,16 +194,7 @@ func (sc *systemChain) authorize(configEnvelope *cb.ConfigurationEnvelope) cb.St
// XXX actually do policy signature validation
_ = policy

var remainingBytes []byte
for i, item := range configEnvelope.Items {
if i == 0 {
// Do not include the creation policy
continue
}
remainingBytes = util.ConcatenateBytes(remainingBytes, item.ConfigurationItem)
}

configHash := util.ComputeCryptoHash(remainingBytes)
configHash := configtx.HashItems(configEnvelope.Items[1:])

if !bytes.Equal(configHash, creationPolicy.Digest) {
logger.Debugf("Validly signed chain creation did not contain correct digest for remaining configuration %x vs. %x", configHash, creationPolicy.Digest)
Expand Down
28 changes: 14 additions & 14 deletions protos/utils/blockutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"github.com/golang/protobuf/proto"

"github.com/hyperledger/fabric/common/cauthdsl"
"github.com/hyperledger/fabric/common/configtx"
"github.com/hyperledger/fabric/msp"
cb "github.com/hyperledger/fabric/protos/common"
ab "github.com/hyperledger/fabric/protos/orderer"
Expand Down Expand Up @@ -132,14 +131,15 @@ func MakeConfigurationBlock(testChainID string) (*cb.Block, error) {
}

const (
batchSize = 10
consensusType = "solo"
epoch = uint64(0)
messageVersion = int32(1)
lastModified = uint64(0)
consensusTypeKey = "ConsensusType"
batchSizeKey = "BatchSize"
mspKey = "MSP"
batchSize = 10
consensusType = "solo"
epoch = uint64(0)
messageVersion = int32(1)
lastModified = uint64(0)
consensusTypeKey = "ConsensusType"
batchSizeKey = "BatchSize"
mspKey = "MSP"
XXX_DefaultModificationPolicyID = "DefaultModificationPolicy" // Break an import cycle during work to remove the below configtx construction methods
)

func createSignedConfigItem(chainID string,
Expand All @@ -162,21 +162,21 @@ func encodeConsensusType(testChainID string) *cb.SignedConfigurationItem {
return createSignedConfigItem(testChainID,
consensusTypeKey,
MarshalOrPanic(&ab.ConsensusType{Type: consensusType}),
configtx.DefaultModificationPolicyID)
XXX_DefaultModificationPolicyID)
}

func encodeBatchSize(testChainID string) *cb.SignedConfigurationItem {
return createSignedConfigItem(testChainID,
batchSizeKey,
MarshalOrPanic(&ab.BatchSize{MaxMessageCount: batchSize}),
configtx.DefaultModificationPolicyID)
XXX_DefaultModificationPolicyID)
}

func lockDefaultModificationPolicy(testChainID string) *cb.SignedConfigurationItem {
return createSignedConfigItem(testChainID,
configtx.DefaultModificationPolicyID,
XXX_DefaultModificationPolicyID,
MarshalOrPanic(MakePolicyOrPanic(cauthdsl.RejectAllPolicy)),
configtx.DefaultModificationPolicyID)
XXX_DefaultModificationPolicyID)
}

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

0 comments on commit 3e8d0ae

Please sign in to comment.