Skip to content

fix(core): verify gossip source matches claimed peer_id in auction bids/receipts - #10

Merged
ralyodio merged 1 commit into
profullstack:masterfrom
eltociear:fix/auction-verify-gossip-source
Jul 12, 2026
Merged

fix(core): verify gossip source matches claimed peer_id in auction bids/receipts#10
ralyodio merged 1 commit into
profullstack:masterfrom
eltociear:fix/auction-verify-gossip-source

Conversation

@eltociear

Copy link
Copy Markdown
Contributor

Fixes #9.

run_auction() matched incoming JobBid/JobReceipt gossip messages only on the self-reported bidder_peer_id/worker_peer_id field inside the JSON payload, never against GossipMessage.source — the libp2p-authenticated sender identity that's already attached to every message and can't be forged. capabilities.rs's capability-ad registry already keys off source for exactly this reason; the auction path didn't follow the same pattern.

Since JobAccept (naming the winning bidder) is broadcast publicly, any peer can see who won a job and race to publish a bid or completion receipt claiming to be that peer — run_auction() returns on the first message matching job_id + the claimed string, forged or not.

Change

Added source_matches_claim() and call it before accepting a bid (bid-collection loop) or a receipt (receipt-wait loop). Mismatches are discarded with a warn! log, same style as the existing discard-and-log pattern already used elsewhere in this file and in capabilities.rs.

This is independent of and doesn't replace the DID-signature scheme mentioned in JobReceipt.signature's doc comment (that's presumably still coming from CoinPay) — it's a free, already-available authentication check that should hold regardless.

Testing

I don't have a Rust toolchain in my environment to run cargo test -p c0mpute-core or cargo check, so I traced the types by hand instead of compiling:

  • GossipMessage.source: Option<PeerId> (c0mpute-net/src/swarm.rs) — already imported via the existing use c0mpute_net::{GossipMessage, Libp2pNetwork};.
  • PeerId::to_base58() — already used earlier in the same file (net.peer_id().to_base58()), so it's the right method.
  • No new dependencies, no Cargo.toml changes.

Please run the test suite before merging since I couldn't verify it compiles locally — happy to fix anything that doesn't build.

JobBid.bidder_peer_id and JobReceipt.worker_peer_id are self-reported
strings inside the gossip payload. run_auction() only checked these
strings against job_id / the chosen winner — it never checked them
against GossipMessage.source, the libp2p-authenticated sender identity
that's already available on every message and can't be forged.

Since JobAccept (naming the winning bidder) is broadcast publicly on
the job topic, any peer can see who won and race to publish a bid or
a completion receipt claiming to be that peer_id. A forged receipt
that arrives before the real winner's would be accepted as-is (wrong
worker credited, or the real worker's later genuine receipt ignored
since run_auction() already returned).

capabilities.rs's ad registry already keys its ad table by
msg.source for exactly this reason — this brings the auction path in
line with that same discipline as a stopgap ahead of the DID-signature
scheme mentioned in JobReceipt's doc comment.

Couldn't run `cargo test -p c0mpute-core` in my environment (no Rust
toolchain available) — traced types by hand (GossipMessage.source is
already Option<PeerId>, PeerId::to_base58() is already used elsewhere
in this same file) but please double check on a real build.
@ralyodio
ralyodio merged commit ddc45e7 into profullstack:master Jul 12, 2026
8 checks passed
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.

Auction accepts spoofed bids/receipts — bidder_peer_id/worker_peer_id never checked against gossip source

2 participants