685 lines
26 KiB
Go
685 lines
26 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package encoder
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
cb "github.com/hyperledger/fabric-protos-go/common"
|
|
pb "github.com/hyperledger/fabric-protos-go/peer"
|
|
"github.com/hyperledger/fabric/common/channelconfig"
|
|
"github.com/hyperledger/fabric/common/flogging"
|
|
"github.com/hyperledger/fabric/common/genesis"
|
|
"github.com/hyperledger/fabric/common/policies"
|
|
"github.com/hyperledger/fabric/common/policydsl"
|
|
"github.com/hyperledger/fabric/common/util"
|
|
"github.com/hyperledger/fabric/internal/configtxgen/genesisconfig"
|
|
"github.com/hyperledger/fabric/internal/configtxlator/update"
|
|
"github.com/hyperledger/fabric/internal/pkg/identity"
|
|
"github.com/hyperledger/fabric/msp"
|
|
"github.com/hyperledger/fabric/protoutil"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
const (
|
|
ordererAdminsPolicyName = "/Channel/Orderer/Admins"
|
|
|
|
msgVersion = int32(0)
|
|
epoch = 0
|
|
)
|
|
|
|
var logger = flogging.MustGetLogger("common.tools.configtxgen.encoder")
|
|
|
|
const (
|
|
// ConsensusTypeSolo identifies the solo consensus implementation.
|
|
ConsensusTypeSolo = "solo"
|
|
// ConsensusTypeEtcdRaft identifies the Raft-based consensus implementation.
|
|
ConsensusTypeEtcdRaft = "etcdraft"
|
|
// ConsensusTypeBFT identifies the BFT-based consensus implementation.
|
|
ConsensusTypeBFT = "BFT"
|
|
|
|
// BlockValidationPolicyKey TODO
|
|
BlockValidationPolicyKey = "BlockValidation"
|
|
|
|
// OrdererAdminsPolicy is the absolute path to the orderer admins policy
|
|
OrdererAdminsPolicy = "/Channel/Orderer/Admins"
|
|
|
|
// SignaturePolicyType is the 'Type' string for signature policies
|
|
SignaturePolicyType = "Signature"
|
|
|
|
// ImplicitMetaPolicyType is the 'Type' string for implicit meta policies
|
|
ImplicitMetaPolicyType = "ImplicitMeta"
|
|
)
|
|
|
|
func addValue(cg *cb.ConfigGroup, value channelconfig.ConfigValue, modPolicy string) {
|
|
cg.Values[value.Key()] = &cb.ConfigValue{
|
|
Value: protoutil.MarshalOrPanic(value.Value()),
|
|
ModPolicy: modPolicy,
|
|
}
|
|
}
|
|
|
|
func addPolicy(cg *cb.ConfigGroup, policy policies.ConfigPolicy, modPolicy string) {
|
|
cg.Policies[policy.Key()] = &cb.ConfigPolicy{
|
|
Policy: policy.Value(),
|
|
ModPolicy: modPolicy,
|
|
}
|
|
}
|
|
|
|
func AddOrdererPolicies(cg *cb.ConfigGroup, policyMap map[string]*genesisconfig.Policy, modPolicy string) error {
|
|
switch {
|
|
case policyMap == nil:
|
|
return errors.Errorf("no policies defined")
|
|
case policyMap[BlockValidationPolicyKey] == nil:
|
|
return errors.Errorf("no BlockValidation policy defined")
|
|
}
|
|
|
|
return AddPolicies(cg, policyMap, modPolicy)
|
|
}
|
|
|
|
func AddPolicies(cg *cb.ConfigGroup, policyMap map[string]*genesisconfig.Policy, modPolicy string) error {
|
|
switch {
|
|
case policyMap == nil:
|
|
return errors.Errorf("no policies defined")
|
|
case policyMap[channelconfig.AdminsPolicyKey] == nil:
|
|
return errors.Errorf("no Admins policy defined")
|
|
case policyMap[channelconfig.ReadersPolicyKey] == nil:
|
|
return errors.Errorf("no Readers policy defined")
|
|
case policyMap[channelconfig.WritersPolicyKey] == nil:
|
|
return errors.Errorf("no Writers policy defined")
|
|
}
|
|
|
|
for policyName, policy := range policyMap {
|
|
switch policy.Type {
|
|
case ImplicitMetaPolicyType:
|
|
imp, err := policies.ImplicitMetaFromString(policy.Rule)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "invalid implicit meta policy rule '%s'", policy.Rule)
|
|
}
|
|
cg.Policies[policyName] = &cb.ConfigPolicy{
|
|
ModPolicy: modPolicy,
|
|
Policy: &cb.Policy{
|
|
Type: int32(cb.Policy_IMPLICIT_META),
|
|
Value: protoutil.MarshalOrPanic(imp),
|
|
},
|
|
}
|
|
case SignaturePolicyType:
|
|
sp, err := policydsl.FromString(policy.Rule)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "invalid signature policy rule '%s'", policy.Rule)
|
|
}
|
|
cg.Policies[policyName] = &cb.ConfigPolicy{
|
|
ModPolicy: modPolicy,
|
|
Policy: &cb.Policy{
|
|
Type: int32(cb.Policy_SIGNATURE),
|
|
Value: protoutil.MarshalOrPanic(sp),
|
|
},
|
|
}
|
|
default:
|
|
return errors.Errorf("unknown policy type: %s", policy.Type)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// NewChannelGroup defines the root of the channel configuration. It defines basic operating principles like the hashing
|
|
// algorithm used for the blocks, as well as the location of the ordering service. It will recursively call into the
|
|
// NewOrdererGroup, NewConsortiumsGroup, and NewApplicationGroup depending on whether these sub-elements are set in the
|
|
// configuration. All mod_policy values are set to "Admins" for this group, with the exception of the OrdererAddresses
|
|
// value which is set to "/Channel/Orderer/Admins".
|
|
func NewChannelGroup(conf *genesisconfig.Profile) (*cb.ConfigGroup, error) {
|
|
channelGroup := protoutil.NewConfigGroup()
|
|
if err := AddPolicies(channelGroup, conf.Policies, channelconfig.AdminsPolicyKey); err != nil {
|
|
return nil, errors.Wrapf(err, "error adding policies to channel group")
|
|
}
|
|
|
|
addValue(channelGroup, channelconfig.HashingAlgorithmValue(), channelconfig.AdminsPolicyKey)
|
|
addValue(channelGroup, channelconfig.BlockDataHashingStructureValue(), channelconfig.AdminsPolicyKey)
|
|
if conf.Orderer != nil && len(conf.Orderer.Addresses) > 0 {
|
|
addValue(channelGroup, channelconfig.OrdererAddressesValue(conf.Orderer.Addresses), ordererAdminsPolicyName)
|
|
}
|
|
|
|
if conf.Consortium != "" {
|
|
addValue(channelGroup, channelconfig.ConsortiumValue(conf.Consortium), channelconfig.AdminsPolicyKey)
|
|
}
|
|
|
|
if len(conf.Capabilities) > 0 {
|
|
addValue(channelGroup, channelconfig.CapabilitiesValue(conf.Capabilities), channelconfig.AdminsPolicyKey)
|
|
}
|
|
|
|
var err error
|
|
if conf.Orderer != nil {
|
|
channelGroup.Groups[channelconfig.OrdererGroupKey], err = NewOrdererGroup(conf.Orderer, conf.Capabilities)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not create orderer group")
|
|
}
|
|
}
|
|
|
|
if conf.Application != nil {
|
|
channelGroup.Groups[channelconfig.ApplicationGroupKey], err = NewApplicationGroup(conf.Application)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not create application group")
|
|
}
|
|
}
|
|
|
|
if conf.Consortiums != nil {
|
|
channelGroup.Groups[channelconfig.ConsortiumsGroupKey], err = NewConsortiumsGroup(conf.Consortiums)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "could not create consortiums group")
|
|
}
|
|
}
|
|
|
|
channelGroup.ModPolicy = channelconfig.AdminsPolicyKey
|
|
return channelGroup, nil
|
|
}
|
|
|
|
// NewOrdererGroup returns the orderer component of the channel configuration. It defines parameters of the ordering service
|
|
// about how large blocks should be, how frequently they should be emitted, etc. as well as the organizations of the ordering network.
|
|
// It sets the mod_policy of all elements to "Admins". This group is always present in any channel configuration.
|
|
func NewOrdererGroup(conf *genesisconfig.Orderer, channelCapabilities map[string]bool) (*cb.ConfigGroup, error) {
|
|
if conf.OrdererType == "BFT" && !channelCapabilities["V3_0"] {
|
|
return nil, errors.New("orderer type BFT must be used with V3_0 capability")
|
|
}
|
|
ordererGroup := protoutil.NewConfigGroup()
|
|
if err := AddOrdererPolicies(ordererGroup, conf.Policies, channelconfig.AdminsPolicyKey); err != nil {
|
|
return nil, errors.Wrapf(err, "error adding policies to orderer group")
|
|
}
|
|
addValue(ordererGroup, channelconfig.BatchSizeValue(
|
|
conf.BatchSize.MaxMessageCount,
|
|
conf.BatchSize.AbsoluteMaxBytes,
|
|
conf.BatchSize.PreferredMaxBytes,
|
|
), channelconfig.AdminsPolicyKey)
|
|
addValue(ordererGroup, channelconfig.BatchTimeoutValue(conf.BatchTimeout.String()), channelconfig.AdminsPolicyKey)
|
|
addValue(ordererGroup, channelconfig.ChannelRestrictionsValue(conf.MaxChannels), channelconfig.AdminsPolicyKey)
|
|
|
|
if len(conf.Capabilities) > 0 {
|
|
addValue(ordererGroup, channelconfig.CapabilitiesValue(conf.Capabilities), channelconfig.AdminsPolicyKey)
|
|
}
|
|
|
|
var consensusMetadata []byte
|
|
var err error
|
|
|
|
switch conf.OrdererType {
|
|
case ConsensusTypeSolo:
|
|
case ConsensusTypeEtcdRaft:
|
|
if consensusMetadata, err = channelconfig.MarshalEtcdRaftMetadata(conf.EtcdRaft); err != nil {
|
|
return nil, errors.Errorf("cannot marshal metadata for orderer type %s: %s", ConsensusTypeEtcdRaft, err)
|
|
}
|
|
case ConsensusTypeBFT:
|
|
consenterProtos, err := consenterProtosFromConfig(conf.ConsenterMapping)
|
|
if err != nil {
|
|
return nil, errors.Errorf("cannot load consenter config for orderer type %s: %s", ConsensusTypeBFT, err)
|
|
}
|
|
addValue(ordererGroup, channelconfig.OrderersValue(consenterProtos), channelconfig.AdminsPolicyKey)
|
|
if consensusMetadata, err = channelconfig.MarshalBFTOptions(conf.SmartBFT); err != nil {
|
|
return nil, errors.Errorf("consenter options read failed with error %s for orderer type %s", err, ConsensusTypeBFT)
|
|
}
|
|
// Overwrite policy manually by computing it from the consenters
|
|
policies.EncodeBFTBlockVerificationPolicy(consenterProtos, ordererGroup)
|
|
default:
|
|
return nil, errors.Errorf("unknown orderer type: %s", conf.OrdererType)
|
|
}
|
|
|
|
addValue(ordererGroup, channelconfig.ConsensusTypeValue(conf.OrdererType, consensusMetadata), channelconfig.AdminsPolicyKey)
|
|
|
|
for _, org := range conf.Organizations {
|
|
var err error
|
|
ordererGroup.Groups[org.Name], err = NewOrdererOrgGroup(org)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to create orderer org")
|
|
}
|
|
}
|
|
|
|
ordererGroup.ModPolicy = channelconfig.AdminsPolicyKey
|
|
return ordererGroup, nil
|
|
}
|
|
|
|
func consenterProtosFromConfig(consenterMapping []*genesisconfig.Consenter) ([]*cb.Consenter, error) {
|
|
var consenterProtos []*cb.Consenter
|
|
for _, consenter := range consenterMapping {
|
|
c := &cb.Consenter{
|
|
Id: consenter.ID,
|
|
Host: consenter.Host,
|
|
Port: consenter.Port,
|
|
MspId: consenter.MSPID,
|
|
}
|
|
// Expect the user to set the config value for client/server certs or identity to the
|
|
// path where they are persisted locally, then load these files to memory.
|
|
if consenter.ClientTLSCert != "" {
|
|
clientCert, err := ioutil.ReadFile(consenter.ClientTLSCert)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot load client cert for consenter %s:%d: %s", c.GetHost(), c.GetPort(), err)
|
|
}
|
|
c.ClientTlsCert = clientCert
|
|
}
|
|
|
|
if consenter.ServerTLSCert != "" {
|
|
serverCert, err := ioutil.ReadFile(consenter.ServerTLSCert)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot load server cert for consenter %s:%d: %s", c.GetHost(), c.GetPort(), err)
|
|
}
|
|
c.ServerTlsCert = serverCert
|
|
}
|
|
|
|
if consenter.Identity != "" {
|
|
identity, err := ioutil.ReadFile(consenter.Identity)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot load identity for consenter %s:%d: %s", c.GetHost(), c.GetPort(), err)
|
|
}
|
|
c.Identity = identity
|
|
}
|
|
|
|
consenterProtos = append(consenterProtos, c)
|
|
}
|
|
return consenterProtos, nil
|
|
}
|
|
|
|
// NewConsortiumsGroup returns an org component of the channel configuration. It defines the crypto material for the
|
|
// organization (its MSP). It sets the mod_policy of all elements to "Admins".
|
|
func NewConsortiumOrgGroup(conf *genesisconfig.Organization) (*cb.ConfigGroup, error) {
|
|
consortiumsOrgGroup := protoutil.NewConfigGroup()
|
|
consortiumsOrgGroup.ModPolicy = channelconfig.AdminsPolicyKey
|
|
|
|
if conf.SkipAsForeign {
|
|
return consortiumsOrgGroup, nil
|
|
}
|
|
|
|
mspConfig, err := msp.GetVerifyingMspConfig(conf.MSPDir, conf.ID, conf.MSPType)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "1 - Error loading MSP configuration for org: %s", conf.Name)
|
|
}
|
|
|
|
if err := AddPolicies(consortiumsOrgGroup, conf.Policies, channelconfig.AdminsPolicyKey); err != nil {
|
|
return nil, errors.Wrapf(err, "error adding policies to consortiums org group '%s'", conf.Name)
|
|
}
|
|
|
|
addValue(consortiumsOrgGroup, channelconfig.MSPValue(mspConfig), channelconfig.AdminsPolicyKey)
|
|
|
|
return consortiumsOrgGroup, nil
|
|
}
|
|
|
|
// NewOrdererOrgGroup returns an orderer org component of the channel configuration. It defines the crypto material for the
|
|
// organization (its MSP). It sets the mod_policy of all elements to "Admins".
|
|
func NewOrdererOrgGroup(conf *genesisconfig.Organization) (*cb.ConfigGroup, error) {
|
|
ordererOrgGroup := protoutil.NewConfigGroup()
|
|
ordererOrgGroup.ModPolicy = channelconfig.AdminsPolicyKey
|
|
|
|
if conf.SkipAsForeign {
|
|
return ordererOrgGroup, nil
|
|
}
|
|
|
|
mspConfig, err := msp.GetVerifyingMspConfig(conf.MSPDir, conf.ID, conf.MSPType)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "1 - Error loading MSP configuration for org: %s", conf.Name)
|
|
}
|
|
|
|
if err := AddPolicies(ordererOrgGroup, conf.Policies, channelconfig.AdminsPolicyKey); err != nil {
|
|
return nil, errors.Wrapf(err, "error adding policies to orderer org group '%s'", conf.Name)
|
|
}
|
|
|
|
addValue(ordererOrgGroup, channelconfig.MSPValue(mspConfig), channelconfig.AdminsPolicyKey)
|
|
|
|
if len(conf.OrdererEndpoints) > 0 {
|
|
addValue(ordererOrgGroup, channelconfig.EndpointsValue(conf.OrdererEndpoints), channelconfig.AdminsPolicyKey)
|
|
}
|
|
|
|
return ordererOrgGroup, nil
|
|
}
|
|
|
|
// NewApplicationGroup returns the application component of the channel configuration. It defines the organizations which are involved
|
|
// in application logic like chaincodes, and how these members may interact with the orderer. It sets the mod_policy of all elements to "Admins".
|
|
func NewApplicationGroup(conf *genesisconfig.Application) (*cb.ConfigGroup, error) {
|
|
applicationGroup := protoutil.NewConfigGroup()
|
|
if err := AddPolicies(applicationGroup, conf.Policies, channelconfig.AdminsPolicyKey); err != nil {
|
|
return nil, errors.Wrapf(err, "error adding policies to application group")
|
|
}
|
|
|
|
if len(conf.ACLs) > 0 {
|
|
addValue(applicationGroup, channelconfig.ACLValues(conf.ACLs), channelconfig.AdminsPolicyKey)
|
|
}
|
|
|
|
if len(conf.Capabilities) > 0 {
|
|
addValue(applicationGroup, channelconfig.CapabilitiesValue(conf.Capabilities), channelconfig.AdminsPolicyKey)
|
|
}
|
|
|
|
for _, org := range conf.Organizations {
|
|
var err error
|
|
applicationGroup.Groups[org.Name], err = NewApplicationOrgGroup(org)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to create application org")
|
|
}
|
|
}
|
|
|
|
applicationGroup.ModPolicy = channelconfig.AdminsPolicyKey
|
|
return applicationGroup, nil
|
|
}
|
|
|
|
// NewApplicationOrgGroup returns an application org component of the channel configuration. It defines the crypto material for the organization
|
|
// (its MSP) as well as its anchor peers for use by the gossip network. It sets the mod_policy of all elements to "Admins".
|
|
func NewApplicationOrgGroup(conf *genesisconfig.Organization) (*cb.ConfigGroup, error) {
|
|
applicationOrgGroup := protoutil.NewConfigGroup()
|
|
applicationOrgGroup.ModPolicy = channelconfig.AdminsPolicyKey
|
|
|
|
if conf.SkipAsForeign {
|
|
return applicationOrgGroup, nil
|
|
}
|
|
|
|
mspConfig, err := msp.GetVerifyingMspConfig(conf.MSPDir, conf.ID, conf.MSPType)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "1 - Error loading MSP configuration for org %s", conf.Name)
|
|
}
|
|
|
|
if err := AddPolicies(applicationOrgGroup, conf.Policies, channelconfig.AdminsPolicyKey); err != nil {
|
|
return nil, errors.Wrapf(err, "error adding policies to application org group %s", conf.Name)
|
|
}
|
|
addValue(applicationOrgGroup, channelconfig.MSPValue(mspConfig), channelconfig.AdminsPolicyKey)
|
|
|
|
var anchorProtos []*pb.AnchorPeer
|
|
for _, anchorPeer := range conf.AnchorPeers {
|
|
anchorProtos = append(anchorProtos, &pb.AnchorPeer{
|
|
Host: anchorPeer.Host,
|
|
Port: int32(anchorPeer.Port),
|
|
})
|
|
}
|
|
|
|
// Avoid adding an unnecessary anchor peers element when one is not required. This helps
|
|
// prevent a delta from the orderer system channel when computing more complex channel
|
|
// creation transactions
|
|
if len(anchorProtos) > 0 {
|
|
addValue(applicationOrgGroup, channelconfig.AnchorPeersValue(anchorProtos), channelconfig.AdminsPolicyKey)
|
|
}
|
|
|
|
return applicationOrgGroup, nil
|
|
}
|
|
|
|
// NewConsortiumsGroup returns the consortiums component of the channel configuration. This element is only defined for the ordering system channel.
|
|
// It sets the mod_policy for all elements to "/Channel/Orderer/Admins".
|
|
func NewConsortiumsGroup(conf map[string]*genesisconfig.Consortium) (*cb.ConfigGroup, error) {
|
|
consortiumsGroup := protoutil.NewConfigGroup()
|
|
// This policy is not referenced anywhere, it is only used as part of the implicit meta policy rule at the channel level, so this setting
|
|
// effectively degrades control of the ordering system channel to the ordering admins
|
|
addPolicy(consortiumsGroup, policies.SignaturePolicy(channelconfig.AdminsPolicyKey, policydsl.AcceptAllPolicy), ordererAdminsPolicyName)
|
|
|
|
for consortiumName, consortium := range conf {
|
|
var err error
|
|
consortiumsGroup.Groups[consortiumName], err = NewConsortiumGroup(consortium)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to create consortium %s", consortiumName)
|
|
}
|
|
}
|
|
|
|
consortiumsGroup.ModPolicy = ordererAdminsPolicyName
|
|
return consortiumsGroup, nil
|
|
}
|
|
|
|
// NewConsortiums returns a consortiums component of the channel configuration. Each consortium defines the organizations which may be involved in channel
|
|
// creation, as well as the channel creation policy the orderer checks at channel creation time to authorize the action. It sets the mod_policy of all
|
|
// elements to "/Channel/Orderer/Admins".
|
|
func NewConsortiumGroup(conf *genesisconfig.Consortium) (*cb.ConfigGroup, error) {
|
|
consortiumGroup := protoutil.NewConfigGroup()
|
|
|
|
for _, org := range conf.Organizations {
|
|
var err error
|
|
consortiumGroup.Groups[org.Name], err = NewConsortiumOrgGroup(org)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to create consortium org")
|
|
}
|
|
}
|
|
|
|
addValue(consortiumGroup, channelconfig.ChannelCreationPolicyValue(policies.ImplicitMetaAnyPolicy(channelconfig.AdminsPolicyKey).Value()), ordererAdminsPolicyName)
|
|
|
|
consortiumGroup.ModPolicy = ordererAdminsPolicyName
|
|
return consortiumGroup, nil
|
|
}
|
|
|
|
// NewChannelCreateConfigUpdate generates a ConfigUpdate which can be sent to the orderer to create a new channel. Optionally, the channel group of the
|
|
// ordering system channel may be passed in, and the resulting ConfigUpdate will extract the appropriate versions from this file.
|
|
func NewChannelCreateConfigUpdate(channelID string, conf *genesisconfig.Profile, templateConfig *cb.ConfigGroup) (*cb.ConfigUpdate, error) {
|
|
if conf.Application == nil {
|
|
return nil, errors.New("cannot define a new channel with no Application section")
|
|
}
|
|
|
|
if conf.Consortium == "" {
|
|
return nil, errors.New("cannot define a new channel with no Consortium value")
|
|
}
|
|
|
|
newChannelGroup, err := NewChannelGroup(conf)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "could not turn parse profile into channel group")
|
|
}
|
|
|
|
updt, err := update.Compute(&cb.Config{ChannelGroup: templateConfig}, &cb.Config{ChannelGroup: newChannelGroup})
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "could not compute update")
|
|
}
|
|
|
|
// Add the consortium name to create the channel for into the write set as required.
|
|
updt.ChannelId = channelID
|
|
updt.ReadSet.Values[channelconfig.ConsortiumKey] = &cb.ConfigValue{Version: 0}
|
|
updt.WriteSet.Values[channelconfig.ConsortiumKey] = &cb.ConfigValue{
|
|
Version: 0,
|
|
Value: protoutil.MarshalOrPanic(&cb.Consortium{
|
|
Name: conf.Consortium,
|
|
}),
|
|
}
|
|
|
|
return updt, nil
|
|
}
|
|
|
|
// DefaultConfigTemplate generates a config template based on the assumption that
|
|
// the input profile is a channel creation template and no system channel context
|
|
// is available.
|
|
func DefaultConfigTemplate(conf *genesisconfig.Profile) (*cb.ConfigGroup, error) {
|
|
channelGroup, err := NewChannelGroup(conf)
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "error parsing configuration")
|
|
}
|
|
|
|
if _, ok := channelGroup.Groups[channelconfig.ApplicationGroupKey]; !ok {
|
|
return nil, errors.New("channel template configs must contain an application section")
|
|
}
|
|
|
|
channelGroup.Groups[channelconfig.ApplicationGroupKey].Values = nil
|
|
channelGroup.Groups[channelconfig.ApplicationGroupKey].Policies = nil
|
|
|
|
return channelGroup, nil
|
|
}
|
|
|
|
func ConfigTemplateFromGroup(conf *genesisconfig.Profile, cg *cb.ConfigGroup) (*cb.ConfigGroup, error) {
|
|
template := proto.Clone(cg).(*cb.ConfigGroup)
|
|
if template.Groups == nil {
|
|
return nil, errors.Errorf("supplied system channel group has no sub-groups")
|
|
}
|
|
|
|
template.Groups[channelconfig.ApplicationGroupKey] = &cb.ConfigGroup{
|
|
Groups: map[string]*cb.ConfigGroup{},
|
|
Policies: map[string]*cb.ConfigPolicy{
|
|
channelconfig.AdminsPolicyKey: {},
|
|
},
|
|
}
|
|
|
|
consortiums, ok := template.Groups[channelconfig.ConsortiumsGroupKey]
|
|
if !ok {
|
|
return nil, errors.Errorf("supplied system channel group does not appear to be system channel (missing consortiums group)")
|
|
}
|
|
|
|
if consortiums.Groups == nil {
|
|
return nil, errors.Errorf("system channel consortiums group appears to have no consortiums defined")
|
|
}
|
|
|
|
consortium, ok := consortiums.Groups[conf.Consortium]
|
|
if !ok {
|
|
return nil, errors.Errorf("supplied system channel group is missing '%s' consortium", conf.Consortium)
|
|
}
|
|
|
|
if conf.Application == nil {
|
|
return nil, errors.Errorf("supplied channel creation profile does not contain an application section")
|
|
}
|
|
|
|
for _, organization := range conf.Application.Organizations {
|
|
var ok bool
|
|
template.Groups[channelconfig.ApplicationGroupKey].Groups[organization.Name], ok = consortium.Groups[organization.Name]
|
|
if !ok {
|
|
return nil, errors.Errorf("consortium %s does not contain member org %s", conf.Consortium, organization.Name)
|
|
}
|
|
}
|
|
delete(template.Groups, channelconfig.ConsortiumsGroupKey)
|
|
|
|
addValue(template, channelconfig.ConsortiumValue(conf.Consortium), channelconfig.AdminsPolicyKey)
|
|
|
|
return template, nil
|
|
}
|
|
|
|
// MakeChannelCreationTransaction is a handy utility function for creating transactions for channel creation.
|
|
// It assumes the invoker has no system channel context so ignores all but the application section.
|
|
// Deprecated
|
|
func MakeChannelCreationTransaction(
|
|
channelID string,
|
|
signer identity.SignerSerializer,
|
|
conf *genesisconfig.Profile,
|
|
) (*cb.Envelope, error) {
|
|
template, err := DefaultConfigTemplate(conf)
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "could not generate default config template")
|
|
}
|
|
return MakeChannelCreationTransactionFromTemplate(channelID, signer, conf, template)
|
|
}
|
|
|
|
// MakeChannelCreationTransactionWithSystemChannelContext is a utility function for creating channel creation txes.
|
|
// It requires a configuration representing the orderer system channel to allow more sophisticated channel creation
|
|
// transactions modifying pieces of the configuration like the orderer set.
|
|
// Deprecated
|
|
func MakeChannelCreationTransactionWithSystemChannelContext(
|
|
channelID string,
|
|
signer identity.SignerSerializer,
|
|
conf,
|
|
systemChannelConf *genesisconfig.Profile,
|
|
) (*cb.Envelope, error) {
|
|
cg, err := NewChannelGroup(systemChannelConf)
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "could not parse system channel config")
|
|
}
|
|
|
|
template, err := ConfigTemplateFromGroup(conf, cg)
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "could not create config template")
|
|
}
|
|
|
|
return MakeChannelCreationTransactionFromTemplate(channelID, signer, conf, template)
|
|
}
|
|
|
|
// MakeChannelCreationTransactionFromTemplate creates a transaction for creating a channel. It uses
|
|
// the given template to produce the config update set. Usually, the caller will want to invoke
|
|
// MakeChannelCreationTransaction or MakeChannelCreationTransactionWithSystemChannelContext.
|
|
func MakeChannelCreationTransactionFromTemplate(
|
|
channelID string,
|
|
signer identity.SignerSerializer,
|
|
conf *genesisconfig.Profile,
|
|
template *cb.ConfigGroup,
|
|
) (*cb.Envelope, error) {
|
|
newChannelConfigUpdate, err := NewChannelCreateConfigUpdate(channelID, conf, template)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "config update generation failure")
|
|
}
|
|
|
|
newConfigUpdateEnv := &cb.ConfigUpdateEnvelope{
|
|
ConfigUpdate: protoutil.MarshalOrPanic(newChannelConfigUpdate),
|
|
}
|
|
|
|
if signer != nil {
|
|
sigHeader, err := protoutil.NewSignatureHeader(signer)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "creating signature header failed")
|
|
}
|
|
|
|
newConfigUpdateEnv.Signatures = []*cb.ConfigSignature{{
|
|
SignatureHeader: protoutil.MarshalOrPanic(sigHeader),
|
|
}}
|
|
|
|
newConfigUpdateEnv.Signatures[0].Signature, err = signer.Sign(util.ConcatenateBytes(newConfigUpdateEnv.Signatures[0].SignatureHeader, newConfigUpdateEnv.ConfigUpdate))
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "signature failure over config update")
|
|
}
|
|
|
|
}
|
|
|
|
return protoutil.CreateSignedEnvelope(cb.HeaderType_CONFIG_UPDATE, channelID, signer, newConfigUpdateEnv, msgVersion, epoch)
|
|
}
|
|
|
|
// HasSkippedForeignOrgs is used to detect whether a configuration includes
|
|
// org definitions which should not be parsed because this tool is being
|
|
// run in a context where the user does not have access to that org's info
|
|
func HasSkippedForeignOrgs(conf *genesisconfig.Profile) error {
|
|
var organizations []*genesisconfig.Organization
|
|
|
|
if conf.Orderer != nil {
|
|
organizations = append(organizations, conf.Orderer.Organizations...)
|
|
}
|
|
|
|
if conf.Application != nil {
|
|
organizations = append(organizations, conf.Application.Organizations...)
|
|
}
|
|
|
|
for _, consortium := range conf.Consortiums {
|
|
organizations = append(organizations, consortium.Organizations...)
|
|
}
|
|
|
|
for _, org := range organizations {
|
|
if org.SkipAsForeign {
|
|
return errors.Errorf("organization '%s' is marked to be skipped as foreign", org.Name)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Bootstrapper is a wrapper around NewChannelConfigGroup which can produce genesis blocks
|
|
type Bootstrapper struct {
|
|
channelGroup *cb.ConfigGroup
|
|
}
|
|
|
|
// NewBootstrapper creates a bootstrapper but returns an error instead of panic-ing
|
|
func NewBootstrapper(config *genesisconfig.Profile) (*Bootstrapper, error) {
|
|
if err := HasSkippedForeignOrgs(config); err != nil {
|
|
return nil, errors.WithMessage(err, "all org definitions must be local during bootstrapping")
|
|
}
|
|
|
|
channelGroup, err := NewChannelGroup(config)
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "could not create channel group")
|
|
}
|
|
|
|
return &Bootstrapper{
|
|
channelGroup: channelGroup,
|
|
}, nil
|
|
}
|
|
|
|
// New creates a new Bootstrapper for generating genesis blocks
|
|
func New(config *genesisconfig.Profile) *Bootstrapper {
|
|
bs, err := NewBootstrapper(config)
|
|
if err != nil {
|
|
logger.Panicf("Error creating bootsrapper: %s", err)
|
|
}
|
|
return bs
|
|
}
|
|
|
|
// GenesisBlock produces a genesis block for the default test channel id
|
|
func (bs *Bootstrapper) GenesisBlock() *cb.Block {
|
|
// TODO(mjs): remove
|
|
return genesis.NewFactoryImpl(bs.channelGroup).Block("testchannelid")
|
|
}
|
|
|
|
// GenesisBlockForChannel produces a genesis block for a given channel ID
|
|
func (bs *Bootstrapper) GenesisBlockForChannel(channelID string) *cb.Block {
|
|
return genesis.NewFactoryImpl(bs.channelGroup).Block(channelID)
|
|
}
|
|
|
|
func (bs *Bootstrapper) GenesisChannelGroup() *cb.ConfigGroup {
|
|
return bs.channelGroup
|
|
}
|