340 lines
10 KiB
Go
340 lines
10 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package channelconfig
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"os"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
cb "github.com/hyperledger/fabric-protos-go/common"
|
|
mspprotos "github.com/hyperledger/fabric-protos-go/msp"
|
|
ab "github.com/hyperledger/fabric-protos-go/orderer"
|
|
"github.com/hyperledger/fabric-protos-go/orderer/etcdraft"
|
|
"github.com/hyperledger/fabric-protos-go/orderer/smartbft"
|
|
pb "github.com/hyperledger/fabric-protos-go/peer"
|
|
"github.com/hyperledger/fabric/bccsp"
|
|
"github.com/hyperledger/fabric/protoutil"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
const (
|
|
// ReadersPolicyKey is the key used for the read policy
|
|
ReadersPolicyKey = "Readers"
|
|
|
|
// WritersPolicyKey is the key used for the read policy
|
|
WritersPolicyKey = "Writers"
|
|
|
|
// AdminsPolicyKey is the key used for the read policy
|
|
AdminsPolicyKey = "Admins"
|
|
|
|
defaultHashingAlgorithm = bccsp.SHA256
|
|
|
|
defaultBlockDataHashingStructureWidth = math.MaxUint32
|
|
)
|
|
|
|
// ConfigValue defines a common representation for different *cb.ConfigValue values.
|
|
type ConfigValue interface {
|
|
// Key is the key this value should be stored in the *cb.ConfigGroup.Values map.
|
|
Key() string
|
|
|
|
// Value is the message which should be marshaled to opaque bytes for the *cb.ConfigValue.value.
|
|
Value() proto.Message
|
|
}
|
|
|
|
// StandardConfigValue implements the ConfigValue interface.
|
|
type StandardConfigValue struct {
|
|
key string
|
|
value proto.Message
|
|
}
|
|
|
|
// Key is the key this value should be stored in the *cb.ConfigGroup.Values map.
|
|
func (scv *StandardConfigValue) Key() string {
|
|
return scv.key
|
|
}
|
|
|
|
// Value is the message which should be marshaled to opaque bytes for the *cb.ConfigValue.value.
|
|
func (scv *StandardConfigValue) Value() proto.Message {
|
|
return scv.value
|
|
}
|
|
|
|
// ConsortiumValue returns the config definition for the consortium name.
|
|
// It is a value for the channel group.
|
|
func ConsortiumValue(name string) *StandardConfigValue {
|
|
return &StandardConfigValue{
|
|
key: ConsortiumKey,
|
|
value: &cb.Consortium{
|
|
Name: name,
|
|
},
|
|
}
|
|
}
|
|
|
|
// HashingAlgorithmValue returns the default hashing algorithm.
|
|
// It is a value for the /Channel group.
|
|
func HashingAlgorithmValue() *StandardConfigValue {
|
|
return &StandardConfigValue{
|
|
key: HashingAlgorithmKey,
|
|
value: &cb.HashingAlgorithm{
|
|
Name: defaultHashingAlgorithm,
|
|
},
|
|
}
|
|
}
|
|
|
|
// BlockDataHashingStructureValue returns the only currently valid block data hashing structure.
|
|
// It is a value for the /Channel group.
|
|
func BlockDataHashingStructureValue() *StandardConfigValue {
|
|
return &StandardConfigValue{
|
|
key: BlockDataHashingStructureKey,
|
|
value: &cb.BlockDataHashingStructure{
|
|
Width: defaultBlockDataHashingStructureWidth,
|
|
},
|
|
}
|
|
}
|
|
|
|
// OrdererAddressesValue returns the a config definition for the orderer addresses.
|
|
// It is a value for the /Channel group.
|
|
func OrdererAddressesValue(addresses []string) *StandardConfigValue {
|
|
return &StandardConfigValue{
|
|
key: OrdererAddressesKey,
|
|
value: &cb.OrdererAddresses{
|
|
Addresses: addresses,
|
|
},
|
|
}
|
|
}
|
|
|
|
// ConsensusTypeValue returns the config definition for the orderer consensus type.
|
|
// It is a value for the /Channel/Orderer group.
|
|
func ConsensusTypeValue(consensusType string, consensusMetadata []byte) *StandardConfigValue {
|
|
return &StandardConfigValue{
|
|
key: ConsensusTypeKey,
|
|
value: &ab.ConsensusType{
|
|
Type: consensusType,
|
|
Metadata: consensusMetadata,
|
|
},
|
|
}
|
|
}
|
|
|
|
// BatchSizeValue returns the config definition for the orderer batch size.
|
|
// It is a value for the /Channel/Orderer group.
|
|
func BatchSizeValue(maxMessages, absoluteMaxBytes, preferredMaxBytes uint32) *StandardConfigValue {
|
|
return &StandardConfigValue{
|
|
key: BatchSizeKey,
|
|
value: &ab.BatchSize{
|
|
MaxMessageCount: maxMessages,
|
|
AbsoluteMaxBytes: absoluteMaxBytes,
|
|
PreferredMaxBytes: preferredMaxBytes,
|
|
},
|
|
}
|
|
}
|
|
|
|
// BatchTimeoutValue returns the config definition for the orderer batch timeout.
|
|
// It is a value for the /Channel/Orderer group.
|
|
func BatchTimeoutValue(timeout string) *StandardConfigValue {
|
|
return &StandardConfigValue{
|
|
key: BatchTimeoutKey,
|
|
value: &ab.BatchTimeout{
|
|
Timeout: timeout,
|
|
},
|
|
}
|
|
}
|
|
|
|
// ChannelRestrictionsValue returns the config definition for the orderer channel restrictions.
|
|
// It is a value for the /Channel/Orderer group.
|
|
func ChannelRestrictionsValue(maxChannelCount uint64) *StandardConfigValue {
|
|
return &StandardConfigValue{
|
|
key: ChannelRestrictionsKey,
|
|
value: &ab.ChannelRestrictions{
|
|
MaxCount: maxChannelCount,
|
|
},
|
|
}
|
|
}
|
|
|
|
// MSPValue returns the config definition for an MSP.
|
|
// It is a value for the /Channel/Orderer/*, /Channel/Application/*, and /Channel/Consortiums/*/*/* groups.
|
|
func MSPValue(mspDef *mspprotos.MSPConfig) *StandardConfigValue {
|
|
return &StandardConfigValue{
|
|
key: MSPKey,
|
|
value: mspDef,
|
|
}
|
|
}
|
|
|
|
// CapabilitiesValue returns the config definition for a set of capabilities.
|
|
// It is a value for the /Channel/Orderer, Channel/Application/, and /Channel groups.
|
|
func CapabilitiesValue(capabilities map[string]bool) *StandardConfigValue {
|
|
c := &cb.Capabilities{
|
|
Capabilities: make(map[string]*cb.Capability),
|
|
}
|
|
|
|
for capability, required := range capabilities {
|
|
if !required {
|
|
continue
|
|
}
|
|
c.Capabilities[capability] = &cb.Capability{}
|
|
}
|
|
|
|
return &StandardConfigValue{
|
|
key: CapabilitiesKey,
|
|
value: c,
|
|
}
|
|
}
|
|
|
|
func OrderersValue(consenters []*cb.Consenter) *StandardConfigValue {
|
|
o := &cb.Orderers{
|
|
ConsenterMapping: consenters,
|
|
}
|
|
return &StandardConfigValue{
|
|
key: OrderersKey,
|
|
value: o,
|
|
}
|
|
}
|
|
|
|
// EndpointsValue returns the config definition for the orderer addresses at an org scoped level.
|
|
// It is a value for the /Channel/Orderer/<OrgName> group.
|
|
func EndpointsValue(addresses []string) *StandardConfigValue {
|
|
return &StandardConfigValue{
|
|
key: EndpointsKey,
|
|
value: &cb.OrdererAddresses{
|
|
Addresses: addresses,
|
|
},
|
|
}
|
|
}
|
|
|
|
// AnchorPeersValue returns the config definition for an org's anchor peers.
|
|
// It is a value for the /Channel/Application/*.
|
|
func AnchorPeersValue(anchorPeers []*pb.AnchorPeer) *StandardConfigValue {
|
|
return &StandardConfigValue{
|
|
key: AnchorPeersKey,
|
|
value: &pb.AnchorPeers{AnchorPeers: anchorPeers},
|
|
}
|
|
}
|
|
|
|
// ChannelCreationPolicyValue returns the config definition for a consortium's channel creation policy
|
|
// It is a value for the /Channel/Consortiums/*/*.
|
|
func ChannelCreationPolicyValue(policy *cb.Policy) *StandardConfigValue {
|
|
return &StandardConfigValue{
|
|
key: ChannelCreationPolicyKey,
|
|
value: policy,
|
|
}
|
|
}
|
|
|
|
// ACLValues returns the config definition for an applications resources based ACL definitions.
|
|
// It is a value for the /Channel/Application/.
|
|
func ACLValues(acls map[string]string) *StandardConfigValue {
|
|
a := &pb.ACLs{
|
|
Acls: make(map[string]*pb.APIResource),
|
|
}
|
|
|
|
for apiResource, policyRef := range acls {
|
|
a.Acls[apiResource] = &pb.APIResource{PolicyRef: policyRef}
|
|
}
|
|
|
|
return &StandardConfigValue{
|
|
key: ACLsKey,
|
|
value: a,
|
|
}
|
|
}
|
|
|
|
// ValidateCapabilities validates whether the peer can meet the capabilities requirement in the given config block
|
|
func ValidateCapabilities(block *cb.Block, bccsp bccsp.BCCSP) error {
|
|
cc, err := extractChannelConfig(block, bccsp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Check the channel top-level capabilities
|
|
if err := cc.Capabilities().Supported(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Check the application capabilities
|
|
return cc.ApplicationConfig().Capabilities().Supported()
|
|
}
|
|
|
|
// ExtractMSPIDsForApplicationOrgs extracts MSPIDs for application organizations
|
|
func ExtractMSPIDsForApplicationOrgs(block *cb.Block, bccsp bccsp.BCCSP) ([]string, error) {
|
|
cc, err := extractChannelConfig(block, bccsp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if cc.ApplicationConfig() == nil {
|
|
return nil, errors.Errorf("could not get application config for the channel")
|
|
}
|
|
orgs := cc.ApplicationConfig().Organizations()
|
|
mspids := make([]string, 0, len(orgs))
|
|
for _, org := range orgs {
|
|
mspids = append(mspids, org.MSPID())
|
|
}
|
|
return mspids, nil
|
|
}
|
|
|
|
func extractChannelConfig(block *cb.Block, bccsp bccsp.BCCSP) (*ChannelConfig, error) {
|
|
envelopeConfig, err := protoutil.ExtractEnvelope(block, 0)
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "malformed configuration block")
|
|
}
|
|
|
|
configEnv := &cb.ConfigEnvelope{}
|
|
_, err = protoutil.UnmarshalEnvelopeOfType(envelopeConfig, cb.HeaderType_CONFIG, configEnv)
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "malformed configuration envelope")
|
|
}
|
|
|
|
if configEnv.Config == nil {
|
|
return nil, errors.New("no config found in envelope")
|
|
}
|
|
|
|
if configEnv.Config.ChannelGroup == nil {
|
|
return nil, errors.New("no channel configuration found in the config block")
|
|
}
|
|
|
|
if configEnv.Config.ChannelGroup.Groups == nil {
|
|
return nil, errors.New("no channel configuration groups are available")
|
|
}
|
|
|
|
_, exists := configEnv.Config.ChannelGroup.Groups[ApplicationGroupKey]
|
|
if !exists {
|
|
return nil, errors.Errorf("invalid configuration block, missing %s configuration group", ApplicationGroupKey)
|
|
}
|
|
|
|
cc, err := NewChannelConfig(configEnv.Config.ChannelGroup, bccsp)
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "no valid channel configuration found")
|
|
}
|
|
return cc, nil
|
|
}
|
|
|
|
// MarshalEtcdRaftMetadata serializes etcd RAFT metadata.
|
|
func MarshalEtcdRaftMetadata(md *etcdraft.ConfigMetadata) ([]byte, error) {
|
|
copyMd := proto.Clone(md).(*etcdraft.ConfigMetadata)
|
|
for _, c := range copyMd.Consenters {
|
|
// Expect the user to set the config value for client/server certs to the
|
|
// path where they are persisted locally, then load these files to memory.
|
|
clientCert, err := os.ReadFile(string(c.GetClientTlsCert()))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot load client cert for consenter %s:%d: %s", c.GetHost(), c.GetPort(), err)
|
|
}
|
|
c.ClientTlsCert = clientCert
|
|
|
|
serverCert, err := os.ReadFile(string(c.GetServerTlsCert()))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot load server cert for consenter %s:%d: %s", c.GetHost(), c.GetPort(), err)
|
|
}
|
|
c.ServerTlsCert = serverCert
|
|
}
|
|
return proto.Marshal(copyMd)
|
|
}
|
|
|
|
// MarshalBFTOptions serializes smartbft options.
|
|
func MarshalBFTOptions(op *smartbft.Options) ([]byte, error) {
|
|
if copyMd, ok := proto.Clone(op).(*smartbft.Options); ok {
|
|
return proto.Marshal(copyMd)
|
|
} else {
|
|
return nil, errors.New("consenter options type mismatch")
|
|
}
|
|
}
|