From 744b90e5de22c0d0996f437a88cf1a41c9bab45d Mon Sep 17 00:00:00 2001 From: Natalie Morad Date: Mon, 2 Mar 2026 19:22:19 +0200 Subject: [PATCH 1/4] add endpoint and consenter consistency validations to OrdererRules Signed-off-by: Natalie Morad --- config/generate/config_block_gen.go | 18 +++- config/verify/orderer_rules.go | 92 ++++++++++++++++- config/verify/orderer_rules_test.go | 91 +++++++++++++++++ testutil/configutil/config_update_utils.go | 113 +++++++++++++++++++-- 4 files changed, 296 insertions(+), 18 deletions(-) diff --git a/config/generate/config_block_gen.go b/config/generate/config_block_gen.go index 2adc11f36..f7afb805e 100644 --- a/config/generate/config_block_gen.go +++ b/config/generate/config_block_gen.go @@ -18,6 +18,7 @@ import ( "strings" "github.com/hyperledger/fabric-protos-go-apiv2/common" + "github.com/hyperledger/fabric-x-common/api/types" "github.com/hyperledger/fabric-x-common/tools/configtxgen" "github.com/hyperledger/fabric-x-orderer/config" "github.com/pkg/errors" @@ -60,7 +61,7 @@ func CreateProfile(dir string, sharedConfigYaml *config.SharedConfigYaml, shared Host: party.ConsenterConfig.Host, Port: party.ConsenterConfig.Port, MSPID: "", - Identity: party.ConsenterConfig.TLSCert, + Identity: party.ConsenterConfig.SignCert, ClientTLSCert: party.ConsenterConfig.TLSCert, ServerTLSCert: party.ConsenterConfig.TLSCert, } @@ -76,7 +77,7 @@ func CreateProfile(dir string, sharedConfigYaml *config.SharedConfigYaml, shared templateAppOrg := profile.Application.Organizations[0] profile.Application.Organizations = make([]*configtxgen.Organization, len(sharedConfigYaml.PartiesConfig)) - for i := 0; i < len(sharedConfigYaml.PartiesConfig); i++ { + for i, p := range sharedConfigYaml.PartiesConfig { org := &configtxgen.Organization{ Name: fmt.Sprintf("org%d", i+1), ID: fmt.Sprintf("org%d", i+1), @@ -84,7 +85,7 @@ func CreateProfile(dir string, sharedConfigYaml *config.SharedConfigYaml, shared MSPType: templateAppOrg.MSPType, Policies: make(map[string]*configtxgen.Policy), AnchorPeers: templateAppOrg.AnchorPeers, - OrdererEndpoints: templateAppOrg.OrdererEndpoints, // TODO: org.OrdererEndpoints in the new format + OrdererEndpoints: buildOrdererEndpoints(uint32(p.PartyID), p.RouterConfig.Host, int(p.RouterConfig.Port), p.AssemblerConfig.Host, int(p.AssemblerConfig.Port)), AdminPrincipal: templateAppOrg.AdminPrincipal, SkipAsForeign: templateAppOrg.SkipAsForeign, } @@ -104,7 +105,7 @@ func CreateProfile(dir string, sharedConfigYaml *config.SharedConfigYaml, shared templateOrdererOrg := profile.Orderer.Organizations[0] profile.Orderer.Organizations = make([]*configtxgen.Organization, len(sharedConfigYaml.PartiesConfig)) - for i := 0; i < len(sharedConfigYaml.PartiesConfig); i++ { + for i, p := range sharedConfigYaml.PartiesConfig { org := &configtxgen.Organization{ Name: fmt.Sprintf("org%d", i+1), ID: fmt.Sprintf("org%d", i+1), @@ -112,7 +113,7 @@ func CreateProfile(dir string, sharedConfigYaml *config.SharedConfigYaml, shared MSPType: templateOrdererOrg.MSPType, Policies: make(map[string]*configtxgen.Policy), AnchorPeers: templateOrdererOrg.AnchorPeers, - OrdererEndpoints: templateOrdererOrg.OrdererEndpoints, // TODO: org.OrdererEndpoints in the new format + OrdererEndpoints: buildOrdererEndpoints(uint32(p.PartyID), p.RouterConfig.Host, int(p.RouterConfig.Port), p.AssemblerConfig.Host, int(p.AssemblerConfig.Port)), AdminPrincipal: templateOrdererOrg.AdminPrincipal, SkipAsForeign: templateOrdererOrg.SkipAsForeign, } @@ -163,3 +164,10 @@ func generatePublicKey(path string) ([]byte, error) { return publicKeyPEM, nil } + +func buildOrdererEndpoints(id uint32, routerHost string, routerPort int, assemblerHost string, assemblerPort int) []*types.OrdererEndpoint { + return []*types.OrdererEndpoint{ + {Host: routerHost, Port: routerPort, ID: id, API: []string{"broadcast"}}, + {Host: assemblerHost, Port: assemblerPort, ID: id, API: []string{"deliver"}}, + } +} diff --git a/config/verify/orderer_rules.go b/config/verify/orderer_rules.go index f1f9df262..8437777e9 100644 --- a/config/verify/orderer_rules.go +++ b/config/verify/orderer_rules.go @@ -7,13 +7,15 @@ SPDX-License-Identifier: Apache-2.0 package verify import ( + "slices" "time" smartbft_types "github.com/hyperledger-labs/SmartBFT/pkg/types" "github.com/hyperledger/fabric-lib-go/bccsp" "github.com/hyperledger/fabric-protos-go-apiv2/common" + "github.com/hyperledger/fabric-x-common/api/types" "github.com/hyperledger/fabric-x-common/common/channelconfig" - "github.com/hyperledger/fabric-x-orderer/common/types" + arma_types "github.com/hyperledger/fabric-x-orderer/common/types" config_protos "github.com/hyperledger/fabric-x-orderer/config/protos" "github.com/pkg/errors" "google.golang.org/protobuf/proto" @@ -21,7 +23,7 @@ import ( //go:generate counterfeiter -o mocks/orderer_rules.go . OrdererRules type OrdererRules interface { - ValidateNewConfig(envelope *common.Envelope, bccsp bccsp.BCCSP, partyID types.PartyID) error + ValidateNewConfig(envelope *common.Envelope, bccsp bccsp.BCCSP, partyID arma_types.PartyID) error ValidateTransition(current channelconfig.Resources, next *common.Envelope, bccsp bccsp.BCCSP) error } @@ -36,13 +38,14 @@ type DefaultOrdererRules struct{} // 3. SmartBFTConfig.RequestMaxBytes must be positive and >= SharedConfig.BatchingConfig.RequestMaxBytes. // This ensures config requests accepted by the router are not rejected by SmartBFT. // 4. SmartBFTConfig must pass SmartBFT validation. +// 5. OrdererEndpoints for each organization must be defined, non-empty, +// and include both "broadcast" and "deliver" roles. +// 6. ConsenterMapping must be consistent with the consenters defined in the shared config. // -// TODO: Validate OrdererEndpoints in the organization definitions. // TODO: Validate that ca certificates in the sharedConfig are the same as in the ordererOrganization ca certificates. // TODO: Validate new certificates - chain of trust, expiration, etc. -// TODO: Validate ConsenterMapping. // TODO: Validate BlockValidationPolicy. -func (or *DefaultOrdererRules) ValidateNewConfig(envelope *common.Envelope, bccsp bccsp.BCCSP, partyID types.PartyID) error { +func (or *DefaultOrdererRules) ValidateNewConfig(envelope *common.Envelope, bccsp bccsp.BCCSP, partyID arma_types.PartyID) error { bundle, err := channelconfig.NewBundleFromEnvelope(envelope, bccsp) if err != nil { return errors.Wrap(err, "failed to create bundle from new envelope config") @@ -92,6 +95,18 @@ func (or *DefaultOrdererRules) ValidateNewConfig(envelope *common.Envelope, bccs return errors.Wrap(err, "smartbft config validation failed") } + // 5. + for _, org := range ordererConfig.Organizations() { + if err := validateOrdererOrgEndpoints(org.Endpoints()); err != nil { + return errors.Wrapf(err, "invalid endpoints for orderer organization %s", org.Name()) + } + } + + // 6. + if err := validateConsenterConsistency(ordererConfig.Consenters(), sharedConfig.PartiesConfig); err != nil { + return errors.Wrap(err, "consenter mapping is inconsistent with shared config parties") + } + return nil } @@ -289,3 +304,70 @@ func validateSmartBFTConfig(id uint64, cfg *config_protos.SmartBFTConfig) error return nil } + +func validateOrdererOrgEndpoints(endpoints []string) error { + if len(endpoints) == 0 { + return errors.New("endpoints are empty") + } + + hasBroadcast := false + hasDeliver := false + for _, raw := range endpoints { + ep, err := types.ParseOrdererEndpoint(raw) + if err != nil { + return err + } + if slices.Contains(ep.API, types.Broadcast) { + hasBroadcast = true + } + if slices.Contains(ep.API, types.Deliver) { + hasDeliver = true + } + } + + if !hasBroadcast { + return errors.New("missing broadcast endpoint") + } + if !hasDeliver { + return errors.New("missing deliver endpoint") + } + + return nil +} + +func validateConsenterConsistency(consenters []*common.Consenter, parties []*config_protos.PartyConfig) error { + if len(consenters) != len(parties) { + return errors.Errorf("number of parties in Orderer consenters mapping (%d) does not match number of parties in Shared config (%d)", len(consenters), len(parties)) + } + + partiesMap := make(map[uint32]*config_protos.PartyConfig) + for _, p := range parties { + partiesMap[p.PartyID] = p + } + + for _, consenter := range consenters { + party, exists := partiesMap[consenter.Id] + if !exists { + return errors.Errorf("party ID %d missing from shared config", consenter.Id) + } + + nodeCfg := party.ConsenterConfig + if consenter.Host != nodeCfg.Host { + return errors.Errorf("host mismatch for party %d: %s != %s", consenter.Id, consenter.Host, nodeCfg.Host) + } + + if consenter.Port != nodeCfg.Port { + return errors.Errorf("port mismatch for party %d: %d != %d", consenter.Id, consenter.Port, nodeCfg.Port) + } + + if !slices.Equal(consenter.Identity, nodeCfg.SignCert) { + return errors.Errorf("identity/sign_cert mismatch for party %d", consenter.Id) + } + + if !slices.Equal(consenter.ServerTlsCert, nodeCfg.TlsCert) || !slices.Equal(consenter.ClientTlsCert, nodeCfg.TlsCert) { + return errors.Errorf("TLS certificate mismatch for party %d", consenter.Id) + } + } + + return nil +} diff --git a/config/verify/orderer_rules_test.go b/config/verify/orderer_rules_test.go index af85bb265..28cf21012 100644 --- a/config/verify/orderer_rules_test.go +++ b/config/verify/orderer_rules_test.go @@ -9,6 +9,7 @@ package verify_test import ( "os" "path/filepath" + "strings" "testing" "github.com/hyperledger/fabric-lib-go/bccsp/factory" @@ -28,6 +29,7 @@ import ( "github.com/hyperledger/fabric-x-orderer/testutil/configutil" "github.com/hyperledger/fabric/protoutil" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" ) func TestValidateNewConfig(t *testing.T) { @@ -127,6 +129,95 @@ func TestValidateNewConfig_InvalidRequestMaxBytes(t *testing.T) { require.Contains(t, err.Error(), "smartbft RequestMaxBytes must be equal or greater than BatchingConfig RequestMaxBytes") } +func TestValidateNewConfig_InvalidOrdererEndpoint(t *testing.T) { + _, env, _, _, _, _, _, cleanup := setupOrdererRulesTest(t, 1) + defer cleanup() + + payload, err := protoutil.UnmarshalPayload(env.Payload) + require.NoError(t, err) + + cfgEnv := &common.ConfigEnvelope{} + require.NoError(t, proto.Unmarshal(payload.Data, cfgEnv)) + + endpointsVal := cfgEnv.Config.ChannelGroup.Groups["Orderer"].Groups["org1"].Values["Endpoints"] + + oa := &common.OrdererAddresses{} + require.NoError(t, proto.Unmarshal(endpointsVal.Value, oa)) + + // remove broadcast + var addresses []string + for _, a := range oa.Addresses { + if !strings.Contains(a, ",broadcast,") { + addresses = append(addresses, a) + } + } + oa.Addresses = addresses + endpointsVal.Value, err = proto.Marshal(oa) + require.NoError(t, err) + + payload.Data, err = proto.Marshal(cfgEnv) + require.NoError(t, err) + + env.Payload, err = proto.Marshal(payload) + require.NoError(t, err) + + or := verify.DefaultOrdererRules{} + err = or.ValidateNewConfig(env, factory.GetDefault(), types.PartyID(1)) + require.Error(t, err) + require.Contains(t, err.Error(), "missing broadcast endpoint") +} + +func TestValidateNewConfig_ConenterConsistency(t *testing.T) { + dir, _, currBundle, builder, proposer, signer, verifier, cleanup := setupOrdererRulesTest(t, 1) + defer cleanup() + + // create a valid config update first + cert := []byte("fake1-tls-cert") + updatePb := builder.UpdateConsensusTLSCert(t, types.PartyID(1), cert) + + updateEnv := configutil.CreateConfigTX(t, dir, []types.PartyID{1}, 1, updatePb) + req := &comm.Request{Payload: updateEnv.Payload, Signature: updateEnv.Signature} + + nextCfgEnv, err := proposer.ProposeConfigUpdate(req, currBundle, signer, verifier) + require.NoError(t, err) + + env := &common.Envelope{ + Payload: nextCfgEnv.Payload, + Signature: nextCfgEnv.Signature, + } + + // change TLSCert in consenter_mapping to a different value + payload := &common.Payload{} + err = proto.Unmarshal(env.Payload, payload) + require.NoError(t, err) + + cfgEnv := &common.ConfigEnvelope{} + require.NoError(t, proto.Unmarshal(payload.Data, cfgEnv)) + + orderersVal := cfgEnv.Config.ChannelGroup.Groups["Orderer"].Values["Orderers"] + orderers := &common.Orderers{} + require.NoError(t, proto.Unmarshal(orderersVal.Value, orderers)) + + // change the TLS cert in consenter_mapping to create mismatch + orderers.ConsenterMapping[0].ServerTlsCert = []byte("fake2-tls-cert") + orderers.ConsenterMapping[0].ClientTlsCert = []byte("fake2-tls-cert") + + orderersVal.Value, err = proto.Marshal(orderers) + require.NoError(t, err) + + payload.Data, err = proto.Marshal(cfgEnv) + require.NoError(t, err) + + env.Payload, err = proto.Marshal(payload) + require.NoError(t, err) + + or := verify.DefaultOrdererRules{} + err = or.ValidateNewConfig(env, factory.GetDefault(), types.PartyID(1)) + + require.Error(t, err) + require.Contains(t, err.Error(), "TLS certificate mismatch for party 1") +} + func TestValidateTransition_RemoveAndAddSameParty(t *testing.T) { or := verify.DefaultOrdererRules{} bccsp := factory.GetDefault() diff --git a/testutil/configutil/config_update_utils.go b/testutil/configutil/config_update_utils.go index b78b59e1f..d9c5676fc 100644 --- a/testutil/configutil/config_update_utils.go +++ b/testutil/configutil/config_update_utils.go @@ -32,8 +32,9 @@ import ( ) var ( - partiesConfigPath = []string{"channel_group", "groups", "Orderer", "values", "ConsensusType", "value", "metadata", "PartiesConfig"} - sharedConfigPath = []string{"channel_group", "groups", "Orderer", "values", "ConsensusType", "value", "metadata"} + partiesConfigPath = []string{"channel_group", "groups", "Orderer", "values", "ConsensusType", "value", "metadata", "PartiesConfig"} + sharedConfigPath = []string{"channel_group", "groups", "Orderer", "values", "ConsensusType", "value", "metadata"} + consenterMappingPath = []string{"channel_group", "groups", "Orderer", "values", "Orderers", "value", "consenter_mapping"} ) type ( @@ -303,6 +304,7 @@ func (c *ConfigUpdateBuilder) UpdateBatcherSignCert(t *testing.T, partyID types. } func (c *ConfigUpdateBuilder) UpdateConsenterSignCert(t *testing.T, partyID types.PartyID, cert []byte) []byte { + // Update parties config partiesConfig := getNestedJSONValue(t, c.configData, partiesConfigPath...) partiesConfigList := partiesConfig.([]any) @@ -316,8 +318,22 @@ func (c *ConfigUpdateBuilder) UpdateConsenterSignCert(t *testing.T, partyID type break } } - require.True(t, found, "PartyID %d not found in PartiesConfig", partyID) + + // Update consenter mapping + consenterMapping := getNestedJSONValue(t, c.configData, consenterMappingPath...) + mappingList := consenterMapping.([]any) + + found = false + for _, mapping := range mappingList { + mappingMap := mapping.(map[string]any) + if uint32(partyID) == uint32(mappingMap["id"].(float64)) { + mappingMap["identity"] = cert + found = true + break + } + } + require.True(t, found, "PartyID %d not found in ConsenterMapping", partyID) return c.createConfigUpdate(t, c.configData) } @@ -496,6 +512,7 @@ func (c *ConfigUpdateBuilder) UpdateBatcherTLSCert(t *testing.T, partyID types.P } func (c *ConfigUpdateBuilder) UpdateConsensusTLSCert(t *testing.T, partyID types.PartyID, cert []byte) []byte { + // Update parties config partiesConfig := getNestedJSONValue(t, c.configData, partiesConfigPath...) partiesConfigList := partiesConfig.([]any) @@ -509,8 +526,23 @@ func (c *ConfigUpdateBuilder) UpdateConsensusTLSCert(t *testing.T, partyID types break } } - require.True(t, found, "PartyID %d not found in PartiesConfig", partyID) + + // Update consenter mapping + consenterMapping := getNestedJSONValue(t, c.configData, consenterMappingPath...) + mappingList := consenterMapping.([]any) + + found = false + for _, mapping := range mappingList { + mappingMap := mapping.(map[string]any) + if uint32(partyID) == uint32(mappingMap["id"].(float64)) { + mappingMap["client_tls_cert"] = cert + mappingMap["server_tls_cert"] = cert + found = true + break + } + } + require.True(t, found, "PartyID %d not found in ConsenterMapping", partyID) return c.createConfigUpdate(t, c.configData) } @@ -552,7 +584,7 @@ func (c *ConfigUpdateBuilder) UpdateOrderingEndpoint(t *testing.T, partyID types require.True(t, found, "PartyID %d not found in PartiesConfig", partyID) - consenterMapping := getNestedJSONValue(t, c.configData, "channel_group", "groups", "Orderer", "values", "Orderers", "value", "consenter_mapping") + consenterMapping := getNestedJSONValue(t, c.configData, consenterMappingPath...) mappingList := consenterMapping.([]any) found = false @@ -676,8 +708,8 @@ func (c *ConfigUpdateBuilder) UpdateOrgEndpoints(t *testing.T, partyID types.Par // Update OrdererAddresses org := fmt.Sprintf("org%d", partyID) addresses := []string{ - fmt.Sprintf("id=%d,msp-id=%s,api=broadcast,host=%s,port=%d", partyID, org, broadcastHost, broadcastPort), - fmt.Sprintf("id=%d,msp-id=%s,api=deliver,host=%s,port=%d", partyID, org, deliverHost, deliverPort), + fmt.Sprintf("id=%d,broadcast,%s:%d", partyID, broadcastHost, broadcastPort), + fmt.Sprintf("id=%d,deliver,%s:%d", partyID, deliverHost, deliverPort), } overwriteNestedJSONValue(t, c.configData, addresses, "channel_group", "groups", "Orderer", "groups", org, "values", "Endpoints", "value", "addresses") @@ -691,6 +723,7 @@ func (c *ConfigUpdateBuilder) AddNewParty(t *testing.T, newParty *protos.PartyCo maxPartyID++ + // Update parties config batchersConfig := []map[string]any{} for _, bc := range newParty.BatchersConfig { batchersConfig = append(batchersConfig, @@ -730,11 +763,53 @@ func (c *ConfigUpdateBuilder) AddNewParty(t *testing.T, newParty *protos.PartyCo sharedConfig.(map[string]any)["MaxPartyID"] = maxPartyID sharedConfig.(map[string]any)["PartiesConfig"] = partiesConfig + + // Update consenter mapping + consenterMapping := getNestedJSONValue(t, c.configData, consenterMappingPath...) + consenterMappingList := consenterMapping.([]any) + + consenterMappingList = append(consenterMappingList, map[string]any{ + "id": maxPartyID, + "host": newParty.ConsenterConfig.Host, + "port": newParty.ConsenterConfig.Port, + "identity": newParty.ConsenterConfig.SignCert, + "client_tls_cert": newParty.ConsenterConfig.TlsCert, + "server_tls_cert": newParty.ConsenterConfig.TlsCert, + }) + + // Update Organization + orgs := getNestedJSONValue(t, c.configData, "channel_group", "groups", "Orderer", "groups").(map[string]any) + + // use an existing org as a template + var tmpl map[string]any + for _, v := range orgs { + tmpl = v.(map[string]any) + break + } + require.NotNil(t, tmpl, "orderer org not found") + + data, err := json.Marshal(tmpl) + require.NoError(t, err) + var newOrg map[string]any + require.NoError(t, json.Unmarshal(data, &newOrg)) + + // Update endpoints + endpoints := newOrg["values"].(map[string]any)["Endpoints"].(map[string]any) + endpoints["value"].(map[string]any)["addresses"] = []string{ + fmt.Sprintf("id=%d,broadcast,%s:%d", int(maxPartyID), newParty.RouterConfig.Host, newParty.RouterConfig.Port), + fmt.Sprintf("id=%d,deliver,%s:%d", int(maxPartyID), newParty.AssemblerConfig.Host, newParty.AssemblerConfig.Port), + } + orgName := fmt.Sprintf("org%d", uint32(maxPartyID)) + orgs[orgName] = newOrg + overwriteNestedJSONValue(t, c.configData, sharedConfig, sharedConfigPath...) + overwriteNestedJSONValue(t, c.configData, consenterMappingList, consenterMappingPath...) + overwriteNestedJSONValue(t, c.configData, orgs, "channel_group", "groups", "Orderer", "groups") return c.createConfigUpdate(t, c.configData) } func (c *ConfigUpdateBuilder) RemoveParty(t *testing.T, partyID types.PartyID) []byte { + // Remove the party from parties config partiesConfig := getNestedJSONValue(t, c.configData, partiesConfigPath...) partiesConfigList := partiesConfig.([]any) @@ -751,10 +826,32 @@ func (c *ConfigUpdateBuilder) RemoveParty(t *testing.T, partyID types.PartyID) [ break } } - require.True(t, found, "PartyID %d not found in PartiesConfig", partyID) + // Remove the party from consenter mapping + found = false + consenterMapping := getNestedJSONValue(t, c.configData, consenterMappingPath...) + consenterMappingList := consenterMapping.([]any) + for i, party := range consenterMappingList { + partyMap := party.(map[string]any) + if uint32(partyID) == uint32(partyMap["id"].(float64)) { + found = true + consenterMappingList = append(consenterMappingList[:i], consenterMappingList[i+1:]...) + break + } + } + require.True(t, found, "PartyID %d not found in ConsenterMapping", partyID) + + // Remove the party from organization + orgName := fmt.Sprintf("org%d", partyID) + orgs := getNestedJSONValue(t, c.configData, "channel_group", "groups", "Orderer", "groups").(map[string]any) + _, ok := orgs[orgName] + require.True(t, ok, "org %s not found", orgName) + delete(orgs, orgName) + overwriteNestedJSONValue(t, c.configData, partiesConfigList, partiesConfigPath...) + overwriteNestedJSONValue(t, c.configData, consenterMappingList, consenterMappingPath...) + overwriteNestedJSONValue(t, c.configData, orgs, "channel_group", "groups", "Orderer", "groups") return c.createConfigUpdate(t, c.configData) } From b29679fe13d89c337911a6916fbdc5adb50466ef Mon Sep 17 00:00:00 2001 From: Natalie Morad Date: Tue, 10 Mar 2026 09:43:59 +0200 Subject: [PATCH 2/4] fix Signed-off-by: Natalie Morad --- config/generate/config_block_gen.go | 4 ++-- config/verify/orderer_rules.go | 3 +++ config/verify/orderer_rules_test.go | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/config/generate/config_block_gen.go b/config/generate/config_block_gen.go index f7afb805e..186c86424 100644 --- a/config/generate/config_block_gen.go +++ b/config/generate/config_block_gen.go @@ -167,7 +167,7 @@ func generatePublicKey(path string) ([]byte, error) { func buildOrdererEndpoints(id uint32, routerHost string, routerPort int, assemblerHost string, assemblerPort int) []*types.OrdererEndpoint { return []*types.OrdererEndpoint{ - {Host: routerHost, Port: routerPort, ID: id, API: []string{"broadcast"}}, - {Host: assemblerHost, Port: assemblerPort, ID: id, API: []string{"deliver"}}, + {Host: routerHost, Port: routerPort, ID: id, API: []string{types.Broadcast}}, + {Host: assemblerHost, Port: assemblerPort, ID: id, API: []string{types.Deliver}}, } } diff --git a/config/verify/orderer_rules.go b/config/verify/orderer_rules.go index 8437777e9..bc405fcc4 100644 --- a/config/verify/orderer_rules.go +++ b/config/verify/orderer_rules.go @@ -350,6 +350,9 @@ func validateConsenterConsistency(consenters []*common.Consenter, parties []*con if !exists { return errors.Errorf("party ID %d missing from shared config", consenter.Id) } + if party.ConsenterConfig == nil { + return errors.Errorf("consenter config missing for party %d", consenter.Id) + } nodeCfg := party.ConsenterConfig if consenter.Host != nodeCfg.Host { diff --git a/config/verify/orderer_rules_test.go b/config/verify/orderer_rules_test.go index 28cf21012..ac4cad71b 100644 --- a/config/verify/orderer_rules_test.go +++ b/config/verify/orderer_rules_test.go @@ -167,7 +167,7 @@ func TestValidateNewConfig_InvalidOrdererEndpoint(t *testing.T) { require.Contains(t, err.Error(), "missing broadcast endpoint") } -func TestValidateNewConfig_ConenterConsistency(t *testing.T) { +func TestValidateNewConfig_ConsenterConsistency(t *testing.T) { dir, _, currBundle, builder, proposer, signer, verifier, cleanup := setupOrdererRulesTest(t, 1) defer cleanup() From 53b4a5252fbfc11d6d5a912cd30b2c20c9d6e704 Mon Sep 17 00:00:00 2001 From: Natalie Morad Date: Wed, 11 Mar 2026 09:21:09 +0200 Subject: [PATCH 3/4] fix review Signed-off-by: Natalie Morad --- config/generate/config_block_gen.go | 20 +++++++++----------- config/verify/orderer_rules.go | 14 ++++++++++---- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/config/generate/config_block_gen.go b/config/generate/config_block_gen.go index 186c86424..321f3a185 100644 --- a/config/generate/config_block_gen.go +++ b/config/generate/config_block_gen.go @@ -77,17 +77,16 @@ func CreateProfile(dir string, sharedConfigYaml *config.SharedConfigYaml, shared templateAppOrg := profile.Application.Organizations[0] profile.Application.Organizations = make([]*configtxgen.Organization, len(sharedConfigYaml.PartiesConfig)) - for i, p := range sharedConfigYaml.PartiesConfig { + for i := range sharedConfigYaml.PartiesConfig { org := &configtxgen.Organization{ - Name: fmt.Sprintf("org%d", i+1), - ID: fmt.Sprintf("org%d", i+1), - MSPDir: filepath.Join(dir, "crypto", "ordererOrganizations", fmt.Sprintf("org%d", i+1), "msp"), - MSPType: templateAppOrg.MSPType, - Policies: make(map[string]*configtxgen.Policy), - AnchorPeers: templateAppOrg.AnchorPeers, - OrdererEndpoints: buildOrdererEndpoints(uint32(p.PartyID), p.RouterConfig.Host, int(p.RouterConfig.Port), p.AssemblerConfig.Host, int(p.AssemblerConfig.Port)), - AdminPrincipal: templateAppOrg.AdminPrincipal, - SkipAsForeign: templateAppOrg.SkipAsForeign, + Name: fmt.Sprintf("org%d", i+1), + ID: fmt.Sprintf("org%d", i+1), + MSPDir: filepath.Join(dir, "crypto", "ordererOrganizations", fmt.Sprintf("org%d", i+1), "msp"), + MSPType: templateAppOrg.MSPType, + Policies: make(map[string]*configtxgen.Policy), + AnchorPeers: templateAppOrg.AnchorPeers, + AdminPrincipal: templateAppOrg.AdminPrincipal, + SkipAsForeign: templateAppOrg.SkipAsForeign, } // Update policy rules to use correct org names @@ -112,7 +111,6 @@ func CreateProfile(dir string, sharedConfigYaml *config.SharedConfigYaml, shared MSPDir: filepath.Join(dir, "crypto", "ordererOrganizations", fmt.Sprintf("org%d", i+1), "msp"), MSPType: templateOrdererOrg.MSPType, Policies: make(map[string]*configtxgen.Policy), - AnchorPeers: templateOrdererOrg.AnchorPeers, OrdererEndpoints: buildOrdererEndpoints(uint32(p.PartyID), p.RouterConfig.Host, int(p.RouterConfig.Port), p.AssemblerConfig.Host, int(p.AssemblerConfig.Port)), AdminPrincipal: templateOrdererOrg.AdminPrincipal, SkipAsForeign: templateOrdererOrg.SkipAsForeign, diff --git a/config/verify/orderer_rules.go b/config/verify/orderer_rules.go index bc405fcc4..e3bac5f97 100644 --- a/config/verify/orderer_rules.go +++ b/config/verify/orderer_rules.go @@ -342,6 +342,9 @@ func validateConsenterConsistency(consenters []*common.Consenter, parties []*con partiesMap := make(map[uint32]*config_protos.PartyConfig) for _, p := range parties { + if p == nil { + return errors.New("party config is nil in shared config") + } partiesMap[p.PartyID] = p } @@ -351,9 +354,8 @@ func validateConsenterConsistency(consenters []*common.Consenter, parties []*con return errors.Errorf("party ID %d missing from shared config", consenter.Id) } if party.ConsenterConfig == nil { - return errors.Errorf("consenter config missing for party %d", consenter.Id) + return errors.Errorf("consenter config missing in shared config for party %d", consenter.Id) } - nodeCfg := party.ConsenterConfig if consenter.Host != nodeCfg.Host { return errors.Errorf("host mismatch for party %d: %s != %s", consenter.Id, consenter.Host, nodeCfg.Host) @@ -367,8 +369,12 @@ func validateConsenterConsistency(consenters []*common.Consenter, parties []*con return errors.Errorf("identity/sign_cert mismatch for party %d", consenter.Id) } - if !slices.Equal(consenter.ServerTlsCert, nodeCfg.TlsCert) || !slices.Equal(consenter.ClientTlsCert, nodeCfg.TlsCert) { - return errors.Errorf("TLS certificate mismatch for party %d", consenter.Id) + if len(consenter.ServerTlsCert) > 0 && !slices.Equal(consenter.ServerTlsCert, nodeCfg.TlsCert) { + return errors.Errorf("server TLS certificate mismatch for party %d", consenter.Id) + } + + if len(consenter.ClientTlsCert) > 0 && !slices.Equal(consenter.ClientTlsCert, nodeCfg.TlsCert) { + return errors.Errorf("client TLS certificate mismatch for party %d", consenter.Id) } } From 778e1d877f4c4db136bbc1633956a595c74483c8 Mon Sep 17 00:00:00 2001 From: Natalie Morad Date: Wed, 11 Mar 2026 12:06:00 +0200 Subject: [PATCH 4/4] fix Signed-off-by: Natalie Morad --- config/verify/orderer_rules.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/verify/orderer_rules.go b/config/verify/orderer_rules.go index e3bac5f97..7fe1e4216 100644 --- a/config/verify/orderer_rules.go +++ b/config/verify/orderer_rules.go @@ -349,6 +349,9 @@ func validateConsenterConsistency(consenters []*common.Consenter, parties []*con } for _, consenter := range consenters { + if consenter == nil { + return errors.New("consenter config is nil in shared config") + } party, exists := partiesMap[consenter.Id] if !exists { return errors.Errorf("party ID %d missing from shared config", consenter.Id)