From 1697e9edae431b03ff0c2b4607bc084ee30cd90d Mon Sep 17 00:00:00 2001 From: Prachi Agrawal Date: Tue, 5 May 2026 21:56:44 +0530 Subject: [PATCH] fix(transaction): validate endorsements count on merge Signed-off-by: Prachi Agrawal --- tools/fxconfig/internal/transaction/merge.go | 9 ++++ .../internal/transaction/merge_test.go | 44 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/tools/fxconfig/internal/transaction/merge.go b/tools/fxconfig/internal/transaction/merge.go index 885ca31c..e334c153 100644 --- a/tools/fxconfig/internal/transaction/merge.go +++ b/tools/fxconfig/internal/transaction/merge.go @@ -61,6 +61,15 @@ func validateTransactionsForMerge(txs []*applicationpb.Tx) error { return fmt.Errorf("transaction %d: namespace count mismatch", i) } + if len(tx.GetEndorsements()) != baseNsCount { + return fmt.Errorf( + "transaction %d: endorsements count (%d) does not match namespaces count (%d)", + i, + len(tx.GetEndorsements()), + baseNsCount, + ) + } + for nsIdx := range baseTx.GetNamespaces() { if !proto.Equal(baseTx.GetNamespaces()[nsIdx], txs[i].GetNamespaces()[nsIdx]) { return fmt.Errorf("transaction %d: namespace %d content mismatch", i, nsIdx) diff --git a/tools/fxconfig/internal/transaction/merge_test.go b/tools/fxconfig/internal/transaction/merge_test.go index 27c4048d..69d3bdeb 100644 --- a/tools/fxconfig/internal/transaction/merge_test.go +++ b/tools/fxconfig/internal/transaction/merge_test.go @@ -109,6 +109,50 @@ func TestMerge_ErrorCases(t *testing.T) { require.Contains(t, err.Error(), "requires at least one endorsement") }) + t.Run("endorsements count mismatch", func(t *testing.T) { + t.Parallel() + + tx1 := &applicationpb.Tx{ + Namespaces: []*applicationpb.TxNamespace{ + {NsId: "ns1", NsVersion: 1}, + }, + Endorsements: []*applicationpb.Endorsements{ + { + EndorsementsWithIdentity: []*applicationpb.EndorsementWithIdentity{ + {Endorsement: []byte("sig-a1"), Identity: &msppb.Identity{MspId: "Org1MSP"}}, + }, + }, + { + EndorsementsWithIdentity: []*applicationpb.EndorsementWithIdentity{ + {Endorsement: []byte("sig-a2"), Identity: &msppb.Identity{MspId: "Org2MSP"}}, + }, + }, + }, + } + tx2 := &applicationpb.Tx{ + Namespaces: []*applicationpb.TxNamespace{ + {NsId: "ns1", NsVersion: 1}, + }, + Endorsements: []*applicationpb.Endorsements{ + { + EndorsementsWithIdentity: []*applicationpb.EndorsementWithIdentity{ + {Endorsement: []byte("sig-b1"), Identity: &msppb.Identity{MspId: "Org3MSP"}}, + }, + }, + { + EndorsementsWithIdentity: []*applicationpb.EndorsementWithIdentity{ + {Endorsement: []byte("sig-b2"), Identity: &msppb.Identity{MspId: "Org4MSP"}}, + }, + }, + }, + } + + result, err := Merge([]*applicationpb.Tx{tx1, tx2}) + require.Error(t, err) + require.Nil(t, result) + require.Contains(t, err.Error(), "endorsements count") + }) + t.Run("conflicting namespace writes", func(t *testing.T) { t.Parallel()