Skip to content

Fix Signify multisig exchange leader election - #455

Open
kentbull wants to merge 1 commit into
WebOfTrust:mainfrom
kentbull:fix/signify-multisig-exchange-election
Open

Fix Signify multisig exchange leader election#455
kentbull wants to merge 1 commit into
WebOfTrust:mainfrom
kentbull:fix/signify-multisig-exchange-election

Conversation

@kentbull

@kentbull kentbull commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • elect one KERIA Signify multisig member to send a completed exchange message
  • reuse KERIpy's lowest-present-signature-index election rule for SignifyGroupHab
  • preserve the base Exchanger behavior for single-signature and KERIpy GroupHab habitats
  • cover elected, non-elected, missing-signature, fallback, and Agent wiring behavior

Why this is necessary

KERIA represents edge-controlled multisig groups with SignifyGroupHab. KERIpy's Exchanger.lead() only recognizes GroupHab, and SignifyGroupHab is a sibling type rather than a subclass. The default path therefore treats every Signify group member like an ordinary habitat and allows every member to send.

For a three-member multisig operation, each KERIA Agent independently completes the same EXN after collecting threshold signatures. Without election, all three Agents forward that identical EXN. IPEX grants amplify the cost because each sender also forwards the supporting KEL, TEL, and ACDC artifact stream. Recipient mailboxes correctly retain those deliveries, so downstream parsing and callback handling repeat the same work.

This was discovered while profiling a local QVI workflow: one grant produced 121 recipient mailbox entries, consisting of three identical 41-entry streams. The same grant EXN appeared at offsets 0, 41, and 82, and Sally (vLEI Reporting API - not a part of KERIA) produced three callbacks for a single grant where there should have just been one. Applying leader election reduced that delivery to one 41-entry stream and one callback when combined with the separate artifact-completeness work (from another branch coming in a follow on PR).

Election behavior

After the completed EXN signatures have been stored, the exchanger:

  1. reads the indexed signature group for the EXN;
  2. selects the lowest present signing index;
  3. maps that index to the ordered group signing keys; and
  4. sends only when the local member's signing key matches the elected key.

This changes transport responsibility only. It does not change signing, thresholds, exchange completion, or recipient mailbox semantics.

Scope

This PR intentionally does not change:

  • within-stream artifact deduplication;
  • mailbox storage or processing;
  • multisig thresholds or signature collection;
  • KERIpy's habitat class hierarchy;
  • counselor, delegation, or scheduler behavior;

KERIA represents edge-controlled multisig groups with SignifyGroupHab, while KERIpy's Exchanger applies sender election only to GroupHab. This caused every KERIA member to forward the same completed EXN and, for IPEX grants, the same supporting artifact stream.

Add a KERIA-specific exchanger that applies the existing lowest-signature-index election to SignifyGroupHab and delegates every other habitat type to the base implementation. Wire Agent to use it and cover elected, non-elected, missing-signature, and fallback behavior.
@codecov

codecov Bot commented Jul 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.47%. Comparing base (b5d8e64) to head (edb62f8).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #455      +/-   ##
==========================================
+ Coverage   88.44%   88.47%   +0.02%     
==========================================
  Files          26       26              
  Lines        5938     5950      +12     
==========================================
+ Hits         5252     5264      +12     
  Misses        686      686              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@kentbull
kentbull requested a review from iFergal July 25, 2026 23:49
Comment thread src/keria/app/agenting.py
raise KeyboardInterrupt("Agency shutdown complete. Exiting Agency.")


class SignifyExchanger(exchanging.Exchanger):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This works but why not update keripy to if not isinstance(hab, habbing.GroupHab) && isinstance(hab, habbing.SignifyGroupHab): to reduce the duplication in case they diverge?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good point, I'll do that.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Actually, I'm not quite sure what you're referring to. Are you saying we should change KERIpy instead of KERIA?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yes, considering SignifyGroupHab still lives in keripy, I think the cleanest solution would be to update keripy rather than (almost) duplicate it here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants