github.com/sykesm/fabric@v1.1.0-preview.0.20200129034918-2aa12b1a0181/internal/configtxgen/encoder/encoder_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package encoder_test
     8  
     9  import (
    10  	"fmt"
    11  
    12  	. "github.com/onsi/ginkgo"
    13  	. "github.com/onsi/gomega"
    14  
    15  	"github.com/golang/protobuf/proto"
    16  	cb "github.com/hyperledger/fabric-protos-go/common"
    17  	ab "github.com/hyperledger/fabric-protos-go/orderer"
    18  	"github.com/hyperledger/fabric-protos-go/orderer/etcdraft"
    19  	"github.com/hyperledger/fabric/common/util"
    20  	"github.com/hyperledger/fabric/internal/configtxgen/encoder"
    21  	"github.com/hyperledger/fabric/internal/configtxgen/encoder/fakes"
    22  	"github.com/hyperledger/fabric/internal/configtxgen/genesisconfig"
    23  	"github.com/hyperledger/fabric/internal/pkg/identity"
    24  	"github.com/hyperledger/fabric/protoutil"
    25  )
    26  
    27  //go:generate counterfeiter -o fakes/signer_serializer.go --fake-name SignerSerializer . signerSerializer
    28  type signerSerializer interface {
    29  	identity.SignerSerializer
    30  }
    31  
    32  func CreateStandardPolicies() map[string]*genesisconfig.Policy {
    33  	return map[string]*genesisconfig.Policy{
    34  		"Admins": {
    35  			Type: "ImplicitMeta",
    36  			Rule: "ANY Admins",
    37  		},
    38  		"Readers": {
    39  			Type: "ImplicitMeta",
    40  			Rule: "ANY Readers",
    41  		},
    42  		"Writers": {
    43  			Type: "ImplicitMeta",
    44  			Rule: "ANY Writers",
    45  		},
    46  	}
    47  }
    48  
    49  var _ = Describe("Encoder", func() {
    50  	Describe("AddPolicies", func() {
    51  		var (
    52  			cg       *cb.ConfigGroup
    53  			policies map[string]*genesisconfig.Policy
    54  		)
    55  
    56  		BeforeEach(func() {
    57  			cg = protoutil.NewConfigGroup()
    58  			policies = CreateStandardPolicies()
    59  		})
    60  
    61  		It("adds the policies to the group", func() {
    62  			err := encoder.AddPolicies(cg, policies, "Admins")
    63  			Expect(err).NotTo(HaveOccurred())
    64  			Expect(len(cg.Policies)).To(Equal(3))
    65  			Expect(cg.Policies["Admins"]).NotTo(BeNil())
    66  			Expect(cg.Policies["Readers"]).NotTo(BeNil())
    67  			Expect(cg.Policies["Writers"]).NotTo(BeNil())
    68  		})
    69  
    70  		Context("when the policy map is nil", func() {
    71  			BeforeEach(func() {
    72  				policies = nil
    73  			})
    74  
    75  			It("returns an error", func() {
    76  				err := encoder.AddPolicies(cg, policies, "Admins")
    77  				Expect(err).To(MatchError("no policies defined"))
    78  			})
    79  		})
    80  
    81  		Context("when the policy map is missing 'Admins'", func() {
    82  			BeforeEach(func() {
    83  				delete(policies, "Admins")
    84  			})
    85  
    86  			It("returns an error", func() {
    87  				err := encoder.AddPolicies(cg, policies, "Admins")
    88  				Expect(err).To(MatchError("no Admins policy defined"))
    89  			})
    90  		})
    91  
    92  		Context("when the policy map is missing 'Readers'", func() {
    93  			BeforeEach(func() {
    94  				delete(policies, "Readers")
    95  			})
    96  
    97  			It("returns an error", func() {
    98  				err := encoder.AddPolicies(cg, policies, "Readers")
    99  				Expect(err).To(MatchError("no Readers policy defined"))
   100  			})
   101  		})
   102  
   103  		Context("when the policy map is missing 'Writers'", func() {
   104  			BeforeEach(func() {
   105  				delete(policies, "Writers")
   106  			})
   107  
   108  			It("returns an error", func() {
   109  				err := encoder.AddPolicies(cg, policies, "Writers")
   110  				Expect(err).To(MatchError("no Writers policy defined"))
   111  			})
   112  		})
   113  
   114  		Context("when the signature policy definition is bad", func() {
   115  			BeforeEach(func() {
   116  				policies["Readers"].Type = "Signature"
   117  				policies["Readers"].Rule = "garbage"
   118  			})
   119  
   120  			It("wraps and returns the error", func() {
   121  				err := encoder.AddPolicies(cg, policies, "Readers")
   122  				Expect(err).To(MatchError("invalid signature policy rule 'garbage': unrecognized token 'garbage' in policy string"))
   123  			})
   124  		})
   125  
   126  		Context("when the implicit policy definition is bad", func() {
   127  			BeforeEach(func() {
   128  				policies["Readers"].Type = "ImplicitMeta"
   129  				policies["Readers"].Rule = "garbage"
   130  			})
   131  
   132  			It("wraps and returns the error", func() {
   133  				err := encoder.AddPolicies(cg, policies, "Readers")
   134  				Expect(err).To(MatchError("invalid implicit meta policy rule 'garbage': expected two space separated tokens, but got 1"))
   135  			})
   136  		})
   137  
   138  		Context("when the policy type is unknown", func() {
   139  			BeforeEach(func() {
   140  				policies["Readers"].Type = "garbage"
   141  			})
   142  
   143  			It("returns an error", func() {
   144  				err := encoder.AddPolicies(cg, policies, "Readers")
   145  				Expect(err).To(MatchError("unknown policy type: garbage"))
   146  			})
   147  		})
   148  	})
   149  
   150  	Describe("NewChannelGroup", func() {
   151  		var (
   152  			conf *genesisconfig.Profile
   153  		)
   154  
   155  		BeforeEach(func() {
   156  			conf = &genesisconfig.Profile{
   157  				Consortium: "MyConsortium",
   158  				Policies:   CreateStandardPolicies(),
   159  				Application: &genesisconfig.Application{
   160  					Policies: CreateStandardPolicies(),
   161  				},
   162  				Orderer: &genesisconfig.Orderer{
   163  					OrdererType: "solo",
   164  					Policies:    CreateStandardPolicies(),
   165  					Addresses:   []string{"foo.com:7050", "bar.com:8050"},
   166  				},
   167  				Consortiums: map[string]*genesisconfig.Consortium{
   168  					"SampleConsortium": {},
   169  				},
   170  				Capabilities: map[string]bool{
   171  					"FakeCapability": true,
   172  				},
   173  			}
   174  		})
   175  
   176  		It("translates the config into a config group", func() {
   177  			cg, err := encoder.NewChannelGroup(conf)
   178  			Expect(err).NotTo(HaveOccurred())
   179  			Expect(len(cg.Values)).To(Equal(5))
   180  			Expect(cg.Values["BlockDataHashingStructure"]).NotTo(BeNil())
   181  			Expect(cg.Values["Consortium"]).NotTo(BeNil())
   182  			Expect(cg.Values["Capabilities"]).NotTo(BeNil())
   183  			Expect(cg.Values["HashingAlgorithm"]).NotTo(BeNil())
   184  			Expect(cg.Values["OrdererAddresses"]).NotTo(BeNil())
   185  		})
   186  
   187  		Context("when the policy definition is bad", func() {
   188  			BeforeEach(func() {
   189  				conf.Policies["Admins"].Rule = "garbage"
   190  			})
   191  
   192  			It("wraps and returns the error", func() {
   193  				_, err := encoder.NewChannelGroup(conf)
   194  				Expect(err).To(MatchError("error adding policies to channel group: invalid implicit meta policy rule 'garbage': expected two space separated tokens, but got 1"))
   195  			})
   196  		})
   197  
   198  		Context("when the orderer addresses are omitted", func() {
   199  			BeforeEach(func() {
   200  				conf.Orderer.Addresses = []string{}
   201  			})
   202  
   203  			It("does not create the config value", func() {
   204  				cg, err := encoder.NewChannelGroup(conf)
   205  				Expect(err).NotTo(HaveOccurred())
   206  				Expect(cg.Values["OrdererAddresses"]).To(BeNil())
   207  			})
   208  		})
   209  
   210  		Context("when the orderer config is bad", func() {
   211  			BeforeEach(func() {
   212  				conf.Orderer.OrdererType = "bad-type"
   213  			})
   214  
   215  			It("wraps and returns the error", func() {
   216  				_, err := encoder.NewChannelGroup(conf)
   217  				Expect(err).To(MatchError("could not create orderer group: unknown orderer type: bad-type"))
   218  			})
   219  
   220  			Context("when the orderer config is missing", func() {
   221  				BeforeEach(func() {
   222  					conf.Orderer = nil
   223  				})
   224  
   225  				It("handles it gracefully", func() {
   226  					_, err := encoder.NewChannelGroup(conf)
   227  					Expect(err).NotTo(HaveOccurred())
   228  				})
   229  			})
   230  		})
   231  
   232  		Context("when the application config is bad", func() {
   233  			BeforeEach(func() {
   234  				conf.Application.Policies["Admins"] = &genesisconfig.Policy{
   235  					Type: "garbage",
   236  				}
   237  			})
   238  
   239  			It("wraps and returns the error", func() {
   240  				_, err := encoder.NewChannelGroup(conf)
   241  				Expect(err).To(MatchError("could not create application group: error adding policies to application group: unknown policy type: garbage"))
   242  			})
   243  		})
   244  
   245  		Context("when the consortium config is bad", func() {
   246  			BeforeEach(func() {
   247  				conf.Consortiums["SampleConsortium"].Organizations = []*genesisconfig.Organization{
   248  					{
   249  						Policies: map[string]*genesisconfig.Policy{
   250  							"garbage-policy": {
   251  								Type: "garbage",
   252  							},
   253  						},
   254  					},
   255  				}
   256  			})
   257  
   258  			It("wraps and returns the error", func() {
   259  				_, err := encoder.NewChannelGroup(conf)
   260  				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 ''"))
   261  			})
   262  		})
   263  	})
   264  
   265  	Describe("NewOrdererGroup", func() {
   266  		var (
   267  			conf *genesisconfig.Orderer
   268  		)
   269  
   270  		BeforeEach(func() {
   271  			conf = &genesisconfig.Orderer{
   272  				OrdererType: "solo",
   273  				Organizations: []*genesisconfig.Organization{
   274  					{
   275  						MSPDir:   "../../../sampleconfig/msp",
   276  						ID:       "SampleMSP",
   277  						MSPType:  "bccsp",
   278  						Name:     "SampleOrg",
   279  						Policies: CreateStandardPolicies(),
   280  					},
   281  				},
   282  				Policies: CreateStandardPolicies(),
   283  				Capabilities: map[string]bool{
   284  					"FakeCapability": true,
   285  				},
   286  			}
   287  		})
   288  
   289  		It("translates the config into a config group", func() {
   290  			cg, err := encoder.NewOrdererGroup(conf)
   291  			Expect(err).NotTo(HaveOccurred())
   292  			Expect(len(cg.Policies)).To(Equal(4)) // BlockValidation automatically added
   293  			Expect(cg.Policies["Admins"]).NotTo(BeNil())
   294  			Expect(cg.Policies["Readers"]).NotTo(BeNil())
   295  			Expect(cg.Policies["Writers"]).NotTo(BeNil())
   296  			Expect(cg.Policies["BlockValidation"]).NotTo(BeNil())
   297  			Expect(len(cg.Groups)).To(Equal(1))
   298  			Expect(cg.Groups["SampleOrg"]).NotTo(BeNil())
   299  			Expect(len(cg.Values)).To(Equal(5))
   300  			Expect(cg.Values["BatchSize"]).NotTo(BeNil())
   301  			Expect(cg.Values["BatchTimeout"]).NotTo(BeNil())
   302  			Expect(cg.Values["ChannelRestrictions"]).NotTo(BeNil())
   303  			Expect(cg.Values["Capabilities"]).NotTo(BeNil())
   304  		})
   305  
   306  		Context("when the policy definition is bad", func() {
   307  			BeforeEach(func() {
   308  				conf.Policies["Admins"].Rule = "garbage"
   309  			})
   310  
   311  			It("wraps and returns the error", func() {
   312  				_, err := encoder.NewOrdererGroup(conf)
   313  				Expect(err).To(MatchError("error adding policies to orderer group: invalid implicit meta policy rule 'garbage': expected two space separated tokens, but got 1"))
   314  			})
   315  		})
   316  
   317  		Context("when the consensus type is Kafka", func() {
   318  			BeforeEach(func() {
   319  				conf.OrdererType = "kafka"
   320  			})
   321  
   322  			It("adds the kafka brokers key", func() {
   323  				cg, err := encoder.NewOrdererGroup(conf)
   324  				Expect(err).NotTo(HaveOccurred())
   325  				Expect(len(cg.Values)).To(Equal(6))
   326  				Expect(cg.Values["KafkaBrokers"]).NotTo(BeNil())
   327  			})
   328  		})
   329  
   330  		Context("when the consensus type is etcd/raft", func() {
   331  			BeforeEach(func() {
   332  				conf.OrdererType = "etcdraft"
   333  				conf.EtcdRaft = &etcdraft.ConfigMetadata{
   334  					Options: &etcdraft.Options{
   335  						TickInterval: "500ms",
   336  					},
   337  				}
   338  			})
   339  
   340  			It("adds the raft metadata", func() {
   341  				cg, err := encoder.NewOrdererGroup(conf)
   342  				Expect(err).NotTo(HaveOccurred())
   343  				Expect(len(cg.Values)).To(Equal(5))
   344  				consensusType := &ab.ConsensusType{}
   345  				err = proto.Unmarshal(cg.Values["ConsensusType"].Value, consensusType)
   346  				Expect(err).NotTo(HaveOccurred())
   347  				Expect(consensusType.Type).To(Equal("etcdraft"))
   348  				metadata := &etcdraft.ConfigMetadata{}
   349  				err = proto.Unmarshal(consensusType.Metadata, metadata)
   350  				Expect(err).NotTo(HaveOccurred())
   351  				Expect(metadata.Options.TickInterval).To(Equal("500ms"))
   352  			})
   353  
   354  			Context("when the raft configuration is bad", func() {
   355  				BeforeEach(func() {
   356  					conf.EtcdRaft = &etcdraft.ConfigMetadata{
   357  						Consenters: []*etcdraft.Consenter{
   358  							{},
   359  						},
   360  					}
   361  				})
   362  
   363  				It("wraps and returns the error", func() {
   364  					_, err := encoder.NewOrdererGroup(conf)
   365  					Expect(err).To(MatchError("cannot marshal metadata for orderer type etcdraft: cannot load client cert for consenter :0: open : no such file or directory"))
   366  				})
   367  			})
   368  		})
   369  
   370  		Context("when the consensus type is unknown", func() {
   371  			BeforeEach(func() {
   372  				conf.OrdererType = "bad-type"
   373  			})
   374  
   375  			It("returns an error", func() {
   376  				_, err := encoder.NewOrdererGroup(conf)
   377  				Expect(err).To(MatchError("unknown orderer type: bad-type"))
   378  			})
   379  		})
   380  
   381  		Context("when the org definition is bad", func() {
   382  			BeforeEach(func() {
   383  				conf.Organizations[0].MSPType = "garbage"
   384  			})
   385  
   386  			It("wraps and returns the error", func() {
   387  				_, err := encoder.NewOrdererGroup(conf)
   388  				Expect(err).To(MatchError("failed to create orderer org: 1 - Error loading MSP configuration for org: SampleOrg: unknown MSP type 'garbage'"))
   389  			})
   390  		})
   391  	})
   392  
   393  	Describe("NewApplicationGroup", func() {
   394  		var (
   395  			conf *genesisconfig.Application
   396  		)
   397  
   398  		BeforeEach(func() {
   399  			conf = &genesisconfig.Application{
   400  				Organizations: []*genesisconfig.Organization{
   401  					{
   402  						MSPDir:   "../../../sampleconfig/msp",
   403  						ID:       "SampleMSP",
   404  						MSPType:  "bccsp",
   405  						Name:     "SampleOrg",
   406  						Policies: CreateStandardPolicies(),
   407  					},
   408  				},
   409  				ACLs: map[string]string{
   410  					"SomeACL": "SomePolicy",
   411  				},
   412  				Policies: CreateStandardPolicies(),
   413  				Capabilities: map[string]bool{
   414  					"FakeCapability": true,
   415  				},
   416  			}
   417  		})
   418  
   419  		It("translates the config into a config group", func() {
   420  			cg, err := encoder.NewApplicationGroup(conf)
   421  			Expect(err).NotTo(HaveOccurred())
   422  			Expect(len(cg.Policies)).To(Equal(3))
   423  			Expect(cg.Policies["Admins"]).NotTo(BeNil())
   424  			Expect(cg.Policies["Readers"]).NotTo(BeNil())
   425  			Expect(cg.Policies["Writers"]).NotTo(BeNil())
   426  			Expect(len(cg.Groups)).To(Equal(1))
   427  			Expect(cg.Groups["SampleOrg"]).NotTo(BeNil())
   428  			Expect(len(cg.Values)).To(Equal(2))
   429  			Expect(cg.Values["ACLs"]).NotTo(BeNil())
   430  			Expect(cg.Values["Capabilities"]).NotTo(BeNil())
   431  		})
   432  
   433  		Context("when the policy definition is bad", func() {
   434  			BeforeEach(func() {
   435  				conf.Policies["Admins"].Rule = "garbage"
   436  			})
   437  
   438  			It("wraps and returns the error", func() {
   439  				_, err := encoder.NewApplicationGroup(conf)
   440  				Expect(err).To(MatchError("error adding policies to application group: invalid implicit meta policy rule 'garbage': expected two space separated tokens, but got 1"))
   441  			})
   442  		})
   443  
   444  		Context("when the org definition is bad", func() {
   445  			BeforeEach(func() {
   446  				conf.Organizations[0].MSPType = "garbage"
   447  			})
   448  
   449  			It("wraps and returns the error", func() {
   450  				_, err := encoder.NewApplicationGroup(conf)
   451  				Expect(err).To(MatchError("failed to create application org: 1 - Error loading MSP configuration for org SampleOrg: unknown MSP type 'garbage'"))
   452  			})
   453  		})
   454  	})
   455  
   456  	Describe("NewConsortiumOrgGroup", func() {
   457  		var (
   458  			conf *genesisconfig.Organization
   459  		)
   460  
   461  		BeforeEach(func() {
   462  			conf = &genesisconfig.Organization{
   463  				MSPDir:   "../../../sampleconfig/msp",
   464  				ID:       "SampleMSP",
   465  				MSPType:  "bccsp",
   466  				Name:     "SampleOrg",
   467  				Policies: CreateStandardPolicies(),
   468  			}
   469  		})
   470  
   471  		It("translates the config into a config group", func() {
   472  			cg, err := encoder.NewConsortiumOrgGroup(conf)
   473  			Expect(err).NotTo(HaveOccurred())
   474  			Expect(len(cg.Values)).To(Equal(1))
   475  			Expect(cg.Values["MSP"]).NotTo(BeNil())
   476  			Expect(len(cg.Policies)).To(Equal(3))
   477  			Expect(cg.Policies["Admins"]).NotTo(BeNil())
   478  			Expect(cg.Policies["Readers"]).NotTo(BeNil())
   479  			Expect(cg.Policies["Writers"]).NotTo(BeNil())
   480  		})
   481  
   482  		Context("when the org is marked to be skipped as foreign", func() {
   483  			BeforeEach(func() {
   484  				conf.SkipAsForeign = true
   485  			})
   486  
   487  			It("returns an empty org group with mod policy set", func() {
   488  				cg, err := encoder.NewConsortiumOrgGroup(conf)
   489  				Expect(err).NotTo(HaveOccurred())
   490  				Expect(len(cg.Values)).To(Equal(0))
   491  				Expect(len(cg.Policies)).To(Equal(0))
   492  			})
   493  
   494  			Context("even when the MSP dir is invalid/corrupt", func() {
   495  				BeforeEach(func() {
   496  					conf.MSPDir = "garbage"
   497  				})
   498  
   499  				It("returns without error", func() {
   500  					_, err := encoder.NewConsortiumOrgGroup(conf)
   501  					Expect(err).NotTo(HaveOccurred())
   502  				})
   503  			})
   504  		})
   505  
   506  		Context("when dev mode is enabled", func() {
   507  			BeforeEach(func() {
   508  				conf.AdminPrincipal = "Member"
   509  			})
   510  
   511  			It("does not produce an error", func() {
   512  				_, err := encoder.NewConsortiumOrgGroup(conf)
   513  				Expect(err).NotTo(HaveOccurred())
   514  			})
   515  		})
   516  
   517  		Context("when the policy definition is bad", func() {
   518  			BeforeEach(func() {
   519  				conf.Policies["Admins"].Rule = "garbage"
   520  			})
   521  
   522  			It("wraps and returns the error", func() {
   523  				_, err := encoder.NewConsortiumOrgGroup(conf)
   524  				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"))
   525  			})
   526  		})
   527  	})
   528  
   529  	Describe("NewOrdererOrgGroup", func() {
   530  		var (
   531  			conf *genesisconfig.Organization
   532  		)
   533  
   534  		BeforeEach(func() {
   535  			conf = &genesisconfig.Organization{
   536  				MSPDir:   "../../../sampleconfig/msp",
   537  				ID:       "SampleMSP",
   538  				MSPType:  "bccsp",
   539  				Name:     "SampleOrg",
   540  				Policies: CreateStandardPolicies(),
   541  				OrdererEndpoints: []string{
   542  					"foo:7050",
   543  					"bar:8050",
   544  				},
   545  			}
   546  		})
   547  
   548  		It("translates the config into a config group", func() {
   549  			cg, err := encoder.NewOrdererOrgGroup(conf)
   550  			Expect(err).NotTo(HaveOccurred())
   551  			Expect(len(cg.Values)).To(Equal(2))
   552  			Expect(cg.Values["MSP"]).NotTo(BeNil())
   553  			Expect(len(cg.Policies)).To(Equal(3))
   554  			Expect(cg.Values["Endpoints"]).NotTo(BeNil())
   555  			Expect(cg.Policies["Admins"]).NotTo(BeNil())
   556  			Expect(cg.Policies["Readers"]).NotTo(BeNil())
   557  			Expect(cg.Policies["Writers"]).NotTo(BeNil())
   558  		})
   559  
   560  		Context("when the org is marked to be skipped as foreign", func() {
   561  			BeforeEach(func() {
   562  				conf.SkipAsForeign = true
   563  			})
   564  
   565  			It("returns an empty org group with mod policy set", func() {
   566  				cg, err := encoder.NewOrdererOrgGroup(conf)
   567  				Expect(err).NotTo(HaveOccurred())
   568  				Expect(len(cg.Values)).To(Equal(0))
   569  				Expect(len(cg.Policies)).To(Equal(0))
   570  			})
   571  
   572  			Context("even when the MSP dir is invalid/corrupt", func() {
   573  				BeforeEach(func() {
   574  					conf.MSPDir = "garbage"
   575  				})
   576  
   577  				It("returns without error", func() {
   578  					_, err := encoder.NewOrdererOrgGroup(conf)
   579  					Expect(err).NotTo(HaveOccurred())
   580  				})
   581  			})
   582  		})
   583  
   584  		Context("when there are no ordering endpoints", func() {
   585  			BeforeEach(func() {
   586  				conf.OrdererEndpoints = []string{}
   587  			})
   588  
   589  			It("does not include the endpoints in the config group", func() {
   590  				cg, err := encoder.NewOrdererOrgGroup(conf)
   591  				Expect(err).NotTo(HaveOccurred())
   592  				Expect(cg.Values["Endpoints"]).To(BeNil())
   593  			})
   594  		})
   595  
   596  		Context("when dev mode is enabled", func() {
   597  			BeforeEach(func() {
   598  				conf.AdminPrincipal = "Member"
   599  			})
   600  
   601  			It("does not produce an error", func() {
   602  				_, err := encoder.NewOrdererOrgGroup(conf)
   603  				Expect(err).NotTo(HaveOccurred())
   604  			})
   605  		})
   606  
   607  		Context("when the policy definition is bad", func() {
   608  			BeforeEach(func() {
   609  				conf.Policies["Admins"].Rule = "garbage"
   610  			})
   611  
   612  			It("wraps and returns the error", func() {
   613  				_, err := encoder.NewOrdererOrgGroup(conf)
   614  				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"))
   615  			})
   616  		})
   617  	})
   618  
   619  	Describe("NewApplicationOrgGroup", func() {
   620  		var (
   621  			conf *genesisconfig.Organization
   622  		)
   623  
   624  		BeforeEach(func() {
   625  			conf = &genesisconfig.Organization{
   626  				MSPDir:   "../../../sampleconfig/msp",
   627  				ID:       "SampleMSP",
   628  				MSPType:  "bccsp",
   629  				Name:     "SampleOrg",
   630  				Policies: CreateStandardPolicies(),
   631  				AnchorPeers: []*genesisconfig.AnchorPeer{
   632  					{
   633  						Host: "hostname",
   634  						Port: 5555,
   635  					},
   636  				},
   637  			}
   638  		})
   639  
   640  		It("translates the config into a config group", func() {
   641  			cg, err := encoder.NewApplicationOrgGroup(conf)
   642  			Expect(err).NotTo(HaveOccurred())
   643  			Expect(len(cg.Values)).To(Equal(2))
   644  			Expect(cg.Values["MSP"]).NotTo(BeNil())
   645  			Expect(cg.Values["AnchorPeers"]).NotTo(BeNil())
   646  			Expect(len(cg.Policies)).To(Equal(3))
   647  			Expect(cg.Policies["Admins"]).NotTo(BeNil())
   648  			Expect(cg.Policies["Readers"]).NotTo(BeNil())
   649  			Expect(cg.Policies["Writers"]).NotTo(BeNil())
   650  			Expect(len(cg.Values)).To(Equal(2))
   651  			Expect(cg.Values["MSP"]).NotTo(BeNil())
   652  			Expect(cg.Values["AnchorPeers"]).NotTo(BeNil())
   653  		})
   654  
   655  		Context("when the org is marked to be skipped as foreign", func() {
   656  			BeforeEach(func() {
   657  				conf.SkipAsForeign = true
   658  			})
   659  
   660  			It("returns an empty org group with mod policy set", func() {
   661  				cg, err := encoder.NewApplicationOrgGroup(conf)
   662  				Expect(err).NotTo(HaveOccurred())
   663  				Expect(len(cg.Values)).To(Equal(0))
   664  				Expect(len(cg.Policies)).To(Equal(0))
   665  			})
   666  
   667  			Context("even when the MSP dir is invalid/corrupt", func() {
   668  				BeforeEach(func() {
   669  					conf.MSPDir = "garbage"
   670  				})
   671  
   672  				It("returns without error", func() {
   673  					_, err := encoder.NewApplicationOrgGroup(conf)
   674  					Expect(err).NotTo(HaveOccurred())
   675  				})
   676  			})
   677  		})
   678  
   679  		Context("when the policy definition is bad", func() {
   680  			BeforeEach(func() {
   681  				conf.Policies["Admins"].Type = "garbage"
   682  			})
   683  
   684  			It("wraps and returns the error", func() {
   685  				_, err := encoder.NewApplicationOrgGroup(conf)
   686  				Expect(err).To(MatchError("error adding policies to application org group SampleOrg: unknown policy type: garbage"))
   687  			})
   688  		})
   689  
   690  		Context("when the MSP definition is bad", func() {
   691  			BeforeEach(func() {
   692  				conf.MSPDir = "garbage"
   693  			})
   694  
   695  			It("wraps and returns the error", func() {
   696  				_, err := encoder.NewApplicationOrgGroup(conf)
   697  				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"))
   698  			})
   699  		})
   700  
   701  		Context("when there are no anchor peers defined", func() {
   702  			BeforeEach(func() {
   703  				conf.AnchorPeers = nil
   704  			})
   705  
   706  			It("does not encode the anchor peers", func() {
   707  				cg, err := encoder.NewApplicationOrgGroup(conf)
   708  				Expect(err).NotTo(HaveOccurred())
   709  				Expect(len(cg.Values)).To(Equal(1))
   710  				Expect(cg.Values["AnchorPeers"]).To(BeNil())
   711  			})
   712  		})
   713  	})
   714  
   715  	Describe("ChannelCreationOperations", func() {
   716  		var (
   717  			conf     *genesisconfig.Profile
   718  			template *cb.ConfigGroup
   719  		)
   720  
   721  		BeforeEach(func() {
   722  			conf = &genesisconfig.Profile{
   723  				Consortium: "MyConsortium",
   724  				Policies:   CreateStandardPolicies(),
   725  				Application: &genesisconfig.Application{
   726  					Organizations: []*genesisconfig.Organization{
   727  						{
   728  							Name:     "SampleOrg",
   729  							MSPDir:   "../../../sampleconfig/msp",
   730  							ID:       "SampleMSP",
   731  							MSPType:  "bccsp",
   732  							Policies: CreateStandardPolicies(),
   733  							AnchorPeers: []*genesisconfig.AnchorPeer{
   734  								{
   735  									Host: "hostname",
   736  									Port: 4444,
   737  								},
   738  							},
   739  						},
   740  					},
   741  					Policies: CreateStandardPolicies(),
   742  				},
   743  			}
   744  
   745  			var err error
   746  			template, err = encoder.DefaultConfigTemplate(conf)
   747  			Expect(err).NotTo(HaveOccurred())
   748  		})
   749  
   750  		Describe("NewChannelCreateConfigUpdate", func() {
   751  			It("translates the config into a config group", func() {
   752  				cg, err := encoder.NewChannelCreateConfigUpdate("channel-id", conf, template)
   753  				Expect(err).NotTo(HaveOccurred())
   754  				expected := &cb.ConfigUpdate{
   755  					ChannelId: "channel-id",
   756  					ReadSet: &cb.ConfigGroup{
   757  						Groups: map[string]*cb.ConfigGroup{
   758  							"Application": {
   759  								Groups: map[string]*cb.ConfigGroup{
   760  									"SampleOrg": {},
   761  								},
   762  							},
   763  						},
   764  						Values: map[string]*cb.ConfigValue{
   765  							"Consortium": {},
   766  						},
   767  					},
   768  					WriteSet: &cb.ConfigGroup{
   769  						Groups: map[string]*cb.ConfigGroup{
   770  							"Application": {
   771  								Version:   1,
   772  								ModPolicy: "Admins",
   773  								Groups: map[string]*cb.ConfigGroup{
   774  									"SampleOrg": {},
   775  								},
   776  								Policies: map[string]*cb.ConfigPolicy{
   777  									"Admins": {
   778  										Policy: &cb.Policy{
   779  											Type: int32(cb.Policy_IMPLICIT_META),
   780  											Value: protoutil.MarshalOrPanic(&cb.ImplicitMetaPolicy{
   781  												SubPolicy: "Admins",
   782  												Rule:      cb.ImplicitMetaPolicy_ANY,
   783  											}),
   784  										},
   785  										ModPolicy: "Admins",
   786  									},
   787  									"Readers": {
   788  										Policy: &cb.Policy{
   789  											Type: int32(cb.Policy_IMPLICIT_META),
   790  											Value: protoutil.MarshalOrPanic(&cb.ImplicitMetaPolicy{
   791  												SubPolicy: "Readers",
   792  												Rule:      cb.ImplicitMetaPolicy_ANY,
   793  											}),
   794  										},
   795  										ModPolicy: "Admins",
   796  									},
   797  									"Writers": {
   798  										Policy: &cb.Policy{
   799  											Type: int32(cb.Policy_IMPLICIT_META),
   800  											Value: protoutil.MarshalOrPanic(&cb.ImplicitMetaPolicy{
   801  												SubPolicy: "Writers",
   802  												Rule:      cb.ImplicitMetaPolicy_ANY,
   803  											}),
   804  										},
   805  										ModPolicy: "Admins",
   806  									},
   807  								},
   808  							},
   809  						},
   810  						Values: map[string]*cb.ConfigValue{
   811  							"Consortium": {
   812  								Value: protoutil.MarshalOrPanic(&cb.Consortium{
   813  									Name: "MyConsortium",
   814  								}),
   815  							},
   816  						},
   817  					},
   818  				}
   819  				Expect(proto.Equal(expected, cg)).To(BeTrue())
   820  			})
   821  
   822  			Context("when the template configuration is not the default", func() {
   823  				BeforeEach(func() {
   824  					differentConf := &genesisconfig.Profile{
   825  						Consortium: "MyConsortium",
   826  						Policies:   CreateStandardPolicies(),
   827  						Application: &genesisconfig.Application{
   828  							Organizations: []*genesisconfig.Organization{
   829  								{
   830  									MSPDir:  "../../../sampleconfig/msp",
   831  									ID:      "SampleMSP",
   832  									MSPType: "bccsp",
   833  									Name:    "SampleOrg",
   834  									AnchorPeers: []*genesisconfig.AnchorPeer{
   835  										{
   836  											Host: "hostname",
   837  											Port: 5555,
   838  										},
   839  									},
   840  									Policies: CreateStandardPolicies(),
   841  								},
   842  							},
   843  							Policies: CreateStandardPolicies(),
   844  						},
   845  					}
   846  
   847  					var err error
   848  					template, err = encoder.DefaultConfigTemplate(differentConf)
   849  					Expect(err).NotTo(HaveOccurred())
   850  				})
   851  
   852  				It("reflects the additional modifications designated by the channel creation profile", func() {
   853  					cg, err := encoder.NewChannelCreateConfigUpdate("channel-id", conf, template)
   854  					Expect(err).NotTo(HaveOccurred())
   855  					Expect(cg.WriteSet.Groups["Application"].Groups["SampleOrg"].Values["AnchorPeers"].Version).To(Equal(uint64(1)))
   856  				})
   857  			})
   858  
   859  			Context("when the application config is bad", func() {
   860  				BeforeEach(func() {
   861  					conf.Application.Policies["Admins"].Type = "bad-type"
   862  				})
   863  
   864  				It("returns an error", func() {
   865  					_, err := encoder.NewChannelCreateConfigUpdate("channel-id", conf, template)
   866  					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"))
   867  				})
   868  
   869  				Context("when the application config is missing", func() {
   870  					BeforeEach(func() {
   871  						conf.Application = nil
   872  					})
   873  
   874  					It("returns an error", func() {
   875  						_, err := encoder.NewChannelCreateConfigUpdate("channel-id", conf, template)
   876  						Expect(err).To(MatchError("cannot define a new channel with no Application section"))
   877  					})
   878  				})
   879  			})
   880  
   881  			Context("when the consortium is empty", func() {
   882  				BeforeEach(func() {
   883  					conf.Consortium = ""
   884  				})
   885  
   886  				It("returns an error", func() {
   887  					_, err := encoder.NewChannelCreateConfigUpdate("channel-id", conf, template)
   888  					Expect(err).To(MatchError("cannot define a new channel with no Consortium value"))
   889  				})
   890  			})
   891  
   892  			Context("when an update cannot be computed", func() {
   893  				It("returns an error", func() {
   894  					_, err := encoder.NewChannelCreateConfigUpdate("channel-id", conf, nil)
   895  					Expect(err).To(MatchError("could not compute update: no channel group included for original config"))
   896  				})
   897  			})
   898  		})
   899  
   900  		Describe("MakeChannelCreationTransaction", func() {
   901  			var (
   902  				fakeSigner *fakes.SignerSerializer
   903  			)
   904  
   905  			BeforeEach(func() {
   906  				fakeSigner = &fakes.SignerSerializer{}
   907  				fakeSigner.SerializeReturns([]byte("fake-creator"), nil)
   908  			})
   909  
   910  			It("returns an encoded and signed tx", func() {
   911  				env, err := encoder.MakeChannelCreationTransaction("channel-id", fakeSigner, conf)
   912  				Expect(err).NotTo(HaveOccurred())
   913  				payload := &cb.Payload{}
   914  				err = proto.Unmarshal(env.Payload, payload)
   915  				Expect(err).NotTo(HaveOccurred())
   916  				configUpdateEnv := &cb.ConfigUpdateEnvelope{}
   917  				err = proto.Unmarshal(payload.Data, configUpdateEnv)
   918  				Expect(err).NotTo(HaveOccurred())
   919  				Expect(len(configUpdateEnv.Signatures)).To(Equal(1))
   920  				Expect(fakeSigner.SerializeCallCount()).To(Equal(2))
   921  				Expect(fakeSigner.SignCallCount()).To(Equal(2))
   922  				Expect(fakeSigner.SignArgsForCall(0)).To(Equal(util.ConcatenateBytes(configUpdateEnv.Signatures[0].SignatureHeader, configUpdateEnv.ConfigUpdate)))
   923  			})
   924  
   925  			Context("when a default config cannot be generated", func() {
   926  				BeforeEach(func() {
   927  					conf.Application = nil
   928  				})
   929  
   930  				It("wraps and returns the error", func() {
   931  					_, err := encoder.MakeChannelCreationTransaction("channel-id", fakeSigner, conf)
   932  					Expect(err).To(MatchError("could not generate default config template: channel template configs must contain an application section"))
   933  				})
   934  			})
   935  
   936  			Context("when the signer cannot create the signature header", func() {
   937  				BeforeEach(func() {
   938  					fakeSigner.SerializeReturns(nil, fmt.Errorf("serialize-error"))
   939  				})
   940  
   941  				It("wraps and returns the error", func() {
   942  					_, err := encoder.MakeChannelCreationTransaction("channel-id", fakeSigner, conf)
   943  					Expect(err).To(MatchError("creating signature header failed: serialize-error"))
   944  				})
   945  			})
   946  
   947  			Context("when the signer cannot sign", func() {
   948  				BeforeEach(func() {
   949  					fakeSigner.SignReturns(nil, fmt.Errorf("sign-error"))
   950  				})
   951  
   952  				It("wraps and returns the error", func() {
   953  					_, err := encoder.MakeChannelCreationTransaction("channel-id", fakeSigner, conf)
   954  					Expect(err).To(MatchError("signature failure over config update: sign-error"))
   955  				})
   956  			})
   957  
   958  			Context("when no signer is provided", func() {
   959  				It("returns an encoded tx with no signature", func() {
   960  					_, err := encoder.MakeChannelCreationTransaction("channel-id", nil, conf)
   961  					Expect(err).NotTo(HaveOccurred())
   962  				})
   963  			})
   964  
   965  			Context("when the config is bad", func() {
   966  				BeforeEach(func() {
   967  					conf.Consortium = ""
   968  				})
   969  
   970  				It("wraps and returns the error", func() {
   971  					_, err := encoder.MakeChannelCreationTransaction("channel-id", nil, conf)
   972  					Expect(err).To(MatchError("config update generation failure: cannot define a new channel with no Consortium value"))
   973  				})
   974  			})
   975  		})
   976  
   977  		Describe("MakeChannelCreationTransactionWithSystemChannelContext", func() {
   978  			var (
   979  				applicationConf *genesisconfig.Profile
   980  				sysChannelConf  *genesisconfig.Profile
   981  			)
   982  
   983  			BeforeEach(func() {
   984  				applicationConf = &genesisconfig.Profile{
   985  					Consortium: "SampleConsortium",
   986  					Policies:   CreateStandardPolicies(),
   987  					Orderer: &genesisconfig.Orderer{
   988  						OrdererType: "solo",
   989  						Policies:    CreateStandardPolicies(),
   990  					},
   991  					Application: &genesisconfig.Application{
   992  						Organizations: []*genesisconfig.Organization{
   993  							{
   994  								MSPDir:  "../../../sampleconfig/msp",
   995  								ID:      "Org1MSP",
   996  								MSPType: "bccsp",
   997  								Name:    "Org1",
   998  								AnchorPeers: []*genesisconfig.AnchorPeer{
   999  									{
  1000  										Host: "my-peer",
  1001  										Port: 5555,
  1002  									},
  1003  								},
  1004  								Policies: CreateStandardPolicies(),
  1005  							},
  1006  							{
  1007  								MSPDir:   "../../../sampleconfig/msp",
  1008  								ID:       "Org2MSP",
  1009  								MSPType:  "bccsp",
  1010  								Name:     "Org2",
  1011  								Policies: CreateStandardPolicies(),
  1012  							},
  1013  						},
  1014  						Policies: CreateStandardPolicies(),
  1015  					},
  1016  				}
  1017  
  1018  				sysChannelConf = &genesisconfig.Profile{
  1019  					Policies: CreateStandardPolicies(),
  1020  					Orderer: &genesisconfig.Orderer{
  1021  						OrdererType: "kafka",
  1022  						Policies:    CreateStandardPolicies(),
  1023  					},
  1024  					Consortiums: map[string]*genesisconfig.Consortium{
  1025  						"SampleConsortium": {
  1026  							Organizations: []*genesisconfig.Organization{
  1027  								{
  1028  									MSPDir:   "../../../sampleconfig/msp",
  1029  									ID:       "Org1MSP",
  1030  									MSPType:  "bccsp",
  1031  									Name:     "Org1",
  1032  									Policies: CreateStandardPolicies(),
  1033  								},
  1034  								{
  1035  									MSPDir:   "../../../sampleconfig/msp",
  1036  									ID:       "Org2MSP",
  1037  									MSPType:  "bccsp",
  1038  									Name:     "Org2",
  1039  									Policies: CreateStandardPolicies(),
  1040  								},
  1041  							},
  1042  						},
  1043  					},
  1044  				}
  1045  			})
  1046  
  1047  			It("returns an encoded and signed tx including differences from the system channel", func() {
  1048  				env, err := encoder.MakeChannelCreationTransactionWithSystemChannelContext("channel-id", nil, applicationConf, sysChannelConf)
  1049  				Expect(err).NotTo(HaveOccurred())
  1050  				payload := &cb.Payload{}
  1051  				err = proto.Unmarshal(env.Payload, payload)
  1052  				Expect(err).NotTo(HaveOccurred())
  1053  				configUpdateEnv := &cb.ConfigUpdateEnvelope{}
  1054  				err = proto.Unmarshal(payload.Data, configUpdateEnv)
  1055  				Expect(err).NotTo(HaveOccurred())
  1056  				configUpdate := &cb.ConfigUpdate{}
  1057  				err = proto.Unmarshal(configUpdateEnv.ConfigUpdate, configUpdate)
  1058  				Expect(err).NotTo(HaveOccurred())
  1059  				Expect(configUpdate.WriteSet.Version).To(Equal(uint64(0)))
  1060  				Expect(configUpdate.WriteSet.Groups["Application"].Policies["Admins"].Version).To(Equal(uint64(1)))
  1061  				Expect(configUpdate.WriteSet.Groups["Application"].Groups["Org1"].Version).To(Equal(uint64(1)))
  1062  				Expect(configUpdate.WriteSet.Groups["Application"].Groups["Org1"].Values["AnchorPeers"]).NotTo(BeNil())
  1063  				Expect(configUpdate.WriteSet.Groups["Application"].Groups["Org2"].Version).To(Equal(uint64(0)))
  1064  				Expect(configUpdate.WriteSet.Groups["Orderer"].Values["ConsensusType"].Version).To(Equal(uint64(1)))
  1065  			})
  1066  
  1067  			Context("when the system channel config is bad", func() {
  1068  				BeforeEach(func() {
  1069  					sysChannelConf.Orderer.OrdererType = "garbage"
  1070  				})
  1071  
  1072  				It("wraps and returns the error", func() {
  1073  					_, err := encoder.MakeChannelCreationTransactionWithSystemChannelContext("channel-id", nil, applicationConf, sysChannelConf)
  1074  					Expect(err).To(MatchError("could not parse system channel config: could not create orderer group: unknown orderer type: garbage"))
  1075  				})
  1076  			})
  1077  
  1078  			Context("when the template cannot be computed", func() {
  1079  				BeforeEach(func() {
  1080  					applicationConf.Application = nil
  1081  				})
  1082  
  1083  				It("wraps and returns the error", func() {
  1084  					_, err := encoder.MakeChannelCreationTransactionWithSystemChannelContext("channel-id", nil, applicationConf, sysChannelConf)
  1085  					Expect(err).To(MatchError("could not create config template: supplied channel creation profile does not contain an application section"))
  1086  				})
  1087  			})
  1088  		})
  1089  
  1090  		Describe("DefaultConfigTemplate", func() {
  1091  			var (
  1092  				conf *genesisconfig.Profile
  1093  			)
  1094  
  1095  			BeforeEach(func() {
  1096  				conf = &genesisconfig.Profile{
  1097  					Policies: CreateStandardPolicies(),
  1098  					Orderer: &genesisconfig.Orderer{
  1099  						OrdererType: "solo",
  1100  						Policies:    CreateStandardPolicies(),
  1101  					},
  1102  					Application: &genesisconfig.Application{
  1103  						Policies: CreateStandardPolicies(),
  1104  						Organizations: []*genesisconfig.Organization{
  1105  							{
  1106  								Name:          "Org1",
  1107  								SkipAsForeign: true,
  1108  							},
  1109  							{
  1110  								Name:          "Org2",
  1111  								SkipAsForeign: true,
  1112  							},
  1113  						},
  1114  					},
  1115  				}
  1116  			})
  1117  
  1118  			It("returns the default config template", func() {
  1119  				cg, err := encoder.DefaultConfigTemplate(conf)
  1120  				Expect(err).NotTo(HaveOccurred())
  1121  				Expect(len(cg.Groups)).To(Equal(2))
  1122  				Expect(cg.Groups["Orderer"]).NotTo(BeNil())
  1123  				Expect(cg.Groups["Application"]).NotTo(BeNil())
  1124  				Expect(cg.Groups["Application"].Policies).To(BeEmpty())
  1125  				Expect(cg.Groups["Application"].Values).To(BeEmpty())
  1126  				Expect(len(cg.Groups["Application"].Groups)).To(Equal(2))
  1127  			})
  1128  
  1129  			Context("when the config cannot be turned into a channel group", func() {
  1130  				BeforeEach(func() {
  1131  					conf.Orderer.OrdererType = "garbage"
  1132  				})
  1133  
  1134  				It("wraps and returns the error", func() {
  1135  					_, err := encoder.DefaultConfigTemplate(conf)
  1136  					Expect(err).To(MatchError("error parsing configuration: could not create orderer group: unknown orderer type: garbage"))
  1137  				})
  1138  			})
  1139  
  1140  			Context("when the application config is nil", func() {
  1141  				BeforeEach(func() {
  1142  					conf.Application = nil
  1143  				})
  1144  
  1145  				It("returns an error", func() {
  1146  					_, err := encoder.DefaultConfigTemplate(conf)
  1147  					Expect(err).To(MatchError("channel template configs must contain an application section"))
  1148  				})
  1149  			})
  1150  		})
  1151  
  1152  		Describe("ConfigTemplateFromGroup", func() {
  1153  			var (
  1154  				applicationConf *genesisconfig.Profile
  1155  				sysChannelGroup *cb.ConfigGroup
  1156  			)
  1157  
  1158  			BeforeEach(func() {
  1159  				applicationConf = &genesisconfig.Profile{
  1160  					Policies:   CreateStandardPolicies(),
  1161  					Consortium: "SampleConsortium",
  1162  					Orderer: &genesisconfig.Orderer{
  1163  						OrdererType: "solo",
  1164  						Policies:    CreateStandardPolicies(),
  1165  					},
  1166  					Application: &genesisconfig.Application{
  1167  						Organizations: []*genesisconfig.Organization{
  1168  							{
  1169  								Name:          "Org1",
  1170  								SkipAsForeign: true,
  1171  							},
  1172  							{
  1173  								Name:          "Org2",
  1174  								SkipAsForeign: true,
  1175  							},
  1176  						},
  1177  					},
  1178  				}
  1179  
  1180  				var err error
  1181  				sysChannelGroup, err = encoder.NewChannelGroup(&genesisconfig.Profile{
  1182  					Policies: CreateStandardPolicies(),
  1183  					Orderer: &genesisconfig.Orderer{
  1184  						OrdererType: "kafka",
  1185  						Policies:    CreateStandardPolicies(),
  1186  					},
  1187  					Consortiums: map[string]*genesisconfig.Consortium{
  1188  						"SampleConsortium": {
  1189  							Organizations: []*genesisconfig.Organization{
  1190  								{
  1191  									Name:          "Org1",
  1192  									SkipAsForeign: true,
  1193  								},
  1194  								{
  1195  									Name:          "Org2",
  1196  									SkipAsForeign: true,
  1197  								},
  1198  							},
  1199  						},
  1200  					},
  1201  				})
  1202  				Expect(err).NotTo(HaveOccurred())
  1203  			})
  1204  
  1205  			It("returns a config template", func() {
  1206  				cg, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
  1207  				Expect(err).NotTo(HaveOccurred())
  1208  				Expect(len(cg.Groups)).To(Equal(2))
  1209  				Expect(cg.Groups["Orderer"]).NotTo(BeNil())
  1210  				Expect(proto.Equal(cg.Groups["Orderer"], sysChannelGroup.Groups["Orderer"])).To(BeTrue())
  1211  				Expect(cg.Groups["Application"]).NotTo(BeNil())
  1212  				Expect(len(cg.Groups["Application"].Policies)).To(Equal(1))
  1213  				Expect(cg.Groups["Application"].Policies["Admins"]).NotTo(BeNil())
  1214  				Expect(cg.Groups["Application"].Values).To(BeEmpty())
  1215  				Expect(len(cg.Groups["Application"].Groups)).To(Equal(2))
  1216  			})
  1217  
  1218  			Context("when the orderer system channel group has no sub-groups", func() {
  1219  				BeforeEach(func() {
  1220  					sysChannelGroup.Groups = nil
  1221  				})
  1222  
  1223  				It("returns an error", func() {
  1224  					_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
  1225  					Expect(err).To(MatchError("supplied system channel group has no sub-groups"))
  1226  				})
  1227  			})
  1228  
  1229  			Context("when the orderer system channel group has no consortiums group", func() {
  1230  				BeforeEach(func() {
  1231  					delete(sysChannelGroup.Groups, "Consortiums")
  1232  				})
  1233  
  1234  				It("returns an error", func() {
  1235  					_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
  1236  					Expect(err).To(MatchError("supplied system channel group does not appear to be system channel (missing consortiums group)"))
  1237  				})
  1238  			})
  1239  
  1240  			Context("when the orderer system channel group has no consortiums in the consortiums group", func() {
  1241  				BeforeEach(func() {
  1242  					sysChannelGroup.Groups["Consortiums"].Groups = nil
  1243  				})
  1244  
  1245  				It("returns an error", func() {
  1246  					_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
  1247  					Expect(err).To(MatchError("system channel consortiums group appears to have no consortiums defined"))
  1248  				})
  1249  			})
  1250  
  1251  			Context("when the orderer system channel group does not have the requested consortium", func() {
  1252  				BeforeEach(func() {
  1253  					applicationConf.Consortium = "bad-consortium"
  1254  				})
  1255  
  1256  				It("returns an error", func() {
  1257  					_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
  1258  					Expect(err).To(MatchError("supplied system channel group is missing 'bad-consortium' consortium"))
  1259  				})
  1260  			})
  1261  
  1262  			Context("when the channel creation profile has no application section", func() {
  1263  				BeforeEach(func() {
  1264  					applicationConf.Application = nil
  1265  				})
  1266  
  1267  				It("returns an error", func() {
  1268  					_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
  1269  					Expect(err).To(MatchError("supplied channel creation profile does not contain an application section"))
  1270  				})
  1271  			})
  1272  
  1273  			Context("when the orderer system channel group does not have all the channel creation orgs", func() {
  1274  				BeforeEach(func() {
  1275  					delete(sysChannelGroup.Groups["Consortiums"].Groups["SampleConsortium"].Groups, "Org1")
  1276  				})
  1277  
  1278  				It("returns an error", func() {
  1279  					_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
  1280  					Expect(err).To(MatchError("consortium SampleConsortium does not contain member org Org1"))
  1281  				})
  1282  			})
  1283  
  1284  		})
  1285  
  1286  		Describe("HasSkippedForeignOrgs", func() {
  1287  			var (
  1288  				conf *genesisconfig.Profile
  1289  			)
  1290  
  1291  			BeforeEach(func() {
  1292  				conf = &genesisconfig.Profile{
  1293  					Orderer: &genesisconfig.Orderer{
  1294  						Organizations: []*genesisconfig.Organization{
  1295  							{
  1296  								Name: "OrdererOrg1",
  1297  							},
  1298  							{
  1299  								Name: "OrdererOrg2",
  1300  							},
  1301  						},
  1302  					},
  1303  					Application: &genesisconfig.Application{
  1304  						Organizations: []*genesisconfig.Organization{
  1305  							{
  1306  								Name: "ApplicationOrg1",
  1307  							},
  1308  							{
  1309  								Name: "ApplicationOrg2",
  1310  							},
  1311  						},
  1312  					},
  1313  					Consortiums: map[string]*genesisconfig.Consortium{
  1314  						"SomeConsortium": {
  1315  							Organizations: []*genesisconfig.Organization{
  1316  								{
  1317  									Name: "ConsortiumOrg1",
  1318  								},
  1319  								{
  1320  									Name: "ConsortiumOrg2",
  1321  								},
  1322  							},
  1323  						},
  1324  					},
  1325  				}
  1326  			})
  1327  
  1328  			It("returns no error if all orgs are not skipped as foreign", func() {
  1329  				err := encoder.HasSkippedForeignOrgs(conf)
  1330  				Expect(err).NotTo(HaveOccurred())
  1331  			})
  1332  
  1333  			Context("when the orderer group has foreign orgs", func() {
  1334  				BeforeEach(func() {
  1335  					conf.Orderer.Organizations[1].SkipAsForeign = true
  1336  				})
  1337  
  1338  				It("returns an error indicating the offending org", func() {
  1339  					err := encoder.HasSkippedForeignOrgs(conf)
  1340  					Expect(err).To(MatchError("organization 'OrdererOrg2' is marked to be skipped as foreign"))
  1341  				})
  1342  			})
  1343  
  1344  			Context("when the application group has foreign orgs", func() {
  1345  				BeforeEach(func() {
  1346  					conf.Application.Organizations[1].SkipAsForeign = true
  1347  				})
  1348  
  1349  				It("returns an error indicating the offending org", func() {
  1350  					err := encoder.HasSkippedForeignOrgs(conf)
  1351  					Expect(err).To(MatchError("organization 'ApplicationOrg2' is marked to be skipped as foreign"))
  1352  				})
  1353  			})
  1354  
  1355  			Context("when the consortium group has foreign orgs", func() {
  1356  				BeforeEach(func() {
  1357  					conf.Consortiums["SomeConsortium"].Organizations[1].SkipAsForeign = true
  1358  				})
  1359  
  1360  				It("returns an error indicating the offending org", func() {
  1361  					err := encoder.HasSkippedForeignOrgs(conf)
  1362  					Expect(err).To(MatchError("organization 'ConsortiumOrg2' is marked to be skipped as foreign"))
  1363  				})
  1364  			})
  1365  		})
  1366  	})
  1367  
  1368  	Describe("Bootstrapper", func() {
  1369  		Describe("NewBootstrapper", func() {
  1370  			var (
  1371  				conf *genesisconfig.Profile
  1372  			)
  1373  
  1374  			BeforeEach(func() {
  1375  				conf = &genesisconfig.Profile{
  1376  					Policies: CreateStandardPolicies(),
  1377  					Orderer: &genesisconfig.Orderer{
  1378  						OrdererType: "solo",
  1379  						Policies:    CreateStandardPolicies(),
  1380  					},
  1381  				}
  1382  			})
  1383  
  1384  			It("creates a new bootstrapper for the given config", func() {
  1385  				bs, err := encoder.NewBootstrapper(conf)
  1386  				Expect(err).NotTo(HaveOccurred())
  1387  				Expect(bs).NotTo(BeNil())
  1388  			})
  1389  
  1390  			Context("when the channel config is bad", func() {
  1391  				BeforeEach(func() {
  1392  					conf.Orderer.OrdererType = "bad-type"
  1393  				})
  1394  
  1395  				It("wraps and returns the error", func() {
  1396  					_, err := encoder.NewBootstrapper(conf)
  1397  					Expect(err).To(MatchError("could not create channel group: could not create orderer group: unknown orderer type: bad-type"))
  1398  				})
  1399  			})
  1400  
  1401  			Context("when the channel config contains a foreign org", func() {
  1402  				BeforeEach(func() {
  1403  					conf.Orderer.Organizations = []*genesisconfig.Organization{
  1404  						{
  1405  							Name:          "MyOrg",
  1406  							SkipAsForeign: true,
  1407  						},
  1408  					}
  1409  				})
  1410  
  1411  				It("wraps and returns the error", func() {
  1412  					_, err := encoder.NewBootstrapper(conf)
  1413  					Expect(err).To(MatchError("all org definitions must be local during bootstrapping: organization 'MyOrg' is marked to be skipped as foreign"))
  1414  				})
  1415  			})
  1416  		})
  1417  
  1418  		Describe("New", func() {
  1419  			var (
  1420  				conf *genesisconfig.Profile
  1421  			)
  1422  
  1423  			BeforeEach(func() {
  1424  				conf = &genesisconfig.Profile{
  1425  					Policies: CreateStandardPolicies(),
  1426  					Orderer: &genesisconfig.Orderer{
  1427  						OrdererType: "solo",
  1428  						Policies:    CreateStandardPolicies(),
  1429  					},
  1430  				}
  1431  			})
  1432  
  1433  			It("creates a new bootstrapper for the given config", func() {
  1434  				bs := encoder.New(conf)
  1435  				Expect(bs).NotTo(BeNil())
  1436  			})
  1437  
  1438  			Context("when the channel config is bad", func() {
  1439  				BeforeEach(func() {
  1440  					conf.Orderer.OrdererType = "bad-type"
  1441  				})
  1442  
  1443  				It("panics", func() {
  1444  					Expect(func() { encoder.New(conf) }).To(Panic())
  1445  				})
  1446  			})
  1447  
  1448  		})
  1449  
  1450  		Describe("Functions", func() {
  1451  			var (
  1452  				bs *encoder.Bootstrapper
  1453  			)
  1454  
  1455  			BeforeEach(func() {
  1456  				bs = encoder.New(&genesisconfig.Profile{
  1457  					Policies: CreateStandardPolicies(),
  1458  					Orderer: &genesisconfig.Orderer{
  1459  						Policies:    CreateStandardPolicies(),
  1460  						OrdererType: "solo",
  1461  					},
  1462  				})
  1463  			})
  1464  
  1465  			Describe("GenesisBlock", func() {
  1466  				It("produces a new genesis block with a default channel ID", func() {
  1467  					block := bs.GenesisBlock()
  1468  					Expect(block).NotTo(BeNil())
  1469  				})
  1470  			})
  1471  
  1472  			Describe("GenesisBlockForChannel", func() {
  1473  				It("produces a new genesis block with a default channel ID", func() {
  1474  					block := bs.GenesisBlockForChannel("channel-id")
  1475  					Expect(block).NotTo(BeNil())
  1476  				})
  1477  			})
  1478  		})
  1479  	})
  1480  })