Skip to content

fix: bind STATE sync sender_id to responder's canonical node_id (Phase 2 regression)#2259

Merged
Scottcjn merged 1 commit intomainfrom
security/phase2-statesync-hotfix
Apr 14, 2026
Merged

fix: bind STATE sync sender_id to responder's canonical node_id (Phase 2 regression)#2259
Scottcjn merged 1 commit intomainfrom
security/phase2-statesync-hotfix

Conversation

@Scottcjn
Copy link
Copy Markdown
Owner

Hotfix for regression observed on 4-node flag-day deploy at 2026-04-14 21:03 UTC.

Symptom

All 4 upgraded P2P nodes logged:

WARNING:rustchain_p2p_gossip:Rejected state merge from http://50.28.86.153:8099: invalid signature

Root cause

#2258 Phase A bound sender_id into HMAC signed content. request_full_sync was still wrapping STATE responses with sender_id=peer_url (e.g. http://50.28.86.153:8099), but the responder's HMAC was computed over its canonical node_id (e.g. node2). Mismatch → verify_message fails.

Fix

_handle_get_state already returns responder's sender_id in its response. This PR makes request_full_sync use that field when wrapping the STATE message, falling back to peer_url only if missing.

Scope

  • 9 added / 2 deleted lines in node/rustchain_p2p_gossip.py (request_full_sync only)
  • Does NOT affect epoch vote / propose / attestation paths — those already use msg.sender_id = self.node_id via create_message()
  • Wire-compatible: the fallback preserves old behavior if a responder doesn't return sender_id (none exist after security: P2P hardening Phase 2 — supersedes #2257 (#2256 A+B+C+D+E) #2258)

Test plan

  • Existing 6 regression tests (test_p2p_hardening_phase2.py) still pass
  • Post-deploy: verify 'Rejected state merge' warnings stop appearing in production logs

Closes the Phase 2 regression introduced by #2258.

Phase A signing includes sender_id in the signed content. request_full_sync
was wrapping STATE responses with sender_id=peer_url, causing signature
mismatch on verify: receiver rebuilt content with peer_url while responder
had signed with its real node_id.

_handle_get_state (updated in #2258) returns the responder's sender_id in
its response dict; request_full_sync now uses that when wrapping the
STATE message for local merge.

Observed on 4-node flag-day deploy (2026-04-14 ~21:03 UTC): all 4 upgraded
nodes logged "Rejected state merge from http://50.28.86.153:8099: invalid
signature" before this fix. Does not affect epoch vote / propose paths —
those already use msg.sender_id = self.node_id via create_message().
@github-actions github-actions Bot added BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) node Node server related labels Apr 14, 2026
@Scottcjn Scottcjn merged commit 0b2a091 into main Apr 14, 2026
6 of 7 checks passed
@Scottcjn Scottcjn deleted the security/phase2-statesync-hotfix branch April 14, 2026 21:07
@github-actions github-actions Bot added the size/S PR: 11-50 lines label Apr 14, 2026
@github-actions
Copy link
Copy Markdown
Contributor

✅ BCOS v2 Scan Results

Metric Value
Trust Score 60/100
Certificate ID BCOS-644b32da
Tier L1 (met)

BCOS Badge

What does this mean?

The BCOS (Beacon Certified Open Source) engine scans for:

  • SPDX license header compliance
  • Known CVE vulnerabilities (OSV database)
  • Static analysis findings (Semgrep)
  • SBOM completeness
  • Dependency freshness
  • Test infrastructure evidence
  • Review attestation tier

Full report | What is BCOS?


BCOS v2 Engine - Free & Open Source (MIT) - Elyan Labs

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

Labels

BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) node Node server related size/S PR: 11-50 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant