Skip to content

Commit

Permalink
Introduce orderer sharedconfig mechanism
Browse files Browse the repository at this point in the history
The configtx.Manager supports maintaining configuration updates based on
configuration transactions, but today, the only interpretation of these
configuration updates is the policies.Manager which handles
configuration updates of type Policy.

There are other types of configuration, such as Orderer type
configuration which are currently ignored.

This changeset adds an orderer shared config manager.  It is called
sharedconfig to make it explicit that this configuration is common to
and available to all orderers.  This is different from the notion of
localconfig which could include things like machine dependent paths and
buffer sizes.

This changeset also establishes a convention for encoding orderer
configuration by creating a proto message with name equal to the Key
field which is set in the orderer config.

This changeset hooks the shared config into the XXX points of the
multichain ChainSupport.  In the future, the batchSize and
consensusType will be removed from the localconfig once the other
consensus plugins have been migrated to the new shared multichain
infrastructure.

Change-Id: Ia68f5a83bf816317609b7df4c9ad0d1bed1bb800
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick committed Dec 2, 2016
1 parent 8e97791 commit e6d3b99
Show file tree
Hide file tree
Showing 9 changed files with 482 additions and 20 deletions.
58 changes: 47 additions & 11 deletions orderer/common/bootstrap/static/static.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,38 +20,74 @@ import (
"github.com/hyperledger/fabric/orderer/common/bootstrap"
"github.com/hyperledger/fabric/orderer/common/cauthdsl"
"github.com/hyperledger/fabric/orderer/common/configtx"
"github.com/hyperledger/fabric/orderer/common/sharedconfig"
"github.com/hyperledger/fabric/orderer/common/util"
cb "github.com/hyperledger/fabric/protos/common"
ab "github.com/hyperledger/fabric/protos/orderer"
)

var TestChainID = "**TEST_CHAINID**"

const msgVersion = int32(1)

type bootstrapper struct {
chainID string
chainID string
lastModified uint64
epoch uint64
consensusType string
batchSize int32
}

// New returns a new static bootstrap helper.
func New() bootstrap.Helper {
return &bootstrapper{chainID: TestChainID}
return &bootstrapper{
chainID: TestChainID,
consensusType: "solo",
batchSize: 10,
}
}

// GenesisBlock returns the genesis block to be used for bootstrapping
func (b *bootstrapper) GenesisBlock() (*cb.Block, error) {
func (b *bootstrapper) encodeConsensusType() *cb.SignedConfigurationItem {
configItemKey := sharedconfig.ConsensusTypeKey
configItemValue := util.MarshalOrPanic(&ab.ConsensusType{Type: b.consensusType})
modPolicy := configtx.DefaultModificationPolicyID

configItemChainHeader := util.MakeChainHeader(cb.HeaderType_CONFIGURATION_ITEM, msgVersion, b.chainID, b.epoch)
configItem := util.MakeConfigurationItem(configItemChainHeader, cb.ConfigurationItem_Orderer, b.lastModified, modPolicy, configItemKey, configItemValue)
return &cb.SignedConfigurationItem{ConfigurationItem: util.MarshalOrPanic(configItem), Signatures: nil}
}

func (b *bootstrapper) encodeBatchSize() *cb.SignedConfigurationItem {
configItemKey := sharedconfig.BatchSizeKey
configItemValue := util.MarshalOrPanic(&ab.BatchSize{Messages: b.batchSize})
modPolicy := configtx.DefaultModificationPolicyID

configItemChainHeader := util.MakeChainHeader(cb.HeaderType_CONFIGURATION_ITEM, msgVersion, b.chainID, b.epoch)
configItem := util.MakeConfigurationItem(configItemChainHeader, cb.ConfigurationItem_Orderer, b.lastModified, modPolicy, configItemKey, configItemValue)
return &cb.SignedConfigurationItem{ConfigurationItem: util.MarshalOrPanic(configItem), Signatures: nil}
}

func (b *bootstrapper) lockDefaultModificationPolicy() *cb.SignedConfigurationItem {
// Lock down the default modification policy to prevent any further policy modifications
configItemKey := configtx.DefaultModificationPolicyID
configItemValue := util.MarshalOrPanic(util.MakePolicyOrPanic(cauthdsl.RejectAllPolicy))
modPolicy := configtx.DefaultModificationPolicyID

lastModified := uint64(0)
epoch := uint64(0)
configItemChainHeader := util.MakeChainHeader(cb.HeaderType_CONFIGURATION_ITEM, msgVersion, b.chainID, epoch)
configItem := util.MakeConfigurationItem(configItemChainHeader, cb.ConfigurationItem_Policy, lastModified, modPolicy, configItemKey, configItemValue)
signedConfigItem := &cb.SignedConfigurationItem{ConfigurationItem: util.MarshalOrPanic(configItem), Signatures: nil}
configItemChainHeader := util.MakeChainHeader(cb.HeaderType_CONFIGURATION_ITEM, msgVersion, b.chainID, b.epoch)
configItem := util.MakeConfigurationItem(configItemChainHeader, cb.ConfigurationItem_Policy, b.lastModified, modPolicy, configItemKey, configItemValue)
return &cb.SignedConfigurationItem{ConfigurationItem: util.MarshalOrPanic(configItem), Signatures: nil}
}

// GenesisBlock returns the genesis block to be used for bootstrapping
func (b *bootstrapper) GenesisBlock() (*cb.Block, error) {
configItemChainHeader := util.MakeChainHeader(cb.HeaderType_CONFIGURATION_ITEM, msgVersion, b.chainID, b.epoch)

configEnvelope := util.MakeConfigurationEnvelope(signedConfigItem)
payloadChainHeader := util.MakeChainHeader(cb.HeaderType_CONFIGURATION_TRANSACTION, configItemChainHeader.Version, b.chainID, epoch)
configEnvelope := util.MakeConfigurationEnvelope(
b.encodeConsensusType(),
b.encodeBatchSize(),
b.lockDefaultModificationPolicy(),
)
payloadChainHeader := util.MakeChainHeader(cb.HeaderType_CONFIGURATION_TRANSACTION, configItemChainHeader.Version, b.chainID, b.epoch)
payloadSignatureHeader := util.MakeSignatureHeader(nil, util.CreateNonceOrPanic())
payloadHeader := util.MakePayloadHeader(payloadChainHeader, payloadSignatureHeader)
payload := &cb.Payload{Header: payloadHeader, Data: util.MarshalOrPanic(configEnvelope)}
Expand Down
4 changes: 2 additions & 2 deletions orderer/common/bootstrap/static/static_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func TestGenesisBlockData(t *testing.T) {
expectedPayloadChainHeaderType := int32(cb.HeaderType_CONFIGURATION_TRANSACTION)
expectedChainHeaderVersion := msgVersion
expectedChainHeaderEpoch := uint64(0)
expectedConfigEnvelopeItemsLength := 1
expectedConfigEnvelopeItemsLength := 3
expectedConfigurationItemChainHeaderType := int32(cb.HeaderType_CONFIGURATION_ITEM)
expectedConfigurationItemChainHeaderVersion := msgVersion
expectedConfigurationItemType := cb.ConfigurationItem_Policy
Expand Down Expand Up @@ -110,7 +110,7 @@ func TestGenesisBlockData(t *testing.T) {
t.Fatalf("Expected configuration envelope to have %d configuration item(s), got %d", expectedConfigEnvelopeItemsLength, len(configurationEnvelope.Items))
}

signedConfigurationItem := configurationEnvelope.Items[0]
signedConfigurationItem := configurationEnvelope.Items[2]
marshaledConfigurationItem := signedConfigurationItem.ConfigurationItem
configurationItem := &cb.ConfigurationItem{}
if err := proto.Unmarshal(marshaledConfigurationItem, configurationItem); err != nil {
Expand Down
141 changes: 141 additions & 0 deletions orderer/common/sharedconfig/sharedconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
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 sharedconfig

import (
"fmt"

cb "github.com/hyperledger/fabric/protos/common"
ab "github.com/hyperledger/fabric/protos/orderer"

"github.com/golang/protobuf/proto"
"github.com/op/go-logging"
)

// ConsensusTypeKey is the cb.ConfigurationItem type key name for the ConsensusType message
const ConsensusTypeKey = "ConsensusType"

// BatchSizeKey is the cb.ConfigurationItem type key name for the BatchSize message
const BatchSizeKey = "BatchSize"

var logger = logging.MustGetLogger("orderer/common/sharedconfig")

func init() {
logging.SetLevel(logging.DEBUG, "")
}

// Manager stores the common shared orderer configuration
// It is intended to be the primary accessor of ManagerImpl
// It is intended to discourage use of the other exported ManagerImpl methods
// which are used for updating the orderer configuration by the ConfigManager
type Manager interface {
// ConsensusType returns the configured consensus type
ConsensusType() string

// BatchSize returns the maximum number of messages to include in a block
BatchSize() int
}

type ordererConfig struct {
consensusType string
batchSize int
}

// ManagerImpl is an implementation of Manager and configtx.ConfigHandler
// In general, it should only be referenced as an Impl for the configtx.ConfigManager
type ManagerImpl struct {
pendingConfig *ordererConfig
config *ordererConfig
}

// NewManagerImpl creates a new ManagerImpl with the given CryptoHelper
func NewManagerImpl() *ManagerImpl {
return &ManagerImpl{
config: &ordererConfig{},
}
}

// ConsensusType returns the configured consensus type
func (pm *ManagerImpl) ConsensusType() string {
return pm.config.consensusType
}

// BatchSize returns the maximum number of messages to include in a block
func (pm *ManagerImpl) BatchSize() int {
return pm.config.batchSize
}

// BeginConfig is used to start a new configuration proposal
func (pm *ManagerImpl) BeginConfig() {
if pm.pendingConfig != nil {
logger.Fatalf("Programming error, cannot call begin in the middle of a proposal")
}
pm.pendingConfig = &ordererConfig{}
}

// RollbackConfig is used to abandon a new configuration proposal
func (pm *ManagerImpl) RollbackConfig() {
pm.pendingConfig = nil
}

// CommitConfig is used to commit a new configuration proposal
func (pm *ManagerImpl) CommitConfig() {
if pm.pendingConfig == nil {
logger.Fatalf("Programming error, cannot call commit without an existing proposal")
}
pm.config = pm.pendingConfig
pm.pendingConfig = nil
}

// ProposeConfig is used to add new configuration to the configuration proposal
func (pm *ManagerImpl) ProposeConfig(configItem *cb.ConfigurationItem) error {
if configItem.Type != cb.ConfigurationItem_Orderer {
return fmt.Errorf("Expected type of ConfigurationItem_Orderer, got %v", configItem.Type)
}

switch configItem.Key {
case ConsensusTypeKey:
consensusType := &ab.ConsensusType{}
err := proto.Unmarshal(configItem.Value, consensusType)
if err != nil {
return fmt.Errorf("Unmarshaling error for ConsensusType: %s", err)
}
if pm.config.consensusType == "" {
// The first configuration we accept the consensus type regardless
pm.config.consensusType = consensusType.Type
}
if consensusType.Type != pm.config.consensusType {
return fmt.Errorf("Attempted to change the consensus type from %s to %s after init", pm.config.consensusType, consensusType.Type)
}

pm.pendingConfig.consensusType = consensusType.Type
case BatchSizeKey:
batchSize := &ab.BatchSize{}
err := proto.Unmarshal(configItem.Value, batchSize)
if err != nil {
return fmt.Errorf("Unmarshaling error for BatchSize: %s", err)
}

if batchSize.Messages <= 0 {
return fmt.Errorf("Attempted to set the batch size to %d which is less than or equal to 0", batchSize.Messages)
}

pm.pendingConfig.batchSize = int(batchSize.Messages)
}

return nil
}

0 comments on commit e6d3b99

Please sign in to comment.