go_study/fabric-main/internal/configtxgen/encoder/encoder_test.go

1570 lines
47 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package encoder_test
import (
"fmt"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/golang/protobuf/proto"
cb "github.com/hyperledger/fabric-protos-go/common"
ab "github.com/hyperledger/fabric-protos-go/orderer"
"github.com/hyperledger/fabric-protos-go/orderer/etcdraft"
"github.com/hyperledger/fabric-protos-go/orderer/smartbft"
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/internal/configtxgen/encoder"
"github.com/hyperledger/fabric/internal/configtxgen/encoder/fakes"
"github.com/hyperledger/fabric/internal/configtxgen/genesisconfig"
"github.com/hyperledger/fabric/internal/pkg/identity"
"github.com/hyperledger/fabric/protoutil"
)
//go:generate counterfeiter -o fakes/signer_serializer.go --fake-name SignerSerializer . signerSerializer
type signerSerializer interface {
identity.SignerSerializer
}
func CreateStandardPolicies() map[string]*genesisconfig.Policy {
return map[string]*genesisconfig.Policy{
"Admins": {
Type: "ImplicitMeta",
Rule: "ANY Admins",
},
"Readers": {
Type: "ImplicitMeta",
Rule: "ANY Readers",
},
"Writers": {
Type: "ImplicitMeta",
Rule: "ANY Writers",
},
}
}
func CreateStandardOrdererPolicies() map[string]*genesisconfig.Policy {
policies := CreateStandardPolicies()
policies["BlockValidation"] = &genesisconfig.Policy{
Type: "ImplicitMeta",
Rule: "ANY Admins",
}
return policies
}
var _ = Describe("Encoder", func() {
Describe("AddOrdererPolicies", func() {
var (
cg *cb.ConfigGroup
policies map[string]*genesisconfig.Policy
)
BeforeEach(func() {
cg = protoutil.NewConfigGroup()
policies = CreateStandardOrdererPolicies()
})
It("adds the block validation policy to the group", func() {
err := encoder.AddOrdererPolicies(cg, policies, "Admins")
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Policies)).To(Equal(4))
Expect(cg.Policies["BlockValidation"].Policy).To(Equal(&cb.Policy{
Type: int32(cb.Policy_IMPLICIT_META),
Value: protoutil.MarshalOrPanic(&cb.ImplicitMetaPolicy{
SubPolicy: "Admins",
Rule: cb.ImplicitMetaPolicy_ANY,
}),
}))
})
Context("when the policy map is nil", func() {
BeforeEach(func() {
policies = nil
})
It("returns an error", func() {
err := encoder.AddOrdererPolicies(cg, policies, "Admins")
Expect(err).To(MatchError("no policies defined"))
})
})
Context("when the policy map is missing 'BlockValidation'", func() {
BeforeEach(func() {
delete(policies, "BlockValidation")
})
It("returns an error", func() {
err := encoder.AddOrdererPolicies(cg, policies, "Admins")
Expect(err).To(MatchError("no BlockValidation policy defined"))
})
})
})
Describe("AddPolicies", func() {
var (
cg *cb.ConfigGroup
policies map[string]*genesisconfig.Policy
)
BeforeEach(func() {
cg = protoutil.NewConfigGroup()
policies = CreateStandardPolicies()
})
It("adds the standard policies to the group", func() {
err := encoder.AddPolicies(cg, policies, "Admins")
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Policies)).To(Equal(3))
Expect(cg.Policies["Admins"].Policy).To(Equal(&cb.Policy{
Type: int32(cb.Policy_IMPLICIT_META),
Value: protoutil.MarshalOrPanic(&cb.ImplicitMetaPolicy{
SubPolicy: "Admins",
Rule: cb.ImplicitMetaPolicy_ANY,
}),
}))
Expect(cg.Policies["Readers"].Policy).To(Equal(&cb.Policy{
Type: int32(cb.Policy_IMPLICIT_META),
Value: protoutil.MarshalOrPanic(&cb.ImplicitMetaPolicy{
SubPolicy: "Readers",
Rule: cb.ImplicitMetaPolicy_ANY,
}),
}))
Expect(cg.Policies["Writers"].Policy).To(Equal(&cb.Policy{
Type: int32(cb.Policy_IMPLICIT_META),
Value: protoutil.MarshalOrPanic(&cb.ImplicitMetaPolicy{
SubPolicy: "Writers",
Rule: cb.ImplicitMetaPolicy_ANY,
}),
}))
})
Context("when the policy map is nil", func() {
BeforeEach(func() {
policies = nil
})
It("returns an error", func() {
err := encoder.AddPolicies(cg, policies, "Admins")
Expect(err).To(MatchError("no policies defined"))
})
})
Context("when the policy map is missing 'Admins'", func() {
BeforeEach(func() {
delete(policies, "Admins")
})
It("returns an error", func() {
err := encoder.AddPolicies(cg, policies, "Admins")
Expect(err).To(MatchError("no Admins policy defined"))
})
})
Context("when the policy map is missing 'Readers'", func() {
BeforeEach(func() {
delete(policies, "Readers")
})
It("returns an error", func() {
err := encoder.AddPolicies(cg, policies, "Readers")
Expect(err).To(MatchError("no Readers policy defined"))
})
})
Context("when the policy map is missing 'Writers'", func() {
BeforeEach(func() {
delete(policies, "Writers")
})
It("returns an error", func() {
err := encoder.AddPolicies(cg, policies, "Writers")
Expect(err).To(MatchError("no Writers policy defined"))
})
})
Context("when the signature policy definition is bad", func() {
BeforeEach(func() {
policies["Readers"].Type = "Signature"
policies["Readers"].Rule = "garbage"
})
It("wraps and returns the error", func() {
err := encoder.AddPolicies(cg, policies, "Readers")
Expect(err).To(MatchError("invalid signature policy rule 'garbage': unrecognized token 'garbage' in policy string"))
})
})
Context("when the implicit policy definition is bad", func() {
BeforeEach(func() {
policies["Readers"].Type = "ImplicitMeta"
policies["Readers"].Rule = "garbage"
})
It("wraps and returns the error", func() {
err := encoder.AddPolicies(cg, policies, "Readers")
Expect(err).To(MatchError("invalid implicit meta policy rule 'garbage': expected two space separated tokens, but got 1"))
})
})
Context("when the policy type is unknown", func() {
BeforeEach(func() {
policies["Readers"].Type = "garbage"
})
It("returns an error", func() {
err := encoder.AddPolicies(cg, policies, "Readers")
Expect(err).To(MatchError("unknown policy type: garbage"))
})
})
})
Describe("NewChannelGroup", func() {
var conf *genesisconfig.Profile
BeforeEach(func() {
conf = &genesisconfig.Profile{
Consortium: "MyConsortium",
Policies: CreateStandardPolicies(),
Application: &genesisconfig.Application{
Policies: CreateStandardPolicies(),
},
Orderer: &genesisconfig.Orderer{
OrdererType: "solo",
Policies: CreateStandardOrdererPolicies(),
Addresses: []string{"foo.com:7050", "bar.com:8050"},
},
Consortiums: map[string]*genesisconfig.Consortium{
"SampleConsortium": {},
},
Capabilities: map[string]bool{
"FakeCapability": true,
},
}
})
It("translates the config into a config group", func() {
cg, err := encoder.NewChannelGroup(conf)
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Values)).To(Equal(5))
Expect(cg.Values["BlockDataHashingStructure"]).NotTo(BeNil())
Expect(cg.Values["Consortium"]).NotTo(BeNil())
Expect(cg.Values["Capabilities"]).NotTo(BeNil())
Expect(cg.Values["HashingAlgorithm"]).NotTo(BeNil())
Expect(cg.Values["OrdererAddresses"]).NotTo(BeNil())
})
Context("when the policy definition is bad", func() {
BeforeEach(func() {
conf.Policies["Admins"].Rule = "garbage"
})
It("wraps and returns the error", func() {
_, err := encoder.NewChannelGroup(conf)
Expect(err).To(MatchError("error adding policies to channel group: invalid implicit meta policy rule 'garbage': expected two space separated tokens, but got 1"))
})
})
Context("when the orderer addresses are omitted", func() {
BeforeEach(func() {
conf.Orderer.Addresses = []string{}
})
It("does not create the config value", func() {
cg, err := encoder.NewChannelGroup(conf)
Expect(err).NotTo(HaveOccurred())
Expect(cg.Values["OrdererAddresses"]).To(BeNil())
})
})
Context("when the orderer config is bad", func() {
BeforeEach(func() {
conf.Orderer.OrdererType = "bad-type"
})
It("wraps and returns the error", func() {
_, err := encoder.NewChannelGroup(conf)
Expect(err).To(MatchError("could not create orderer group: unknown orderer type: bad-type"))
})
Context("when the orderer config is missing", func() {
BeforeEach(func() {
conf.Orderer = nil
})
It("handles it gracefully", func() {
_, err := encoder.NewChannelGroup(conf)
Expect(err).NotTo(HaveOccurred())
})
})
})
Context("when the application config is bad", func() {
BeforeEach(func() {
conf.Application.Policies["Admins"] = &genesisconfig.Policy{
Type: "garbage",
}
})
It("wraps and returns the error", func() {
_, err := encoder.NewChannelGroup(conf)
Expect(err).To(MatchError("could not create application group: error adding policies to application group: unknown policy type: garbage"))
})
})
Context("when the consortium config is bad", func() {
BeforeEach(func() {
conf.Consortiums["SampleConsortium"].Organizations = []*genesisconfig.Organization{
{
Policies: map[string]*genesisconfig.Policy{
"garbage-policy": {
Type: "garbage",
},
},
},
}
})
It("wraps and returns the error", func() {
_, err := encoder.NewChannelGroup(conf)
Expect(err).To(MatchError("could not create consortiums group: failed to create consortium SampleConsortium: failed to create consortium org: 1 - Error loading MSP configuration for org: : unknown MSP type ''"))
})
})
})
Describe("NewOrdererGroup", func() {
var conf *genesisconfig.Orderer
var channelCapabilities map[string]bool
BeforeEach(func() {
conf = &genesisconfig.Orderer{
OrdererType: "solo",
Organizations: []*genesisconfig.Organization{
{
MSPDir: "../../../sampleconfig/msp",
ID: "SampleMSP",
MSPType: "bccsp",
Name: "SampleOrg",
Policies: CreateStandardPolicies(),
},
},
Policies: CreateStandardOrdererPolicies(),
Capabilities: map[string]bool{
"FakeCapability": true,
},
}
channelCapabilities = map[string]bool{
"V3_0": true,
}
})
It("translates the config into a config group", func() {
cg, err := encoder.NewOrdererGroup(conf, channelCapabilities)
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Policies)).To(Equal(4)) // BlockValidation automatically added
Expect(cg.Policies["Admins"]).NotTo(BeNil())
Expect(cg.Policies["Readers"]).NotTo(BeNil())
Expect(cg.Policies["Writers"]).NotTo(BeNil())
Expect(cg.Policies["BlockValidation"]).NotTo(BeNil())
Expect(len(cg.Groups)).To(Equal(1))
Expect(cg.Groups["SampleOrg"]).NotTo(BeNil())
Expect(len(cg.Values)).To(Equal(5))
Expect(cg.Values["BatchSize"]).NotTo(BeNil())
Expect(cg.Values["BatchTimeout"]).NotTo(BeNil())
Expect(cg.Values["ChannelRestrictions"]).NotTo(BeNil())
Expect(cg.Values["Capabilities"]).NotTo(BeNil())
})
Context("when the policy definition is bad", func() {
BeforeEach(func() {
conf.Policies["Admins"].Rule = "garbage"
})
It("wraps and returns the error", func() {
_, err := encoder.NewOrdererGroup(conf, channelCapabilities)
Expect(err).To(MatchError("error adding policies to orderer group: invalid implicit meta policy rule 'garbage': expected two space separated tokens, but got 1"))
})
})
Context("when the consensus type is etcd/raft", func() {
BeforeEach(func() {
conf.OrdererType = "etcdraft"
conf.EtcdRaft = &etcdraft.ConfigMetadata{
Options: &etcdraft.Options{
TickInterval: "500ms",
},
}
})
It("adds the raft metadata", func() {
cg, err := encoder.NewOrdererGroup(conf, channelCapabilities)
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Values)).To(Equal(5))
consensusType := &ab.ConsensusType{}
err = proto.Unmarshal(cg.Values["ConsensusType"].Value, consensusType)
Expect(err).NotTo(HaveOccurred())
Expect(consensusType.Type).To(Equal("etcdraft"))
metadata := &etcdraft.ConfigMetadata{}
err = proto.Unmarshal(consensusType.Metadata, metadata)
Expect(err).NotTo(HaveOccurred())
Expect(metadata.Options.TickInterval).To(Equal("500ms"))
})
Context("when the raft configuration is bad", func() {
BeforeEach(func() {
conf.EtcdRaft = &etcdraft.ConfigMetadata{
Consenters: []*etcdraft.Consenter{
{},
},
}
})
It("wraps and returns the error", func() {
_, err := encoder.NewOrdererGroup(conf, channelCapabilities)
Expect(err).To(MatchError("cannot marshal metadata for orderer type etcdraft: cannot load client cert for consenter :0: open : no such file or directory"))
})
})
})
Context("when the consensus type is BFT", func() {
BeforeEach(func() {
conf.OrdererType = "BFT"
conf.ConsenterMapping = []*genesisconfig.Consenter{
{
ID: 1,
Host: "host1",
Port: 1001,
MSPID: "MSP1",
},
{
ID: 2,
Host: "host2",
Port: 1002,
MSPID: "MSP2",
ClientTLSCert: "../../../sampleconfig/msp/admincerts/admincert.pem",
ServerTLSCert: "../../../sampleconfig/msp/admincerts/admincert.pem",
Identity: "../../../sampleconfig/msp/admincerts/admincert.pem",
},
}
conf.SmartBFT = &smartbft.Options{}
})
It("adds the Orderers key", func() {
cg, err := encoder.NewOrdererGroup(conf, channelCapabilities)
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Values)).To(Equal(6))
Expect(cg.Values["Orderers"]).NotTo(BeNil())
orderersType := &cb.Orderers{}
err = proto.Unmarshal(cg.Values["Orderers"].Value, orderersType)
Expect(err).NotTo(HaveOccurred())
Expect(len(orderersType.ConsenterMapping)).To(Equal(2))
consenter1 := orderersType.ConsenterMapping[0]
Expect(consenter1.Id).To(Equal(uint32(1)))
Expect(consenter1.ClientTlsCert).To(BeNil())
consenter2 := orderersType.ConsenterMapping[1]
Expect(consenter2.Id).To(Equal(uint32(2)))
Expect(consenter2.ClientTlsCert).ToNot(BeNil())
})
})
Context("when the consensus type is unknown", func() {
BeforeEach(func() {
conf.OrdererType = "bad-type"
})
It("returns an error", func() {
_, err := encoder.NewOrdererGroup(conf, channelCapabilities)
Expect(err).To(MatchError("unknown orderer type: bad-type"))
})
})
Context("when the org definition is bad", func() {
BeforeEach(func() {
conf.Organizations[0].MSPType = "garbage"
})
It("wraps and returns the error", func() {
_, err := encoder.NewOrdererGroup(conf, channelCapabilities)
Expect(err).To(MatchError("failed to create orderer org: 1 - Error loading MSP configuration for org: SampleOrg: unknown MSP type 'garbage'"))
})
})
})
Describe("NewApplicationGroup", func() {
var conf *genesisconfig.Application
BeforeEach(func() {
conf = &genesisconfig.Application{
Organizations: []*genesisconfig.Organization{
{
MSPDir: "../../../sampleconfig/msp",
ID: "SampleMSP",
MSPType: "bccsp",
Name: "SampleOrg",
Policies: CreateStandardPolicies(),
},
},
ACLs: map[string]string{
"SomeACL": "SomePolicy",
},
Policies: CreateStandardPolicies(),
Capabilities: map[string]bool{
"FakeCapability": true,
},
}
})
It("translates the config into a config group", func() {
cg, err := encoder.NewApplicationGroup(conf)
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Policies)).To(Equal(3))
Expect(cg.Policies["Admins"]).NotTo(BeNil())
Expect(cg.Policies["Readers"]).NotTo(BeNil())
Expect(cg.Policies["Writers"]).NotTo(BeNil())
Expect(len(cg.Groups)).To(Equal(1))
Expect(cg.Groups["SampleOrg"]).NotTo(BeNil())
Expect(len(cg.Values)).To(Equal(2))
Expect(cg.Values["ACLs"]).NotTo(BeNil())
Expect(cg.Values["Capabilities"]).NotTo(BeNil())
})
Context("when the policy definition is bad", func() {
BeforeEach(func() {
conf.Policies["Admins"].Rule = "garbage"
})
It("wraps and returns the error", func() {
_, err := encoder.NewApplicationGroup(conf)
Expect(err).To(MatchError("error adding policies to application group: invalid implicit meta policy rule 'garbage': expected two space separated tokens, but got 1"))
})
})
Context("when the org definition is bad", func() {
BeforeEach(func() {
conf.Organizations[0].MSPType = "garbage"
})
It("wraps and returns the error", func() {
_, err := encoder.NewApplicationGroup(conf)
Expect(err).To(MatchError("failed to create application org: 1 - Error loading MSP configuration for org SampleOrg: unknown MSP type 'garbage'"))
})
})
})
Describe("NewConsortiumOrgGroup", func() {
var conf *genesisconfig.Organization
BeforeEach(func() {
conf = &genesisconfig.Organization{
MSPDir: "../../../sampleconfig/msp",
ID: "SampleMSP",
MSPType: "bccsp",
Name: "SampleOrg",
Policies: CreateStandardPolicies(),
}
})
It("translates the config into a config group", func() {
cg, err := encoder.NewConsortiumOrgGroup(conf)
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Values)).To(Equal(1))
Expect(cg.Values["MSP"]).NotTo(BeNil())
Expect(len(cg.Policies)).To(Equal(3))
Expect(cg.Policies["Admins"]).NotTo(BeNil())
Expect(cg.Policies["Readers"]).NotTo(BeNil())
Expect(cg.Policies["Writers"]).NotTo(BeNil())
})
Context("when the org is marked to be skipped as foreign", func() {
BeforeEach(func() {
conf.SkipAsForeign = true
})
It("returns an empty org group with mod policy set", func() {
cg, err := encoder.NewConsortiumOrgGroup(conf)
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Values)).To(Equal(0))
Expect(len(cg.Policies)).To(Equal(0))
})
Context("even when the MSP dir is invalid/corrupt", func() {
BeforeEach(func() {
conf.MSPDir = "garbage"
})
It("returns without error", func() {
_, err := encoder.NewConsortiumOrgGroup(conf)
Expect(err).NotTo(HaveOccurred())
})
})
})
Context("when dev mode is enabled", func() {
BeforeEach(func() {
conf.AdminPrincipal = "Member"
})
It("does not produce an error", func() {
_, err := encoder.NewConsortiumOrgGroup(conf)
Expect(err).NotTo(HaveOccurred())
})
})
Context("when the policy definition is bad", func() {
BeforeEach(func() {
conf.Policies["Admins"].Rule = "garbage"
})
It("wraps and returns the error", func() {
_, err := encoder.NewConsortiumOrgGroup(conf)
Expect(err).To(MatchError("error adding policies to consortiums org group 'SampleOrg': invalid implicit meta policy rule 'garbage': expected two space separated tokens, but got 1"))
})
})
})
Describe("NewOrdererOrgGroup", func() {
var conf *genesisconfig.Organization
BeforeEach(func() {
conf = &genesisconfig.Organization{
MSPDir: "../../../sampleconfig/msp",
ID: "SampleMSP",
MSPType: "bccsp",
Name: "SampleOrg",
Policies: CreateStandardPolicies(),
OrdererEndpoints: []string{
"foo:7050",
"bar:8050",
},
}
})
It("translates the config into a config group", func() {
cg, err := encoder.NewOrdererOrgGroup(conf)
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Values)).To(Equal(2))
Expect(cg.Values["MSP"]).NotTo(BeNil())
Expect(len(cg.Policies)).To(Equal(3))
Expect(cg.Values["Endpoints"]).NotTo(BeNil())
Expect(cg.Policies["Admins"]).NotTo(BeNil())
Expect(cg.Policies["Readers"]).NotTo(BeNil())
Expect(cg.Policies["Writers"]).NotTo(BeNil())
})
Context("when the org is marked to be skipped as foreign", func() {
BeforeEach(func() {
conf.SkipAsForeign = true
})
It("returns an empty org group with mod policy set", func() {
cg, err := encoder.NewOrdererOrgGroup(conf)
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Values)).To(Equal(0))
Expect(len(cg.Policies)).To(Equal(0))
})
Context("even when the MSP dir is invalid/corrupt", func() {
BeforeEach(func() {
conf.MSPDir = "garbage"
})
It("returns without error", func() {
_, err := encoder.NewOrdererOrgGroup(conf)
Expect(err).NotTo(HaveOccurred())
})
})
})
Context("when there are no ordering endpoints", func() {
BeforeEach(func() {
conf.OrdererEndpoints = []string{}
})
It("does not include the endpoints in the config group", func() {
cg, err := encoder.NewOrdererOrgGroup(conf)
Expect(err).NotTo(HaveOccurred())
Expect(cg.Values["Endpoints"]).To(BeNil())
})
})
Context("when dev mode is enabled", func() {
BeforeEach(func() {
conf.AdminPrincipal = "Member"
})
It("does not produce an error", func() {
_, err := encoder.NewOrdererOrgGroup(conf)
Expect(err).NotTo(HaveOccurred())
})
})
Context("when the policy definition is bad", func() {
BeforeEach(func() {
conf.Policies["Admins"].Rule = "garbage"
})
It("wraps and returns the error", func() {
_, err := encoder.NewOrdererOrgGroup(conf)
Expect(err).To(MatchError("error adding policies to orderer org group 'SampleOrg': invalid implicit meta policy rule 'garbage': expected two space separated tokens, but got 1"))
})
})
})
Describe("NewApplicationOrgGroup", func() {
var conf *genesisconfig.Organization
BeforeEach(func() {
conf = &genesisconfig.Organization{
MSPDir: "../../../sampleconfig/msp",
ID: "SampleMSP",
MSPType: "bccsp",
Name: "SampleOrg",
Policies: CreateStandardPolicies(),
AnchorPeers: []*genesisconfig.AnchorPeer{
{
Host: "hostname",
Port: 5555,
},
},
}
})
It("translates the config into a config group", func() {
cg, err := encoder.NewApplicationOrgGroup(conf)
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Values)).To(Equal(2))
Expect(cg.Values["MSP"]).NotTo(BeNil())
Expect(cg.Values["AnchorPeers"]).NotTo(BeNil())
Expect(len(cg.Policies)).To(Equal(3))
Expect(cg.Policies["Admins"]).NotTo(BeNil())
Expect(cg.Policies["Readers"]).NotTo(BeNil())
Expect(cg.Policies["Writers"]).NotTo(BeNil())
Expect(len(cg.Values)).To(Equal(2))
Expect(cg.Values["MSP"]).NotTo(BeNil())
Expect(cg.Values["AnchorPeers"]).NotTo(BeNil())
})
Context("when the org is marked to be skipped as foreign", func() {
BeforeEach(func() {
conf.SkipAsForeign = true
})
It("returns an empty org group with mod policy set", func() {
cg, err := encoder.NewApplicationOrgGroup(conf)
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Values)).To(Equal(0))
Expect(len(cg.Policies)).To(Equal(0))
})
Context("even when the MSP dir is invalid/corrupt", func() {
BeforeEach(func() {
conf.MSPDir = "garbage"
})
It("returns without error", func() {
_, err := encoder.NewApplicationOrgGroup(conf)
Expect(err).NotTo(HaveOccurred())
})
})
})
Context("when the policy definition is bad", func() {
BeforeEach(func() {
conf.Policies["Admins"].Type = "garbage"
})
It("wraps and returns the error", func() {
_, err := encoder.NewApplicationOrgGroup(conf)
Expect(err).To(MatchError("error adding policies to application org group SampleOrg: unknown policy type: garbage"))
})
})
Context("when the MSP definition is bad", func() {
BeforeEach(func() {
conf.MSPDir = "garbage"
})
It("wraps and returns the error", func() {
_, err := encoder.NewApplicationOrgGroup(conf)
Expect(err).To(MatchError("1 - Error loading MSP configuration for org SampleOrg: could not load a valid ca certificate from directory garbage/cacerts: stat garbage/cacerts: no such file or directory"))
})
})
Context("when there are no anchor peers defined", func() {
BeforeEach(func() {
conf.AnchorPeers = nil
})
It("does not encode the anchor peers", func() {
cg, err := encoder.NewApplicationOrgGroup(conf)
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Values)).To(Equal(1))
Expect(cg.Values["AnchorPeers"]).To(BeNil())
})
})
})
Describe("ChannelCreationOperations", func() {
var (
conf *genesisconfig.Profile
template *cb.ConfigGroup
)
BeforeEach(func() {
conf = &genesisconfig.Profile{
Consortium: "MyConsortium",
Policies: CreateStandardPolicies(),
Application: &genesisconfig.Application{
Organizations: []*genesisconfig.Organization{
{
Name: "SampleOrg",
MSPDir: "../../../sampleconfig/msp",
ID: "SampleMSP",
MSPType: "bccsp",
Policies: CreateStandardPolicies(),
AnchorPeers: []*genesisconfig.AnchorPeer{
{
Host: "hostname",
Port: 4444,
},
},
},
},
Policies: CreateStandardPolicies(),
},
}
var err error
template, err = encoder.DefaultConfigTemplate(conf)
Expect(err).NotTo(HaveOccurred())
})
Describe("NewChannelCreateConfigUpdate", func() {
It("translates the config into a config group", func() {
cg, err := encoder.NewChannelCreateConfigUpdate("channel-id", conf, template)
Expect(err).NotTo(HaveOccurred())
expected := &cb.ConfigUpdate{
ChannelId: "channel-id",
ReadSet: &cb.ConfigGroup{
Groups: map[string]*cb.ConfigGroup{
"Application": {
Groups: map[string]*cb.ConfigGroup{
"SampleOrg": {},
},
},
},
Values: map[string]*cb.ConfigValue{
"Consortium": {},
},
},
WriteSet: &cb.ConfigGroup{
Groups: map[string]*cb.ConfigGroup{
"Application": {
Version: 1,
ModPolicy: "Admins",
Groups: map[string]*cb.ConfigGroup{
"SampleOrg": {},
},
Policies: map[string]*cb.ConfigPolicy{
"Admins": {
Policy: &cb.Policy{
Type: int32(cb.Policy_IMPLICIT_META),
Value: protoutil.MarshalOrPanic(&cb.ImplicitMetaPolicy{
SubPolicy: "Admins",
Rule: cb.ImplicitMetaPolicy_ANY,
}),
},
ModPolicy: "Admins",
},
"Readers": {
Policy: &cb.Policy{
Type: int32(cb.Policy_IMPLICIT_META),
Value: protoutil.MarshalOrPanic(&cb.ImplicitMetaPolicy{
SubPolicy: "Readers",
Rule: cb.ImplicitMetaPolicy_ANY,
}),
},
ModPolicy: "Admins",
},
"Writers": {
Policy: &cb.Policy{
Type: int32(cb.Policy_IMPLICIT_META),
Value: protoutil.MarshalOrPanic(&cb.ImplicitMetaPolicy{
SubPolicy: "Writers",
Rule: cb.ImplicitMetaPolicy_ANY,
}),
},
ModPolicy: "Admins",
},
},
},
},
Values: map[string]*cb.ConfigValue{
"Consortium": {
Value: protoutil.MarshalOrPanic(&cb.Consortium{
Name: "MyConsortium",
}),
},
},
},
}
Expect(proto.Equal(expected, cg)).To(BeTrue())
})
Context("when the template configuration is not the default", func() {
BeforeEach(func() {
differentConf := &genesisconfig.Profile{
Consortium: "MyConsortium",
Policies: CreateStandardPolicies(),
Application: &genesisconfig.Application{
Organizations: []*genesisconfig.Organization{
{
MSPDir: "../../../sampleconfig/msp",
ID: "SampleMSP",
MSPType: "bccsp",
Name: "SampleOrg",
AnchorPeers: []*genesisconfig.AnchorPeer{
{
Host: "hostname",
Port: 5555,
},
},
Policies: CreateStandardPolicies(),
},
},
Policies: CreateStandardPolicies(),
},
}
var err error
template, err = encoder.DefaultConfigTemplate(differentConf)
Expect(err).NotTo(HaveOccurred())
})
It("reflects the additional modifications designated by the channel creation profile", func() {
cg, err := encoder.NewChannelCreateConfigUpdate("channel-id", conf, template)
Expect(err).NotTo(HaveOccurred())
Expect(cg.WriteSet.Groups["Application"].Groups["SampleOrg"].Values["AnchorPeers"].Version).To(Equal(uint64(1)))
})
})
Context("when the application config is bad", func() {
BeforeEach(func() {
conf.Application.Policies["Admins"].Type = "bad-type"
})
It("returns an error", func() {
_, err := encoder.NewChannelCreateConfigUpdate("channel-id", conf, template)
Expect(err).To(MatchError("could not turn parse profile into channel group: could not create application group: error adding policies to application group: unknown policy type: bad-type"))
})
Context("when the application config is missing", func() {
BeforeEach(func() {
conf.Application = nil
})
It("returns an error", func() {
_, err := encoder.NewChannelCreateConfigUpdate("channel-id", conf, template)
Expect(err).To(MatchError("cannot define a new channel with no Application section"))
})
})
})
Context("when the consortium is empty", func() {
BeforeEach(func() {
conf.Consortium = ""
})
It("returns an error", func() {
_, err := encoder.NewChannelCreateConfigUpdate("channel-id", conf, template)
Expect(err).To(MatchError("cannot define a new channel with no Consortium value"))
})
})
Context("when an update cannot be computed", func() {
It("returns an error", func() {
_, err := encoder.NewChannelCreateConfigUpdate("channel-id", conf, nil)
Expect(err).To(MatchError("could not compute update: no channel group included for original config"))
})
})
})
Describe("MakeChannelCreationTransaction", func() {
var fakeSigner *fakes.SignerSerializer
BeforeEach(func() {
fakeSigner = &fakes.SignerSerializer{}
fakeSigner.SerializeReturns([]byte("fake-creator"), nil)
})
It("returns an encoded and signed tx", func() {
env, err := encoder.MakeChannelCreationTransaction("channel-id", fakeSigner, conf)
Expect(err).NotTo(HaveOccurred())
payload := &cb.Payload{}
err = proto.Unmarshal(env.Payload, payload)
Expect(err).NotTo(HaveOccurred())
configUpdateEnv := &cb.ConfigUpdateEnvelope{}
err = proto.Unmarshal(payload.Data, configUpdateEnv)
Expect(err).NotTo(HaveOccurred())
Expect(len(configUpdateEnv.Signatures)).To(Equal(1))
Expect(fakeSigner.SerializeCallCount()).To(Equal(2))
Expect(fakeSigner.SignCallCount()).To(Equal(2))
Expect(fakeSigner.SignArgsForCall(0)).To(Equal(util.ConcatenateBytes(configUpdateEnv.Signatures[0].SignatureHeader, configUpdateEnv.ConfigUpdate)))
})
Context("when a default config cannot be generated", func() {
BeforeEach(func() {
conf.Application = nil
})
It("wraps and returns the error", func() {
_, err := encoder.MakeChannelCreationTransaction("channel-id", fakeSigner, conf)
Expect(err).To(MatchError("could not generate default config template: channel template configs must contain an application section"))
})
})
Context("when the signer cannot create the signature header", func() {
BeforeEach(func() {
fakeSigner.SerializeReturns(nil, fmt.Errorf("serialize-error"))
})
It("wraps and returns the error", func() {
_, err := encoder.MakeChannelCreationTransaction("channel-id", fakeSigner, conf)
Expect(err).To(MatchError("creating signature header failed: serialize-error"))
})
})
Context("when the signer cannot sign", func() {
BeforeEach(func() {
fakeSigner.SignReturns(nil, fmt.Errorf("sign-error"))
})
It("wraps and returns the error", func() {
_, err := encoder.MakeChannelCreationTransaction("channel-id", fakeSigner, conf)
Expect(err).To(MatchError("signature failure over config update: sign-error"))
})
})
Context("when no signer is provided", func() {
It("returns an encoded tx with no signature", func() {
_, err := encoder.MakeChannelCreationTransaction("channel-id", nil, conf)
Expect(err).NotTo(HaveOccurred())
})
})
Context("when the config is bad", func() {
BeforeEach(func() {
conf.Consortium = ""
})
It("wraps and returns the error", func() {
_, err := encoder.MakeChannelCreationTransaction("channel-id", nil, conf)
Expect(err).To(MatchError("config update generation failure: cannot define a new channel with no Consortium value"))
})
})
})
Describe("MakeChannelCreationTransactionWithSystemChannelContext", func() {
var (
applicationConf *genesisconfig.Profile
sysChannelConf *genesisconfig.Profile
)
BeforeEach(func() {
applicationConf = &genesisconfig.Profile{
Consortium: "SampleConsortium",
Policies: CreateStandardPolicies(),
Orderer: &genesisconfig.Orderer{
OrdererType: "solo",
Policies: CreateStandardOrdererPolicies(),
},
Application: &genesisconfig.Application{
Organizations: []*genesisconfig.Organization{
{
MSPDir: "../../../sampleconfig/msp",
ID: "Org1MSP",
MSPType: "bccsp",
Name: "Org1",
AnchorPeers: []*genesisconfig.AnchorPeer{
{
Host: "my-peer",
Port: 5555,
},
},
Policies: CreateStandardPolicies(),
},
{
MSPDir: "../../../sampleconfig/msp",
ID: "Org2MSP",
MSPType: "bccsp",
Name: "Org2",
Policies: CreateStandardPolicies(),
},
},
Policies: CreateStandardPolicies(),
},
}
sysChannelConf = &genesisconfig.Profile{
Policies: CreateStandardPolicies(),
Orderer: &genesisconfig.Orderer{
OrdererType: "solo",
BatchTimeout: time.Hour,
Policies: CreateStandardOrdererPolicies(),
},
Consortiums: map[string]*genesisconfig.Consortium{
"SampleConsortium": {
Organizations: []*genesisconfig.Organization{
{
MSPDir: "../../../sampleconfig/msp",
ID: "Org1MSP",
MSPType: "bccsp",
Name: "Org1",
Policies: CreateStandardPolicies(),
},
{
MSPDir: "../../../sampleconfig/msp",
ID: "Org2MSP",
MSPType: "bccsp",
Name: "Org2",
Policies: CreateStandardPolicies(),
},
},
},
},
}
})
It("returns an encoded and signed tx including differences from the system channel", func() {
env, err := encoder.MakeChannelCreationTransactionWithSystemChannelContext("channel-id", nil, applicationConf, sysChannelConf)
Expect(err).NotTo(HaveOccurred())
payload := &cb.Payload{}
err = proto.Unmarshal(env.Payload, payload)
Expect(err).NotTo(HaveOccurred())
configUpdateEnv := &cb.ConfigUpdateEnvelope{}
err = proto.Unmarshal(payload.Data, configUpdateEnv)
Expect(err).NotTo(HaveOccurred())
configUpdate := &cb.ConfigUpdate{}
err = proto.Unmarshal(configUpdateEnv.ConfigUpdate, configUpdate)
Expect(err).NotTo(HaveOccurred())
Expect(configUpdate.WriteSet.Version).To(Equal(uint64(0)))
Expect(configUpdate.WriteSet.Groups["Application"].Policies["Admins"].Version).To(Equal(uint64(1)))
Expect(configUpdate.WriteSet.Groups["Application"].Groups["Org1"].Version).To(Equal(uint64(1)))
Expect(configUpdate.WriteSet.Groups["Application"].Groups["Org1"].Values["AnchorPeers"]).NotTo(BeNil())
Expect(configUpdate.WriteSet.Groups["Application"].Groups["Org2"].Version).To(Equal(uint64(0)))
Expect(configUpdate.WriteSet.Groups["Orderer"].Values["BatchTimeout"].Version).To(Equal(uint64(1)))
})
Context("when the system channel config is bad", func() {
BeforeEach(func() {
sysChannelConf.Orderer.OrdererType = "garbage"
})
It("wraps and returns the error", func() {
_, err := encoder.MakeChannelCreationTransactionWithSystemChannelContext("channel-id", nil, applicationConf, sysChannelConf)
Expect(err).To(MatchError("could not parse system channel config: could not create orderer group: unknown orderer type: garbage"))
})
})
Context("when the template cannot be computed", func() {
BeforeEach(func() {
applicationConf.Application = nil
})
It("wraps and returns the error", func() {
_, err := encoder.MakeChannelCreationTransactionWithSystemChannelContext("channel-id", nil, applicationConf, sysChannelConf)
Expect(err).To(MatchError("could not create config template: supplied channel creation profile does not contain an application section"))
})
})
})
Describe("DefaultConfigTemplate", func() {
var conf *genesisconfig.Profile
BeforeEach(func() {
conf = &genesisconfig.Profile{
Policies: CreateStandardPolicies(),
Orderer: &genesisconfig.Orderer{
OrdererType: "solo",
Policies: CreateStandardOrdererPolicies(),
},
Application: &genesisconfig.Application{
Policies: CreateStandardPolicies(),
Organizations: []*genesisconfig.Organization{
{
Name: "Org1",
SkipAsForeign: true,
},
{
Name: "Org2",
SkipAsForeign: true,
},
},
},
}
})
It("returns the default config template", func() {
cg, err := encoder.DefaultConfigTemplate(conf)
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Groups)).To(Equal(2))
Expect(cg.Groups["Orderer"]).NotTo(BeNil())
Expect(cg.Groups["Application"]).NotTo(BeNil())
Expect(cg.Groups["Application"].Policies).To(BeEmpty())
Expect(cg.Groups["Application"].Values).To(BeEmpty())
Expect(len(cg.Groups["Application"].Groups)).To(Equal(2))
})
Context("when the config cannot be turned into a channel group", func() {
BeforeEach(func() {
conf.Orderer.OrdererType = "garbage"
})
It("wraps and returns the error", func() {
_, err := encoder.DefaultConfigTemplate(conf)
Expect(err).To(MatchError("error parsing configuration: could not create orderer group: unknown orderer type: garbage"))
})
})
Context("when the application config is nil", func() {
BeforeEach(func() {
conf.Application = nil
})
It("returns an error", func() {
_, err := encoder.DefaultConfigTemplate(conf)
Expect(err).To(MatchError("channel template configs must contain an application section"))
})
})
})
Describe("ConfigTemplateFromGroup", func() {
var (
applicationConf *genesisconfig.Profile
sysChannelGroup *cb.ConfigGroup
)
BeforeEach(func() {
applicationConf = &genesisconfig.Profile{
Policies: CreateStandardPolicies(),
Consortium: "SampleConsortium",
Orderer: &genesisconfig.Orderer{
OrdererType: "solo",
Policies: CreateStandardOrdererPolicies(),
},
Application: &genesisconfig.Application{
Organizations: []*genesisconfig.Organization{
{
Name: "Org1",
SkipAsForeign: true,
},
{
Name: "Org2",
SkipAsForeign: true,
},
},
},
}
var err error
sysChannelGroup, err = encoder.NewChannelGroup(&genesisconfig.Profile{
Policies: CreateStandardPolicies(),
Orderer: &genesisconfig.Orderer{
OrdererType: "solo",
Policies: CreateStandardOrdererPolicies(),
},
Consortiums: map[string]*genesisconfig.Consortium{
"SampleConsortium": {
Organizations: []*genesisconfig.Organization{
{
Name: "Org1",
SkipAsForeign: true,
},
{
Name: "Org2",
SkipAsForeign: true,
},
},
},
},
})
Expect(err).NotTo(HaveOccurred())
})
It("returns a config template", func() {
cg, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Groups)).To(Equal(2))
Expect(cg.Groups["Orderer"]).NotTo(BeNil())
Expect(proto.Equal(cg.Groups["Orderer"], sysChannelGroup.Groups["Orderer"])).To(BeTrue())
Expect(cg.Groups["Application"]).NotTo(BeNil())
Expect(len(cg.Groups["Application"].Policies)).To(Equal(1))
Expect(cg.Groups["Application"].Policies["Admins"]).NotTo(BeNil())
Expect(cg.Groups["Application"].Values).To(BeEmpty())
Expect(len(cg.Groups["Application"].Groups)).To(Equal(2))
})
Context("when the orderer system channel group has no sub-groups", func() {
BeforeEach(func() {
sysChannelGroup.Groups = nil
})
It("returns an error", func() {
_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
Expect(err).To(MatchError("supplied system channel group has no sub-groups"))
})
})
Context("when the orderer system channel group has no consortiums group", func() {
BeforeEach(func() {
delete(sysChannelGroup.Groups, "Consortiums")
})
It("returns an error", func() {
_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
Expect(err).To(MatchError("supplied system channel group does not appear to be system channel (missing consortiums group)"))
})
})
Context("when the orderer system channel group has no consortiums in the consortiums group", func() {
BeforeEach(func() {
sysChannelGroup.Groups["Consortiums"].Groups = nil
})
It("returns an error", func() {
_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
Expect(err).To(MatchError("system channel consortiums group appears to have no consortiums defined"))
})
})
Context("when the orderer system channel group does not have the requested consortium", func() {
BeforeEach(func() {
applicationConf.Consortium = "bad-consortium"
})
It("returns an error", func() {
_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
Expect(err).To(MatchError("supplied system channel group is missing 'bad-consortium' consortium"))
})
})
Context("when the channel creation profile has no application section", func() {
BeforeEach(func() {
applicationConf.Application = nil
})
It("returns an error", func() {
_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
Expect(err).To(MatchError("supplied channel creation profile does not contain an application section"))
})
})
Context("when the orderer system channel group does not have all the channel creation orgs", func() {
BeforeEach(func() {
delete(sysChannelGroup.Groups["Consortiums"].Groups["SampleConsortium"].Groups, "Org1")
})
It("returns an error", func() {
_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
Expect(err).To(MatchError("consortium SampleConsortium does not contain member org Org1"))
})
})
})
Describe("HasSkippedForeignOrgs", func() {
var conf *genesisconfig.Profile
BeforeEach(func() {
conf = &genesisconfig.Profile{
Orderer: &genesisconfig.Orderer{
Organizations: []*genesisconfig.Organization{
{
Name: "OrdererOrg1",
},
{
Name: "OrdererOrg2",
},
},
},
Application: &genesisconfig.Application{
Organizations: []*genesisconfig.Organization{
{
Name: "ApplicationOrg1",
},
{
Name: "ApplicationOrg2",
},
},
},
Consortiums: map[string]*genesisconfig.Consortium{
"SomeConsortium": {
Organizations: []*genesisconfig.Organization{
{
Name: "ConsortiumOrg1",
},
{
Name: "ConsortiumOrg2",
},
},
},
},
}
})
It("returns no error if all orgs are not skipped as foreign", func() {
err := encoder.HasSkippedForeignOrgs(conf)
Expect(err).NotTo(HaveOccurred())
})
Context("when the orderer group has foreign orgs", func() {
BeforeEach(func() {
conf.Orderer.Organizations[1].SkipAsForeign = true
})
It("returns an error indicating the offending org", func() {
err := encoder.HasSkippedForeignOrgs(conf)
Expect(err).To(MatchError("organization 'OrdererOrg2' is marked to be skipped as foreign"))
})
})
Context("when the application group has foreign orgs", func() {
BeforeEach(func() {
conf.Application.Organizations[1].SkipAsForeign = true
})
It("returns an error indicating the offending org", func() {
err := encoder.HasSkippedForeignOrgs(conf)
Expect(err).To(MatchError("organization 'ApplicationOrg2' is marked to be skipped as foreign"))
})
})
Context("when the consortium group has foreign orgs", func() {
BeforeEach(func() {
conf.Consortiums["SomeConsortium"].Organizations[1].SkipAsForeign = true
})
It("returns an error indicating the offending org", func() {
err := encoder.HasSkippedForeignOrgs(conf)
Expect(err).To(MatchError("organization 'ConsortiumOrg2' is marked to be skipped as foreign"))
})
})
})
})
Describe("Bootstrapper", func() {
Describe("NewBootstrapper", func() {
var conf *genesisconfig.Profile
BeforeEach(func() {
conf = &genesisconfig.Profile{
Policies: CreateStandardPolicies(),
Orderer: &genesisconfig.Orderer{
OrdererType: "solo",
Policies: CreateStandardOrdererPolicies(),
},
}
})
It("creates a new bootstrapper for the given config", func() {
bs, err := encoder.NewBootstrapper(conf)
Expect(err).NotTo(HaveOccurred())
Expect(bs).NotTo(BeNil())
})
Context("when the channel config is bad", func() {
BeforeEach(func() {
conf.Orderer.OrdererType = "bad-type"
})
It("wraps and returns the error", func() {
_, err := encoder.NewBootstrapper(conf)
Expect(err).To(MatchError("could not create channel group: could not create orderer group: unknown orderer type: bad-type"))
})
})
Context("when the channel config contains a foreign org", func() {
BeforeEach(func() {
conf.Orderer.Organizations = []*genesisconfig.Organization{
{
Name: "MyOrg",
SkipAsForeign: true,
},
}
})
It("wraps and returns the error", func() {
_, err := encoder.NewBootstrapper(conf)
Expect(err).To(MatchError("all org definitions must be local during bootstrapping: organization 'MyOrg' is marked to be skipped as foreign"))
})
})
})
Describe("New", func() {
var conf *genesisconfig.Profile
BeforeEach(func() {
conf = &genesisconfig.Profile{
Policies: CreateStandardPolicies(),
Orderer: &genesisconfig.Orderer{
OrdererType: "solo",
Policies: CreateStandardOrdererPolicies(),
},
}
})
It("creates a new bootstrapper for the given config", func() {
bs := encoder.New(conf)
Expect(bs).NotTo(BeNil())
})
Context("when the channel config is bad", func() {
BeforeEach(func() {
conf.Orderer.OrdererType = "bad-type"
})
It("panics", func() {
Expect(func() { encoder.New(conf) }).To(Panic())
})
})
})
Describe("Functions", func() {
var bs *encoder.Bootstrapper
BeforeEach(func() {
bs = encoder.New(&genesisconfig.Profile{
Policies: CreateStandardPolicies(),
Orderer: &genesisconfig.Orderer{
Policies: CreateStandardOrdererPolicies(),
OrdererType: "solo",
},
})
})
Describe("GenesisBlock", func() {
It("produces a new genesis block with a default channel ID", func() {
block := bs.GenesisBlock()
Expect(block).NotTo(BeNil())
})
})
Describe("GenesisBlockForChannel", func() {
It("produces a new genesis block with a default channel ID", func() {
block := bs.GenesisBlockForChannel("channel-id")
Expect(block).NotTo(BeNil())
})
})
})
})
})