Skip to content

fix(network): fill same-MSP endorser slots with distinct identities - #2139

Open
HayimShaul wants to merge 1 commit into
mainfrom
2048_select_distinct_endorsers_per_required_slot
Open

fix(network): fill same-MSP endorser slots with distinct identities#2139
HayimShaul wants to merge 1 commit into
mainfrom
2048_select_distinct_endorsers_per_required_slot

Conversation

@HayimShaul

Copy link
Copy Markdown
Contributor

Fixes #2048

SelectEndorsersForMSPSets sampled one configured endorser per required MSP ID of a candidate set without excluding identities already picked for an earlier slot of the same set. A namespace endorsement policy requiring two signers from one MSP — e.g. AND(Org1MSP.member, Org1MSP.member), whose principal set inquire.SatisfiedBy() reports as ["Org1MSP", "Org1MSP"] — could therefore be satisfied by one endorser signing twice, collapsing the intended 2-of-N-within-org guarantee onto a single point of trust.

Changes:

  • Per-slot sampling now excludes identities already selected for the same candidate set, so every returned identity is distinct and the result length always matches the chosen candidate set.
  • A candidate set requiring more distinct signers from an MSP than that MSP has configured endorsers is no longer satisfiable: it is skipped so the remaining candidate sets are tried, and only a hard error is returned if none is coverable — correctness over availability, consistent with the rest of this selector.
  • Duplicate entries in the configured endorser list collapse to a single candidate, since one endorser provides one endorsement; counting it twice skewed the uniform random pick and overstated how many distinct endorsers an MSP offers.
  • Regression tests: the same-MSP-twice case never returns a duplicate over many randomized runs (and both endorsers remain reachable in both slots), plus non-coverable-set fallthrough, duplicate configured entries, and a mixed repeated/single MSP set. All five new subtests fail against the pre-fix code.
  • Docs updated (docs/services/network-fabricx.md, docs/services/network-fabric.md, docs/configuration.md) to state that same-MSP signer slots are filled with distinct endorsers, and that an MSP with fewer distinct endorsers than required signers is a hard error.

make checks and make lint pass; the affected packages pass under -race.

@HayimShaul HayimShaul added this to the Q3/26 milestone Aug 4, 2026
@HayimShaul HayimShaul self-assigned this Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

📊 Token Validation Benchmark

Comparison of this PR against the base branch. 🟢 improvement · 🔴 regression · ➖ within ±1.0% noise.

Variant Benchmark Params Workers TPS (base → PR) Δ TPS
csp BenchmarkAPIGRPC f=1, nc=4, w=token-validation-service 4 137 → 137 ➖ -0.1%
csp BenchmarkLocalTokenValidation out-tokens=2in-tokens=2 4 104 → 104 ➖ +0.0%
ipa BenchmarkAPIGRPC f=1, nc=4, w=token-validation-service 4 78 → 78 ➖ +0.1%
ipa BenchmarkLocalTokenValidation out-tokens=2in-tokens=2 4 76 → 76 ➖ +0.2%

@HayimShaul
HayimShaul force-pushed the 2048_select_distinct_endorsers_per_required_slot branch from 81ec699 to f5e34e1 Compare August 5, 2026 08:27
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

📊 Token Validation Benchmark

Comparison of this PR against the base branch. 🟢 improvement · 🔴 regression · ➖ within ±1.0% noise.

Variant Benchmark Params Workers TPS (base → PR) Δ TPS
csp BenchmarkAPIGRPC f=1, nc=4, w=token-validation-service 4 129 → 129 ➖ +0.1%
csp BenchmarkLocalTokenValidation out-tokens=2in-tokens=2 4 116 → 116 ➖ +0.0%
ipa BenchmarkAPIGRPC f=1, nc=4, w=token-validation-service 4 78 → 78 ➖ +0.1%
ipa BenchmarkLocalTokenValidation out-tokens=2in-tokens=2 4 76 → 76 ➖ -0.0%

@HayimShaul
HayimShaul force-pushed the 2048_select_distinct_endorsers_per_required_slot branch from f5e34e1 to 47812f0 Compare August 5, 2026 11:34
@AkramBitar
AkramBitar marked this pull request as ready for review August 5, 2026 14:39
@AkramBitar
AkramBitar self-requested a review August 5, 2026 14:40

@AkramBitar AkramBitar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three notes on the fix itself.

Comment on lines +48 to +53
if _, ok := seen[string(id)]; ok {
// configured may legitimately list the same endorser twice; it still provides a
// single endorsement, so bucket it once.
continue
}
seen[string(id)] = struct{}{}

@AkramBitar AkramBitar Aug 5, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dedup is byte-exact, so two byte-distinct identities carrying the same signing key count as two endorsers and can fill both same-MSP slots — one physical signer, the collapse this PR closes.

The sibling threshold_rule path treats exactly that as an anomaly (fabricx/endorsement/nspolicy.go:209-212: two distinct identities with the same key "would mean two different endorsers share a private key"), so the two paths now disagree on the same question. Narrow in practice, but the godoc promises "never the same endorser twice", which byte equality does not quite deliver.

Either reuse ecdsaPublicKeyOf to close it, or add one godoc line saying distinctness is by identity bytes.

}
}

return nil, errors.Errorf("no configured endorser covers any of the [%d] policy-satisfying MSP set(s) with a distinct endorser per required signer", len(candidates))

@AkramBitar AkramBitar Aug 5, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both failure modes produce this one string, and neither is diagnosable: with AND(Org1, Org1) and one configured Org1 endorser, the operator learns only that [1] set failed — not which MSP was short, nor by how much. This PR’s docs make "fewer distinct endorsers than required signers" its own error class, so the error should be able to name it.

selectDistinctForMSPSet returns only bool. Returning the failing mspID plus required-vs-available would give: "MSP [Org1MSP] requires 2 distinct signers but only 1 configured endorser belongs to it".

Comment on lines +71 to +74
// A greedy per-slot pick is complete here: every slot requiring a given MSP ID draws from
// the same pool, so the only way this fails is that some MSP ID appears in requiredMSPIDs
// more times than that MSP has distinct configured endorsers - genuinely unsatisfiable
// whatever the order of the picks. No backtracking is needed.

@AkramBitar AkramBitar Aug 5, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirming this argument rather than leaving the next reviewer to re-derive it: byMSP partitions the identities, so slots for MSP m draw only from pool[m] and the pools are disjoint. Feasibility is therefore just count(m) <= |pool[m]| per MSP, which greedy always attains — no ordering does better, so skipping backtracking is correct, not merely convenient.

Same for the test: it asserts both endorsers reach both slots, not just distinctness. The obvious "take the first unused" fix would pass the latter and fail the former.

@HayimShaul
HayimShaul force-pushed the 2048_select_distinct_endorsers_per_required_slot branch from 47812f0 to 42c2192 Compare August 6, 2026 06:19
SelectEndorsersForMSPSets sampled one configured endorser per required MSP ID
of a candidate set without excluding identities already picked for an earlier
slot of that same set. A namespace endorsement policy requiring two signers
from one MSP - e.g. AND(Org1MSP.member, Org1MSP.member), whose principal set
inquire.SatisfiedBy() reports as ["Org1MSP", "Org1MSP"] - could therefore be
satisfied by asking a single endorser to sign twice, collapsing the intended
2-of-N-within-org guarantee onto a single point of trust.

Per-slot sampling now excludes identities already selected for the same
candidate set, so every returned identity is distinct and the result length
always matches the chosen candidate set. A set requiring more distinct signers
from an MSP than that MSP has configured endorsers is no longer satisfiable and
is skipped, leaving the caller to try the remaining candidate sets before
failing - correctness over availability, as elsewhere in this selector.

Duplicate entries in the configured endorser list now collapse to a single
candidate: such a duplicate denotes one endorser providing one endorsement, so
counting it twice both skewed the uniform random pick and overstated how many
distinct endorsers an MSP actually offers.

Signed-off-by: Hayim.Shaul@ibm.com <hayimsha@fhe03.vpc.cloud9.ibm.com>
@HayimShaul
HayimShaul force-pushed the 2048_select_distinct_endorsers_per_required_slot branch from 42c2192 to f1007d6 Compare August 6, 2026 11:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

network-driver: duplicate identity can fill two distinct required MSP slots in SelectEndorsersForMSPSets

2 participants